From 279605e6ae98548aeba1fe004dc8ae02fc3e6098 Mon Sep 17 00:00:00 2001
From: LagradOst <11805592+LagradOst@users.noreply.github.com>
Date: Tue, 29 Mar 2022 20:34:00 +0200
Subject: [PATCH] added primitive search history & added appCategory = video
fixed #874 and #878
---
app/src/main/AndroidManifest.xml | 3 +-
.../cloudstream3/ui/search/SearchFragment.kt | 58 ++++++++--
.../ui/search/SearchHistoryAdaptor.kt | 103 ++++++++++++++++++
.../cloudstream3/ui/search/SearchViewModel.kt | 47 ++++++--
app/src/main/res/layout/fragment_search.xml | 14 +++
.../main/res/layout/search_history_item.xml | 37 +++++++
6 files changed, 246 insertions(+), 16 deletions(-)
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchHistoryAdaptor.kt
create mode 100644 app/src/main/res/layout/search_history_item.xml
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6396f687..07937af4 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -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">
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt
index 0b0eb782..5ba23e11 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt
@@ -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(R.id.cancel_btt)
val applyBtt = dialog.findViewById(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>(SEARCH_PREF_TAGS)
?.mapNotNull { listName -> TvType.values().firstOrNull { it.name == listName } }
?.toMutableList()
?: mutableListOf(TvType.Movie, TvType.TvSeries)
+
+ fun updateSelectedList(list: MutableList) {
+ 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")
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchHistoryAdaptor.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchHistoryAdaptor.kt
new file mode 100644
index 00000000..8132301b
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchHistoryAdaptor.kt
@@ -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,
+ @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,
+ private val clickCallback: (SearchHistoryCallback) -> Unit,
+) : RecyclerView.Adapter() {
+
+ 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) {
+ 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,
+ private val newList: List
+) :
+ 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]
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchViewModel.kt
index 81dab9f9..551f0919 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchViewModel.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchViewModel.kt
@@ -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>
)
-class SearchViewModel : ViewModel() {
- private val _searchResponse: MutableLiveData>> =
- MutableLiveData()
- val searchResponse: LiveData>> get() = _searchResponse
+const val SEARCH_HISTORY_KEY = "search_history"
- private val _currentSearch: MutableLiveData> = MutableLiveData()
- val currentSearch: LiveData> get() = _currentSearch
+class SearchViewModel : ViewModel() {
+ private val _searchResponse: MutableLiveData>> =
+ MutableLiveData()
+ val searchResponse: LiveData>> get() = _searchResponse
+
+ private val _currentSearch: MutableLiveData> = MutableLiveData()
+ val currentSearch: LiveData> get() = _currentSearch
+
+ private val _currentHistory: MutableLiveData> = MutableLiveData()
+ val currentHistory: LiveData> 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(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()
diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml
index 48b4e70a..c7d41b7f 100644
--- a/app/src/main/res/layout/fragment_search.xml
+++ b/app/src/main/res/layout/fragment_search.xml
@@ -81,6 +81,7 @@
app:tint="?attr/textColor"
android:contentDescription="@string/change_providers_img_des" />
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/search_history_item.xml b/app/src/main/res/layout/search_history_item.xml
new file mode 100644
index 00000000..4daf4c8c
--- /dev/null
+++ b/app/src/main/res/layout/search_history_item.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+