anilist/mal search

This commit is contained in:
LagradOst 2021-11-20 01:41:37 +01:00
parent a8b6fc4e70
commit 28fcf68b14
10 changed files with 396 additions and 45 deletions

View file

@ -3,12 +3,12 @@ package com.lagradost.cloudstream3.syncproviders
import android.content.Context import android.content.Context
import com.lagradost.cloudstream3.ShowStatus import com.lagradost.cloudstream3.ShowStatus
interface SyncAPI { interface SyncAPI : OAuth2API {
data class SyncSearchResult( data class SyncSearchResult(
val name: String, val name: String,
val syncApiName: String, val syncApiName: String,
val id: String, val id: String,
val url: String?, val url: String,
val posterUrl: String?, val posterUrl: String?,
) )
@ -64,7 +64,7 @@ interface SyncAPI {
var characters: List<SyncCharacter>? = null, var characters: List<SyncCharacter>? = null,
) )
val icon : Int val icon: Int
val mainUrl: String val mainUrl: String
fun search(context: Context, name: String): List<SyncSearchResult>? fun search(context: Context, name: String): List<SyncSearchResult>?
@ -78,9 +78,9 @@ interface SyncAPI {
4 -> PlanToWatch 4 -> PlanToWatch
5 -> ReWatching 5 -> ReWatching
*/ */
fun score(context: Context, id: String, status : SyncStatus): Boolean fun score(context: Context, id: String, status: SyncStatus): Boolean
fun getStatus(context: Context, id : String) : SyncStatus? fun getStatus(context: Context, id: String): SyncStatus?
fun getResult(context: Context, id: String): SyncResult? fun getResult(context: Context, id: String): SyncResult?
} }

View file

