forked from recloudstream/cloudstream
added primitive search history & added appCategory = video
fixed #874 and #878
This commit is contained in:
parent
71da2233d9
commit
279605e6ae
6 changed files with 246 additions and 16 deletions
|
@ -27,7 +27,8 @@
|
|||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme"
|
||||
android:fullBackupContent="@xml/backup_descriptor"
|
||||
tools:targetApi="m">
|
||||
android:appCategory="video"
|
||||
tools:targetApi="o">
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
|
||||
android:value="com.lagradost.cloudstream3.utils.CastOptionsProvider"/>
|
||||
|
|
|
@ -23,6 +23,7 @@ import com.lagradost.cloudstream3.*
|
|||
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiFromName
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiSettings
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
|
||||
import com.lagradost.cloudstream3.mvvm.Resource
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import com.lagradost.cloudstream3.mvvm.observe
|
||||
|
@ -164,7 +165,8 @@ class SearchFragment : Fragment() {
|
|||
val cancelBtt = dialog.findViewById<MaterialButton>(R.id.cancel_btt)
|
||||
val applyBtt = dialog.findViewById<MaterialButton>(R.id.apply_btt)
|
||||
|
||||
val pairList = HomeFragment.getPairList(anime, cartoons, tvs, docs, movies,asian)
|
||||
val pairList =
|
||||
HomeFragment.getPairList(anime, cartoons, tvs, docs, movies, asian)
|
||||
|
||||
cancelBtt?.setOnClickListener {
|
||||
dialog.dismissSafe()
|
||||
|
@ -277,10 +279,21 @@ class SearchFragment : Fragment() {
|
|||
search_select_asian,
|
||||
)
|
||||
|
||||
val settingsManager = context?.let { PreferenceManager.getDefaultSharedPreferences(it) }
|
||||
val isAdvancedSearch = settingsManager?.getBoolean("advanced_search", true) ?: true
|
||||
|
||||
selectedSearchTypes = context?.getKey<List<String>>(SEARCH_PREF_TAGS)
|
||||
?.mapNotNull { listName -> TvType.values().firstOrNull { it.name == listName } }
|
||||
?.toMutableList()
|
||||
?: mutableListOf(TvType.Movie, TvType.TvSeries)
|
||||
|
||||
fun updateSelectedList(list: MutableList<TvType>) {
|
||||
selectedSearchTypes = list
|
||||
for ((button, validTypes) in pairList) {
|
||||
button?.isSelected = selectedSearchTypes.any { validTypes.contains(it) }
|
||||
}
|
||||
}
|
||||
|
||||
context?.filterProviderByPreferredMedia()?.let { validAPIs ->
|
||||
for ((button, validTypes) in pairList) {
|
||||
val isValid =
|
||||
|
@ -339,10 +352,25 @@ class SearchFragment : Fragment() {
|
|||
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
//searchViewModel.quickSearch(newText)
|
||||
val showHistory = newText.isBlank()
|
||||
searchViewModel.clearSearch()
|
||||
searchViewModel.updateHistory()
|
||||
|
||||
search_history_recycler?.isVisible = showHistory
|
||||
|
||||
search_master_recycler?.isVisible = !showHistory && isAdvancedSearch
|
||||
search_autofit_results?.isVisible = !showHistory && !isAdvancedSearch
|
||||
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
observe(searchViewModel.currentHistory) { list ->
|
||||
(search_history_recycler.adapter as? SearchHistoryAdaptor?)?.updateList(list)
|
||||
}
|
||||
|
||||
searchViewModel.updateHistory()
|
||||
|
||||
observe(searchViewModel.searchResponse) {
|
||||
when (it) {
|
||||
is Resource.Success -> {
|
||||
|
@ -406,15 +434,31 @@ class SearchFragment : Fragment() {
|
|||
activity?.loadHomepageList(item)
|
||||
})
|
||||
|
||||
val historyAdapter = SearchHistoryAdaptor(mutableListOf()) { click ->
|
||||
val searchItem = click.item
|
||||
when (click.clickAction) {
|
||||
SEARCH_HISTORY_OPEN -> {
|
||||
searchViewModel.clearSearch()
|
||||
if (searchItem.type.isNotEmpty())
|
||||
updateSelectedList(searchItem.type.toMutableList())
|
||||
main_search?.setQuery(searchItem.searchText, true)
|
||||
}
|
||||
SEARCH_HISTORY_REMOVE -> {
|
||||
removeKey(SEARCH_HISTORY_KEY, searchItem.key)
|
||||
searchViewModel.updateHistory()
|
||||
}
|
||||
else -> {
|
||||
// wth are you doing???
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
search_history_recycler?.adapter = historyAdapter
|
||||
search_history_recycler?.layoutManager = GridLayoutManager(context, 1)
|
||||
|
||||
search_master_recycler?.adapter = masterAdapter
|
||||
search_master_recycler?.layoutManager = GridLayoutManager(context, 1)
|
||||
|
||||
val settingsManager = context?.let { PreferenceManager.getDefaultSharedPreferences(it) }
|
||||
val isAdvancedSearch = settingsManager?.getBoolean("advanced_search", true) ?: true
|
||||
|
||||
search_master_recycler?.isVisible = isAdvancedSearch
|
||||
search_autofit_results?.isVisible = !isAdvancedSearch
|
||||
|
||||
// SubtitlesFragment.push(activity)
|
||||
//searchViewModel.search("iron man")
|
||||
//(activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro")
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
package com.lagradost.cloudstream3.ui.search
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.TvType
|
||||
import kotlinx.android.synthetic.main.search_history_item.view.*
|
||||
|
||||
data class SearchHistoryItem(
|
||||
@JsonProperty("searchedAt") val searchedAt: Long,
|
||||
@JsonProperty("searchText") val searchText: String,
|
||||
@JsonProperty("type") val type: List<TvType>,
|
||||
@JsonProperty("key") val key: String,
|
||||
)
|
||||
|
||||
data class SearchHistoryCallback(
|
||||
val item: SearchHistoryItem,
|
||||
val clickAction: Int,
|
||||
)
|
||||
|
||||
const val SEARCH_HISTORY_OPEN = 0
|
||||
const val SEARCH_HISTORY_REMOVE = 1
|
||||
|
||||
class SearchHistoryAdaptor(
|
||||
private val cardList: MutableList<SearchHistoryItem>,
|
||||
private val clickCallback: (SearchHistoryCallback) -> Unit,
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return CardViewHolder(
|
||||
LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.search_history_item, parent, false),
|
||||
clickCallback,
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is CardViewHolder -> {
|
||||
holder.bind(cardList[position])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return cardList.size
|
||||
}
|
||||
|
||||
fun updateList(newList: List<SearchHistoryItem>) {
|
||||
val diffResult = DiffUtil.calculateDiff(
|
||||
SearchHistoryDiffCallback(this.cardList, newList)
|
||||
)
|
||||
|
||||
cardList.clear()
|
||||
cardList.addAll(newList)
|
||||
|
||||
diffResult.dispatchUpdatesTo(this)
|
||||
}
|
||||
|
||||
class CardViewHolder
|
||||
constructor(
|
||||
itemView: View,
|
||||
private val clickCallback: (SearchHistoryCallback) -> Unit,
|
||||
) :
|
||||
RecyclerView.ViewHolder(itemView) {
|
||||
private val removeButton: ImageView = itemView.home_history_remove
|
||||
private val openButton: View = itemView.home_history_tab
|
||||
private val title: TextView = itemView.home_history_title
|
||||
|
||||
fun bind(card: SearchHistoryItem) {
|
||||
title.text = card.searchText
|
||||
|
||||
removeButton.setOnClickListener {
|
||||
clickCallback.invoke(SearchHistoryCallback(card, SEARCH_HISTORY_REMOVE))
|
||||
}
|
||||
openButton.setOnClickListener {
|
||||
clickCallback.invoke(SearchHistoryCallback(card, SEARCH_HISTORY_OPEN))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SearchHistoryDiffCallback(
|
||||
private val oldList: List<SearchHistoryItem>,
|
||||
private val newList: List<SearchHistoryItem>
|
||||
) :
|
||||
DiffUtil.Callback() {
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
||||
oldList[oldItemPosition].searchText == newList[newItemPosition].searchText
|
||||
|
||||
override fun getOldListSize() = oldList.size
|
||||
|
||||
override fun getNewListSize() = newList.size
|
||||
|
||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
||||
oldList[oldItemPosition] == newList[newItemPosition]
|
||||
}
|
|
@ -6,11 +6,15 @@ import androidx.lifecycle.ViewModel
|
|||
import androidx.lifecycle.viewModelScope
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.APIHolder.apis
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||
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.utils.Coroutines.ioSafe
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -21,22 +25,28 @@ data class OnGoingSearch(
|
|||
val data: Resource<List<SearchResponse>>
|
||||
)
|
||||
|
||||
class SearchViewModel : ViewModel() {
|
||||
private val _searchResponse: MutableLiveData<Resource<ArrayList<SearchResponse>>> =
|
||||
MutableLiveData()
|
||||
val searchResponse: LiveData<Resource<ArrayList<SearchResponse>>> get() = _searchResponse
|
||||
const val SEARCH_HISTORY_KEY = "search_history"
|
||||
|
||||
private val _currentSearch: MutableLiveData<ArrayList<OnGoingSearch>> = MutableLiveData()
|
||||
val currentSearch: LiveData<ArrayList<OnGoingSearch>> get() = _currentSearch
|
||||
class SearchViewModel : ViewModel() {
|
||||
private val _searchResponse: MutableLiveData<Resource<List<SearchResponse>>> =
|
||||
MutableLiveData()
|
||||
val searchResponse: LiveData<Resource<List<SearchResponse>>> get() = _searchResponse
|
||||
|
||||
private val _currentSearch: MutableLiveData<List<OnGoingSearch>> = MutableLiveData()
|
||||
val currentSearch: LiveData<List<OnGoingSearch>> get() = _currentSearch
|
||||
|
||||
private val _currentHistory: MutableLiveData<List<SearchHistoryItem>> = MutableLiveData()
|
||||
val currentHistory: LiveData<List<SearchHistoryItem>> get() = _currentHistory
|
||||
|
||||
private val repos = apis.map { APIRepository(it) }
|
||||
private val syncApis = SyncApis
|
||||
|
||||
private fun clearSearch() {
|
||||
fun clearSearch() {
|
||||
_searchResponse.postValue(Resource.Success(ArrayList()))
|
||||
_currentSearch.postValue(emptyList())
|
||||
}
|
||||
|
||||
var onGoingSearch: Job? = null
|
||||
private var onGoingSearch: Job? = null
|
||||
fun searchAndCancel(
|
||||
query: String,
|
||||
isMainApis: Boolean = true,
|
||||
|
@ -68,6 +78,15 @@ class SearchViewModel : ViewModel() {
|
|||
)
|
||||
}
|
||||
|
||||
fun updateHistory() = viewModelScope.launch {
|
||||
ioSafe {
|
||||
val items = getKeys(SEARCH_HISTORY_KEY)?.mapNotNull {
|
||||
getKey<SearchHistoryItem>(it)
|
||||
}?.sortedByDescending { it.searchedAt } ?: emptyList()
|
||||
_currentHistory.postValue(items)
|
||||
}
|
||||
}
|
||||
|
||||
private fun search(
|
||||
query: String,
|
||||
isMainApis: Boolean = true,
|
||||
|
@ -80,6 +99,18 @@ class SearchViewModel : ViewModel() {
|
|||
return@launch
|
||||
}
|
||||
|
||||
val key = query.hashCode().toString()
|
||||
setKey(
|
||||
SEARCH_HISTORY_KEY,
|
||||
key,
|
||||
SearchHistoryItem(
|
||||
searchedAt = System.currentTimeMillis(),
|
||||
searchText = query,
|
||||
type = emptyList(), // TODO implement tv type
|
||||
key = key,
|
||||
)
|
||||
)
|
||||
|
||||
_searchResponse.postValue(Resource.Loading())
|
||||
|
||||
val currentList = ArrayList<OnGoingSearch>()
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
app:tint="?attr/textColor"
|
||||
android:contentDescription="@string/change_providers_img_des" />
|
||||
</FrameLayout>
|
||||
|
||||
<HorizontalScrollView
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
|
@ -144,6 +145,7 @@
|
|||
|
||||
|
||||
<com.lagradost.cloudstream3.ui.AutofitRecyclerView
|
||||
android:visibility="gone"
|
||||
android:nextFocusLeft="@id/nav_rail_view"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
|
||||
|
@ -160,6 +162,7 @@
|
|||
android:orientation="vertical" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:visibility="gone"
|
||||
android:nextFocusLeft="@id/nav_rail_view"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
|
||||
|
@ -168,4 +171,15 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:listitem="@layout/homepage_parent" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:visibility="visible"
|
||||
android:nextFocusLeft="@id/nav_rail_view"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
|
||||
android:background="?attr/primaryBlackBackground"
|
||||
android:id="@+id/search_history_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:listitem="@layout/search_history_item" />
|
||||
</LinearLayout>
|
37
app/src/main/res/layout/search_history_item.xml
Normal file
37
app/src/main/res/layout/search_history_item.xml
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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="horizontal"
|
||||
android:id="@+id/home_history_tab"
|
||||
android:nextFocusRight="@id/home_history_remove">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/home_history_title"
|
||||
android:textColor="?attr/textColor"
|
||||
android:textSize="18sp"
|
||||
tools:text="Hello World"
|
||||
android:padding="10dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:padding="10dp"
|
||||
android:id="@+id/home_history_remove"
|
||||
android:nextFocusLeft="@id/home_history_tab"
|
||||
|
||||
android:src="@drawable/ic_baseline_close_24"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:layout_height="match_parent"
|
||||
app:tint="?attr/white"
|
||||
tools:ignore="ContentDescription"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in a new issue