forked from recloudstream/cloudstream
anilist/mal search
This commit is contained in:
parent
a8b6fc4e70
commit
28fcf68b14
10 changed files with 396 additions and 45 deletions
|
@ -3,12 +3,12 @@ package com.lagradost.cloudstream3.syncproviders
|
|||
import android.content.Context
|
||||
import com.lagradost.cloudstream3.ShowStatus
|
||||
|
||||
interface SyncAPI {
|
||||
interface SyncAPI : OAuth2API {
|
||||
data class SyncSearchResult(
|
||||
val name: String,
|
||||
val syncApiName: String,
|
||||
val id: String,
|
||||
val url: String?,
|
||||
val url: String,
|
||||
val posterUrl: String?,
|
||||
)
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
|
|||
|
||||
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 res = get(
|
||||
var res = get(
|
||||
url, headers = mapOf(
|
||||
"Authorization" to "Bearer " + context.getKey<String>(
|
||||
accountId,
|
||||
|
@ -71,12 +71,13 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
|
|||
), cacheTime = 0
|
||||
).text
|
||||
return mapper.readValue<MalSearch>(res).data.map {
|
||||
val node = it.node
|
||||
SyncAPI.SyncSearchResult(
|
||||
it.title,
|
||||
node.title,
|
||||
this.name,
|
||||
it.id.toString(),
|
||||
"$mainUrl/anime/${it.id}/",
|
||||
it.main_picture?.large ?: it.main_picture?.medium
|
||||
node.id.toString(),
|
||||
"$mainUrl/anime/${node.id}/",
|
||||
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("title") val title: String,
|
||||
@JsonProperty("main_picture") val main_picture: MainPicture?,
|
||||
@JsonProperty("alternative_titles") val alternative_titles: AlternativeTitles,
|
||||
@JsonProperty("media_type") val media_type: String,
|
||||
@JsonProperty("num_episodes") val num_episodes: Int,
|
||||
@JsonProperty("status") val status: String,
|
||||
@JsonProperty("alternative_titles") val alternative_titles: AlternativeTitles?,
|
||||
@JsonProperty("media_type") val media_type: String?,
|
||||
@JsonProperty("num_episodes") val num_episodes: Int?,
|
||||
@JsonProperty("status") val status: String?,
|
||||
@JsonProperty("start_date") val start_date: String?,
|
||||
@JsonProperty("end_date") val end_date: String?,
|
||||
@JsonProperty("average_episode_duration") val average_episode_duration: Int,
|
||||
@JsonProperty("synopsis") val synopsis: String,
|
||||
@JsonProperty("mean") val mean: Double,
|
||||
@JsonProperty("average_episode_duration") val average_episode_duration: Int?,
|
||||
@JsonProperty("synopsis") val synopsis: String?,
|
||||
@JsonProperty("mean") val mean: Double?,
|
||||
@JsonProperty("genres") val genres: List<Genres>?,
|
||||
@JsonProperty("rank") val rank: Int,
|
||||
@JsonProperty("popularity") val popularity: Int,
|
||||
@JsonProperty("num_list_users") val num_list_users: Int,
|
||||
@JsonProperty("num_favorites") val num_favorites: Int,
|
||||
@JsonProperty("num_scoring_users") val num_scoring_users: Int,
|
||||
@JsonProperty("rank") val rank: Int?,
|
||||
@JsonProperty("popularity") val popularity: Int?,
|
||||
@JsonProperty("num_list_users") val num_list_users: Int?,
|
||||
@JsonProperty("num_favorites") val num_favorites: Int?,
|
||||
@JsonProperty("num_scoring_users") val num_scoring_users: Int?,
|
||||
@JsonProperty("start_season") val start_season: StartSeason?,
|
||||
@JsonProperty("broadcast") val broadcast: Broadcast?,
|
||||
@JsonProperty("nsfw") val nsfw: String,
|
||||
@JsonProperty("created_at") val created_at: String,
|
||||
@JsonProperty("updated_at") val updated_at: String
|
||||
@JsonProperty("nsfw") val nsfw: String?,
|
||||
@JsonProperty("created_at") val created_at: String?,
|
||||
@JsonProperty("updated_at") val updated_at: String?
|
||||
)
|
||||
|
||||
data class ListStatus(
|
||||
|
@ -600,14 +601,18 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
|
|||
// Used for getDataAboutId()
|
||||
data class MalAnime(
|
||||
@JsonProperty("id") val id: Int,
|
||||
@JsonProperty("title") val title: String,
|
||||
@JsonProperty("title") val title: String?,
|
||||
@JsonProperty("num_episodes") val num_episodes: Int,
|
||||
@JsonProperty("my_list_status") val my_list_status: MalStatus?,
|
||||
@JsonProperty("main_picture") val main_picture: MalMainPicture?,
|
||||
)
|
||||
|
||||
data class MalSearchNode(
|
||||
@JsonProperty("node") val node: Node,
|
||||
)
|
||||
|
||||
data class MalSearch(
|
||||
@JsonProperty("data") val data: List<MalAnime>,
|
||||
@JsonProperty("data") val data: List<MalSearchNode>,
|
||||
//paging
|
||||
)
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -47,6 +47,7 @@ import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownload
|
|||
import com.lagradost.cloudstream3.ui.download.EasyDownloadButton
|
||||
import com.lagradost.cloudstream3.ui.player.PlayerData
|
||||
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.utils.*
|
||||
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 {
|
||||
val i = Intent(ACTION_SEND)
|
||||
i.type = "text/plain"
|
||||
|
|
|
@ -10,7 +10,7 @@ import android.widget.*
|
|||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
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(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View? {
|
||||
searchViewModel =
|
||||
ViewModelProvider(this).get(SearchViewModel::class.java)
|
||||
activity?.window?.setSoftInputMode(
|
||||
WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
|
||||
)
|
||||
|
@ -299,7 +297,10 @@ class SearchFragment : Fragment() {
|
|||
|
||||
main_search.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
searchViewModel.searchAndCancel(query)
|
||||
context?.let { ctx ->
|
||||
searchViewModel.searchAndCancel(query = query, context = ctx)
|
||||
}
|
||||
|
||||
main_search?.let {
|
||||
hideKeyboard(it)
|
||||
}
|
||||
|
@ -366,7 +367,7 @@ class SearchFragment : Fragment() {
|
|||
typesActive = it.getApiTypeSettings()
|
||||
}
|
||||
|
||||
main_search.setOnQueryTextFocusChangeListener { searchView, b ->
|
||||
main_search.setOnQueryTextFocusChangeListener { _, b ->
|
||||
if (b) {
|
||||
// https://stackoverflow.com/questions/12022715/unable-to-show-keyboard-automatically-in-the-searchview
|
||||
showInputMethod(view.findFocus())
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
package com.lagradost.cloudstream3.ui.search
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.lagradost.cloudstream3.APIHolder.apis
|
||||
import com.lagradost.cloudstream3.ErrorLoadingException
|
||||
import com.lagradost.cloudstream3.SearchResponse
|
||||
import com.lagradost.cloudstream3.TvType
|
||||
import com.lagradost.cloudstream3.apmap
|
||||
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.Companion.providersActive
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -28,18 +34,39 @@ class SearchViewModel : ViewModel() {
|
|||
val currentSearch: LiveData<ArrayList<OnGoingSearch>> get() = _currentSearch
|
||||
|
||||
private val repos = apis.map { APIRepository(it) }
|
||||
private val syncApis = SyncApis
|
||||
|
||||
private fun clearSearch() {
|
||||
_searchResponse.postValue(Resource.Success(ArrayList()))
|
||||
}
|
||||
|
||||
var onGoingSearch: Job? = null
|
||||
fun searchAndCancel(query: String) {
|
||||
fun searchAndCancel(query: String, isMainApis : Boolean = true, ignoreSettings : Boolean = false, context: Context) {
|
||||
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) {
|
||||
clearSearch()
|
||||
return@launch
|
||||
|
@ -52,17 +79,26 @@ class SearchViewModel : ViewModel() {
|
|||
_currentSearch.postValue(ArrayList())
|
||||
|
||||
withContext(Dispatchers.IO) { // This interrupts UI otherwise
|
||||
if (isMainApis) {
|
||||
repos.filter { a ->
|
||||
(providersActive.size == 0 || providersActive.contains(a.name))
|
||||
ignoreSettings || (providersActive.size == 0 || providersActive.contains(a.name))
|
||||
}.apmap { a -> // Parallel
|
||||
val search = a.search(query)
|
||||
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)
|
||||
|
||||
|
||||
val list = ArrayList<SearchResponse>()
|
||||
val nestedList =
|
||||
currentList.map { it.data }.filterIsInstance<Resource.Success<List<SearchResponse>>>().map { it.value }
|
||||
|
|
|
@ -232,7 +232,7 @@
|
|||
android:nextFocusUp="@id/result_back"
|
||||
android:nextFocusDown="@id/result_descript"
|
||||
android:nextFocusLeft="@id/result_share"
|
||||
android:nextFocusRight="@id/result_bookmark_button"
|
||||
android:nextFocusRight="@id/result_search"
|
||||
|
||||
android:id="@+id/result_openinbrower"
|
||||
android:layout_width="25dp"
|
||||
|
@ -247,6 +247,26 @@
|
|||
android:layout_gravity="center"
|
||||
android:contentDescription="@string/result_open_in_browser">
|
||||
</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>
|
||||
</GridLayout>
|
||||
<TextView
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
android:layout_margin="10dp"
|
||||
android:background="@drawable/search_background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="45dp"
|
||||
>
|
||||
<FrameLayout
|
||||
android:layout_gravity="center_vertical"
|
||||
|
|
81
app/src/main/res/layout/quick_search.xml
Normal file
81
app/src/main/res/layout/quick_search.xml
Normal 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>
|
|
@ -81,6 +81,25 @@
|
|||
/>
|
||||
</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"
|
||||
app:destination="@id/navigation_settings"
|
||||
app:enterAnim="@anim/enter_anim"
|
||||
|
@ -149,6 +168,12 @@
|
|||
android:name="com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment"
|
||||
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
|
||||
android:id="@+id/navigation_download_child"
|
||||
android:layout_height="match_parent"
|
||||
|
|
Loading…
Reference in a new issue