@ -62,7 +62,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
override fun search(context: Context, name: String): List<SyncAPI.SyncSearchResult> { override fun search(context: Context, name: String): List<SyncAPI.SyncSearchResult> {
val url = "https://api.myanimelist.net/v2/anime?q=$name&limit=$MAL_MAX_SEARCH_LIMIT" val url = "https://api.myanimelist.net/v2/anime?q=$name&limit=$MAL_MAX_SEARCH_LIMIT"
val res = get( var res = get(
url, headers = mapOf( url, headers = mapOf(
"Authorization" to "Bearer " + context.getKey<String>( "Authorization" to "Bearer " + context.getKey<String>(
accountId, accountId,
@ -71,12 +71,13 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
), cacheTime = 0 ), cacheTime = 0
).text ).text
return mapper.readValue<MalSearch>(res).data.map { return mapper.readValue<MalSearch>(res).data.map {
val node = it.node
SyncAPI.SyncSearchResult( SyncAPI.SyncSearchResult(
it.title, node.title,
this.name, this.name,
it.id.toString(), node.id.toString(),
"$mainUrl/anime/${it.id}/", "$mainUrl/anime/${node.id}/",
it.main_picture?.large ?: it.main_picture?.medium node.main_picture?.large ?: node.main_picture?.medium
) )
} }
} }
@ -225,26 +226,26 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
@JsonProperty("id") val id: Int, @JsonProperty("id") val id: Int,
@JsonProperty("title") val title: String, @JsonProperty("title") val title: String,
@JsonProperty("main_picture") val main_picture: MainPicture?, @JsonProperty("main_picture") val main_picture: MainPicture?,
@JsonProperty("alternative_titles") val alternative_titles: AlternativeTitles, @JsonProperty("alternative_titles") val alternative_titles: AlternativeTitles?,
@JsonProperty("media_type") val media_type: String, @JsonProperty("media_type") val media_type: String?,
@JsonProperty("num_episodes") val num_episodes: Int, @JsonProperty("num_episodes") val num_episodes: Int?,
@JsonProperty("status") val status: String, @JsonProperty("status") val status: String?,
@JsonProperty("start_date") val start_date: String?, @JsonProperty("start_date") val start_date: String?,
@JsonProperty("end_date") val end_date: String?, @JsonProperty("end_date") val end_date: String?,
@JsonProperty("average_episode_duration") val average_episode_duration: Int, @JsonProperty("average_episode_duration") val average_episode_duration: Int?,
@JsonProperty("synopsis") val synopsis: String, @JsonProperty("synopsis") val synopsis: String?,
@JsonProperty("mean") val mean: Double, @JsonProperty("mean") val mean: Double?,
@JsonProperty("genres") val genres: List<Genres>?, @JsonProperty("genres") val genres: List<Genres>?,
@JsonProperty("rank") val rank: Int, @JsonProperty("rank") val rank: Int?,
@JsonProperty("popularity") val popularity: Int, @JsonProperty("popularity") val popularity: Int?,
@JsonProperty("num_list_users") val num_list_users: Int, @JsonProperty("num_list_users") val num_list_users: Int?,
@JsonProperty("num_favorites") val num_favorites: Int, @JsonProperty("num_favorites") val num_favorites: Int?,
@JsonProperty("num_scoring_users") val num_scoring_users: Int, @JsonProperty("num_scoring_users") val num_scoring_users: Int?,
@JsonProperty("start_season") val start_season: StartSeason?, @JsonProperty("start_season") val start_season: StartSeason?,
@JsonProperty("broadcast") val broadcast: Broadcast?, @JsonProperty("broadcast") val broadcast: Broadcast?,
@JsonProperty("nsfw") val nsfw: String, @JsonProperty("nsfw") val nsfw: String?,
@JsonProperty("created_at") val created_at: String, @JsonProperty("created_at") val created_at: String?,
@JsonProperty("updated_at") val updated_at: String @JsonProperty("updated_at") val updated_at: String?
) )
data class ListStatus( data class ListStatus(
@ -600,14 +601,18 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
// Used for getDataAboutId() // Used for getDataAboutId()
data class MalAnime( data class MalAnime(
@JsonProperty("id") val id: Int, @JsonProperty("id") val id: Int,
@JsonProperty("title") val title: String, @JsonProperty("title") val title: String?,
@JsonProperty("num_episodes") val num_episodes: Int, @JsonProperty("num_episodes") val num_episodes: Int,
@JsonProperty("my_list_status") val my_list_status: MalStatus?, @JsonProperty("my_list_status") val my_list_status: MalStatus?,
@JsonProperty("main_picture") val main_picture: MalMainPicture?, @JsonProperty("main_picture") val main_picture: MalMainPicture?,
) )
data class MalSearchNode(
@JsonProperty("node") val node: Node,
)
data class MalSearch( data class MalSearch(
@JsonProperty("data") val data: List<MalAnime>, @JsonProperty("data") val data: List<MalSearchNode>,
//paging //paging
) )

View file

@ -0,0 +1,178 @@
package com.lagradost.cloudstream3.ui.quicksearch
import android.app.Activity
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.ImageView
import androidx.appcompat.widget.SearchView
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.HomePageList
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.loadHomepageList
import com.lagradost.cloudstream3.ui.home.ParentItemAdapter
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD
import com.lagradost.cloudstream3.ui.search.SearchAdapter
import com.lagradost.cloudstream3.ui.search.SearchFragment.Companion.filterSearchResponse
import com.lagradost.cloudstream3.ui.search.SearchHelper
import com.lagradost.cloudstream3.ui.search.SearchViewModel
import com.lagradost.cloudstream3.utils.UIHelper
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.utils.UIHelper.navigate
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
import kotlinx.android.synthetic.main.fragment_search.*
import kotlinx.android.synthetic.main.quick_search.*
import java.util.concurrent.locks.ReentrantLock
class QuickSearchFragment(var isMainApis: Boolean = false) : Fragment() {
companion object {
fun push(activity: Activity?, mainApi: Boolean = true, autoSearch: String? = null) {
activity.navigate(R.id.global_to_navigation_quick_search, Bundle().apply {
putBoolean("mainapi", mainApi)
putString("autosearch", autoSearch)
})
}
}
private val searchViewModel: SearchViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
activity?.window?.setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
)
return inflater.inflate(R.layout.quick_search, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
context?.fixPaddingStatusbar(quick_search_root)
arguments?.getBoolean("mainapi", true)?.let {
isMainApis = it
}
val listLock = ReentrantLock()
observe(searchViewModel.currentSearch) { list ->
try {
// https://stackoverflow.com/questions/6866238/concurrent-modification-exception-adding-to-an-arraylist
listLock.lock()
(quick_search_master_recycler?.adapter as ParentItemAdapter?)?.apply {
items = list.map { ongoing ->
val ongoingList = HomePageList(
ongoing.apiName,
if (ongoing.data is Resource.Success) ongoing.data.value.filterSearchResponse() else ArrayList()
)
ongoingList
}
notifyDataSetChanged()
}
} catch (e: Exception) {
logError(e)
} finally {
listLock.unlock()
}
}
val masterAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder> = ParentItemAdapter(listOf(), { callback ->
when (callback.action) {
SEARCH_ACTION_LOAD -> {
if (isMainApis) {
// this is due to result page only holding 1 thing
activity?.popCurrentPage()
activity?.popCurrentPage()
SearchHelper.handleSearchClickCallback(activity, callback)
} else {
//TODO MAL RESPONSE
}
}
else -> SearchHelper.handleSearchClickCallback(activity, callback)
}
}, { item ->
activity?.loadHomepageList(item)
})
val searchExitIcon = quick_search.findViewById<ImageView>(androidx.appcompat.R.id.search_close_btn)
val searchMagIcon = quick_search.findViewById<ImageView>(androidx.appcompat.R.id.search_mag_icon)
searchMagIcon.scaleX = 0.65f
searchMagIcon.scaleY = 0.65f
quick_search.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
context?.let { ctx ->
searchViewModel.searchAndCancel(query = query, context = ctx, isMainApis = isMainApis, ignoreSettings = true)
}
quick_search?.let {
UIHelper.hideKeyboard(it)
}
return true
}
override fun onQueryTextChange(newText: String): Boolean {
//searchViewModel.quickSearch(newText)
return true
}
})
quick_search_loading_bar.alpha = 0f
observe(searchViewModel.searchResponse) {
when (it) {
is Resource.Success -> {
it.value.let { data ->
if (data.isNotEmpty()) {
(cardSpace?.adapter as SearchAdapter?)?.apply {
cardList = data.toList()
notifyDataSetChanged()
}
}
}
searchExitIcon.alpha = 1f
quick_search_loading_bar.alpha = 0f
}
is Resource.Failure -> {
// Toast.makeText(activity, "Server error", Toast.LENGTH_LONG).show()
searchExitIcon.alpha = 1f
quick_search_loading_bar.alpha = 0f
}
is Resource.Loading -> {
searchExitIcon.alpha = 0f
quick_search_loading_bar.alpha = 1f
}
}
}
quick_search_master_recycler.adapter = masterAdapter
quick_search_master_recycler.layoutManager = GridLayoutManager(context, 1)
quick_search.setOnQueryTextFocusChangeListener { _, b ->
if (b) {
// https://stackoverflow.com/questions/12022715/unable-to-show-keyboard-automatically-in-the-searchview
UIHelper.showInputMethod(view.findFocus())
}
}
quick_search_back.setOnClickListener {
activity?.popCurrentPage()
}
arguments?.getString("autosearch")?.let {
quick_search.setQuery(it, true)
arguments?.remove("autosearch")
}
}
}

View file

@ -47,6 +47,7 @@ import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownload
import com.lagradost.cloudstream3.ui.download.EasyDownloadButton import com.lagradost.cloudstream3.ui.download.EasyDownloadButton
import com.lagradost.cloudstream3.ui.player.PlayerData import com.lagradost.cloudstream3.ui.player.PlayerData
import com.lagradost.cloudstream3.ui.player.PlayerFragment import com.lagradost.cloudstream3.ui.player.PlayerFragment
import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment.Companion.getDownloadSubsLanguageISO639_1 import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment.Companion.getDownloadSubsLanguageISO639_1
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.isAppInstalled import com.lagradost.cloudstream3.utils.AppUtils.isAppInstalled
@ -944,6 +945,10 @@ class ResultFragment : Fragment() {
} }
} }
result_search?.setOnClickListener {
QuickSearchFragment.push(activity,true, d.name)
}
result_share?.setOnClickListener { result_share?.setOnClickListener {
val i = Intent(ACTION_SEND) val i = Intent(ACTION_SEND)
i.type = "text/plain" i.type = "text/plain"

View file

@ -10,7 +10,7 @@ import android.widget.*
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.fragment.app.activityViewModels
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -53,15 +53,13 @@ class SearchFragment : Fragment() {
} }
} }
private lateinit var searchViewModel: SearchViewModel private val searchViewModel: SearchViewModel by activityViewModels()
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle?, savedInstanceState: Bundle?,
): View? { ): View? {
searchViewModel =
ViewModelProvider(this).get(SearchViewModel::class.java)
activity?.window?.setSoftInputMode( activity?.window?.setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
) )
@ -299,7 +297,10 @@ class SearchFragment : Fragment() {
main_search.setOnQueryTextListener(object : SearchView.OnQueryTextListener { main_search.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean { override fun onQueryTextSubmit(query: String): Boolean {
searchViewModel.searchAndCancel(query) context?.let { ctx ->
searchViewModel.searchAndCancel(query = query, context = ctx)
}
main_search?.let { main_search?.let {
hideKeyboard(it) hideKeyboard(it)
} }
@ -366,7 +367,7 @@ class SearchFragment : Fragment() {
typesActive = it.getApiTypeSettings() typesActive = it.getApiTypeSettings()
} }
main_search.setOnQueryTextFocusChangeListener { searchView, b -> main_search.setOnQueryTextFocusChangeListener { _, b ->
if (b) { if (b) {
// https://stackoverflow.com/questions/12022715/unable-to-show-keyboard-automatically-in-the-searchview // https://stackoverflow.com/questions/12022715/unable-to-show-keyboard-automatically-in-the-searchview
showInputMethod(view.findFocus()) showInputMethod(view.findFocus())

View file

@ -1,13 +1,19 @@
package com.lagradost.cloudstream3.ui.search package com.lagradost.cloudstream3.ui.search
import android.content.Context
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.ErrorLoadingException
import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.apmap import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.SyncApis
import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.ui.APIRepository import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.APIRepository.Companion.providersActive import com.lagradost.cloudstream3.ui.APIRepository.Companion.providersActive
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -28,18 +34,39 @@ class SearchViewModel : ViewModel() {
val currentSearch: LiveData<ArrayList<OnGoingSearch>> get() = _currentSearch val currentSearch: LiveData<ArrayList<OnGoingSearch>> get() = _currentSearch
private val repos = apis.map { APIRepository(it) } private val repos = apis.map { APIRepository(it) }
private val syncApis = SyncApis
private fun clearSearch() { private fun clearSearch() {
_searchResponse.postValue(Resource.Success(ArrayList())) _searchResponse.postValue(Resource.Success(ArrayList()))
} }
var onGoingSearch: Job? = null var onGoingSearch: Job? = null
fun searchAndCancel(query: String) { fun searchAndCancel(query: String, isMainApis : Boolean = true, ignoreSettings : Boolean = false, context: Context) {
onGoingSearch?.cancel() onGoingSearch?.cancel()
onGoingSearch = search(query) onGoingSearch = search(query, isMainApis, ignoreSettings, context)
} }
private fun search(query: String) = viewModelScope.launch { data class SyncSearchResultSearchResponse(
override val name: String,
override val url: String,
override val apiName: String,
override val type: TvType?,
override val posterUrl: String?,
override val id: Int?,
) : SearchResponse
private fun SyncAPI.SyncSearchResult.toSearchResponse(): SyncSearchResultSearchResponse {
return SyncSearchResultSearchResponse(
this.name,
this.url,
this.syncApiName,
null,
this.posterUrl,
null, //this.id.hashCode()
)
}
private fun search(query: String, isMainApis : Boolean = true, ignoreSettings : Boolean = false, context: Context) = viewModelScope.launch {
if (query.length <= 1) { if (query.length <= 1) {
clearSearch() clearSearch()
return@launch return@launch
@ -52,17 +79,26 @@ class SearchViewModel : ViewModel() {
_currentSearch.postValue(ArrayList()) _currentSearch.postValue(ArrayList())
withContext(Dispatchers.IO) { // This interrupts UI otherwise withContext(Dispatchers.IO) { // This interrupts UI otherwise
repos.filter { a -> if (isMainApis) {
(providersActive.size == 0 || providersActive.contains(a.name)) repos.filter { a ->
}.apmap { a -> // Parallel ignoreSettings || (providersActive.size == 0 || providersActive.contains(a.name))
val search = a.search(query) }.apmap { a -> // Parallel
currentList.add(OnGoingSearch(a.name,search )) val search = a.search(query)
_currentSearch.postValue(currentList) currentList.add(OnGoingSearch(a.name, search))
_currentSearch.postValue(currentList)
}
} else {
syncApis.apmap { a ->
val search = safeApiCall {
a.search(context, query)?.map { it.toSearchResponse() } ?: throw ErrorLoadingException()
}
currentList.add(OnGoingSearch(a.name, search))
}
} }
} }
_currentSearch.postValue(currentList) _currentSearch.postValue(currentList)
val list = ArrayList<SearchResponse>() val list = ArrayList<SearchResponse>()
val nestedList = val nestedList =
currentList.map { it.data }.filterIsInstance<Resource.Success<List<SearchResponse>>>().map { it.value } currentList.map { it.data }.filterIsInstance<Resource.Success<List<SearchResponse>>>().map { it.value }

View file

@ -232,7 +232,7 @@
android:nextFocusUp="@id/result_back" android:nextFocusUp="@id/result_back"
android:nextFocusDown="@id/result_descript" android:nextFocusDown="@id/result_descript"
android:nextFocusLeft="@id/result_share" android:nextFocusLeft="@id/result_share"
android:nextFocusRight="@id/result_bookmark_button" android:nextFocusRight="@id/result_search"
android:id="@+id/result_openinbrower" android:id="@+id/result_openinbrower"
android:layout_width="25dp" android:layout_width="25dp"
@ -247,6 +247,26 @@
android:layout_gravity="center" android:layout_gravity="center"
android:contentDescription="@string/result_open_in_browser"> android:contentDescription="@string/result_open_in_browser">
</ImageView> </ImageView>
<ImageView
android:nextFocusUp="@id/result_back"
android:nextFocusDown="@id/result_descript"
android:nextFocusLeft="@id/result_openinbrower"
android:nextFocusRight="@id/result_bookmark_button"
android:id="@+id/result_search"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_margin="5dp"
android:elevation="10dp"
android:tint="?attr/textColor"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/search_icon"
android:layout_gravity="center"
android:contentDescription="@string/result_open_in_browser">
</ImageView>
</LinearLayout> </LinearLayout>
</GridLayout> </GridLayout>
<TextView <TextView

View file

@ -15,7 +15,7 @@
android:layout_margin="10dp" android:layout_margin="10dp"
android:background="@drawable/search_background" android:background="@drawable/search_background"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="45dp"
> >
<FrameLayout <FrameLayout
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"

View file

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/quick_search_root"
android:background="?attr/primaryGrayBackground"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical">
<LinearLayout
android:visibility="visible"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<ImageView
android:id="@+id/quick_search_back"
android:layout_gravity="center"
android:foregroundGravity="center"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_baseline_arrow_back_24"
app:tint="@android:color/white"
android:layout_width="25dp"
android:layout_height="wrap_content">
<requestFocus/>
</ImageView>
<FrameLayout
android:layout_marginStart="10dp"
android:background="@drawable/search_background"
android:layout_gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="45dp">
<androidx.appcompat.widget.SearchView
android:nextFocusRight="@id/search_filter"
android:nextFocusLeft="@id/search_filter"
android:nextFocusDown="@id/cardSpace"
android:imeOptions="actionSearch"
android:inputType="text"
android:id="@+id/quick_search"
app:queryBackground="@color/transparent"
app:searchIcon="@drawable/search_icon"
android:paddingStart="-10dp"
android:iconifiedByDefault="false"
app:queryHint="@string/search_hint"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
app:iconifiedByDefault="false"
tools:ignore="RtlSymmetry">
<androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/quick_search_loading_bar"
android:layout_width="20dp" android:layout_height="20dp"
android:layout_marginStart="-35dp"
style="@style/Widget.AppCompat.ProgressBar"
android:foregroundTint="@color/white"
android:progressTint="@color/white"
android:layout_gravity="center">
</androidx.core.widget.ContentLoadingProgressBar>
<!--app:queryHint="@string/search_hint"
android:background="@color/grayBackground" @color/itemBackground
app:searchHintIcon="@drawable/search_white"
-->
</androidx.appcompat.widget.SearchView>
</FrameLayout>
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:descendantFocusability="afterDescendants"
android:background="?attr/primaryBlackBackground"
android:id="@+id/quick_search_master_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/homepage_parent"
/>
</LinearLayout>

View file

@ -81,6 +81,25 @@
/> />
</action> </action>
<action android:id="@+id/global_to_navigation_quick_search"
app:destination="@id/navigation_quick_search"
app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim">
<argument
android:name="mainapi"
app:argType="boolean"
android:defaultValue="true"
/>
<argument
android:name="autosearch"
app:argType="string"
android:defaultValue="@null"
/>
</action>
<action android:id="@+id/global_to_navigation_settings" <action android:id="@+id/global_to_navigation_settings"
app:destination="@id/navigation_settings" app:destination="@id/navigation_settings"
app:enterAnim="@anim/enter_anim" app:enterAnim="@anim/enter_anim"
@ -149,6 +168,12 @@
android:name="com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment" android:name="com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment"
android:label="@string/subtitles_settings"/> android:label="@string/subtitles_settings"/>
<fragment
android:id="@+id/navigation_quick_search"
android:layout_height="match_parent"
android:name="com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment"
android:label="@string/search"/>
<fragment <fragment
android:id="@+id/navigation_download_child" android:id="@+id/navigation_download_child"
android:layout_height="match_parent" android:layout_height="match_parent"