From 3290fceb4783a1d9e7b8466467e50505064e1b9b Mon Sep 17 00:00:00 2001 From: LagradOst Date: Sat, 14 Aug 2021 21:35:26 +0200 Subject: [PATCH] search providers settings --- .../com/lagradost/cloudstream3/MainAPI.kt | 20 +- .../animeproviders/DubbedAnimeProvider.kt | 8 +- .../animeproviders/TenshiProvider.kt | 4 +- .../animeproviders/WcoProvider.kt | 12 +- .../cloudstream3/ui/APIRepository.kt | 8 +- .../ui/home/HomeChildItemAdapter.kt | 1 + .../cloudstream3/ui/result/ResultFragment.kt | 2 +- .../cloudstream3/ui/search/SearchAdaptor.kt | 1 + .../cloudstream3/ui/search/SearchFragment.kt | 184 +++++++++++++++++- .../cloudstream3/ui/search/SearchViewModel.kt | 4 +- .../lagradost/cloudstream3/utils/DataStore.kt | 1 + app/src/main/res/layout/provider_list.xml | 65 +++++++ app/src/main/res/values/strings.xml | 4 +- 13 files changed, 289 insertions(+), 25 deletions(-) create mode 100644 app/src/main/res/layout/provider_list.xml diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 110c4b13..70a00ae0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -69,6 +69,23 @@ object APIHolder { setOf(apis[defProvider].name) )?.toHashSet() ?: hashSetOf(apis[defProvider].name) } + + fun Activity.getApiTypeSettings(): HashSet { + val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) + val list = settingsManager.getStringSet( + this.getString(R.string.search_types_list_key), + setOf(apis[defProvider].name) + ) + val hashSet = HashSet() + hashSet.addAll(TvType.values()) + if(list.isNullOrEmpty()) return hashSet + + val names = TvType.values().map { it.name }.toHashSet() + val realSet = list.filter { names.contains(it) }.map { TvType.valueOf(it) }.toHashSet() + if(realSet.isEmpty()) return hashSet + + return realSet + } } /**Every provider will **not** have try catch built in, so handle exceptions when calling these functions*/ @@ -186,6 +203,7 @@ enum class DubStatus { enum class TvType { Movie, + AnimeMovie, TvSeries, Cartoon, Anime, @@ -194,7 +212,7 @@ enum class TvType { // IN CASE OF FUTURE ANIME MOVIE OR SMTH fun TvType.isMovieType(): Boolean { - return this == TvType.Movie + return this == TvType.Movie || this == TvType.AnimeMovie } data class SubtitleFile(val lang: String, val url: String) diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/DubbedAnimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/DubbedAnimeProvider.kt index 2b14968b..21d1df12 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/DubbedAnimeProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/DubbedAnimeProvider.kt @@ -20,7 +20,7 @@ class DubbedAnimeProvider : MainAPI() { override val supportedTypes: Set get() = setOf( - TvType.Movie, + TvType.AnimeMovie, TvType.Anime, ) @@ -90,7 +90,7 @@ class DubbedAnimeProvider : MainAPI() { returnValue.add( if (getIsMovie(href)) { MovieSearchResponse( - title, href, this.name, TvType.Movie, img, null + title, href, this.name, TvType.AnimeMovie, img, null ) } else { AnimeSearchResponse( @@ -127,7 +127,7 @@ class DubbedAnimeProvider : MainAPI() { returnValue.add( if (getIsMovie(href)) { MovieSearchResponse( - title, href, this.name, TvType.Movie, img, null + title, href, this.name, TvType.AnimeMovie, img, null ) } else { AnimeSearchResponse( @@ -197,7 +197,7 @@ class DubbedAnimeProvider : MainAPI() { episode.title, realSlug, this.name, - TvType.Movie, + TvType.AnimeMovie, episode.serversHTML, if (poster == null) null else fixUrl(poster), episode.year?.toIntOrNull(), diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/TenshiProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/TenshiProvider.kt index 83ea1ae9..6fd5b431 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/TenshiProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/TenshiProvider.kt @@ -17,7 +17,7 @@ class TenshiProvider : MainAPI() { fun getType(t: String): TvType { return if (t.contains("OVA") || t.contains("Special")) TvType.ONA - else if (t.contains("Movie")) TvType.Movie + else if (t.contains("Movie")) TvType.AnimeMovie else TvType.Anime } } @@ -30,7 +30,7 @@ class TenshiProvider : MainAPI() { get() = false override val supportedTypes: Set - get() = setOf(TvType.Anime, TvType.Movie, TvType.ONA) + get() = setOf(TvType.Anime, TvType.AnimeMovie, TvType.ONA) private fun autoLoadToken(): Boolean { if (token != null) return true diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/WcoProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/WcoProvider.kt index a686eaec..4e5c086d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/WcoProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/WcoProvider.kt @@ -13,7 +13,7 @@ class WcoProvider : MainAPI() { companion object { fun getType(t: String): TvType { return if (t.contains("OVA") || t.contains("Special")) TvType.ONA - else if (t.contains("Movie")) TvType.Movie + else if (t.contains("Movie")) TvType.AnimeMovie else TvType.Anime } } @@ -29,7 +29,7 @@ class WcoProvider : MainAPI() { override val supportedTypes: Set get() = setOf( - TvType.Movie, + TvType.AnimeMovie, TvType.Anime, TvType.ONA ) @@ -91,9 +91,9 @@ class WcoProvider : MainAPI() { val type = i.selectFirst(".film-detail.film-detail-fix > div > span:nth-child(3)").text() returnValue.add( - if (getType(type) == TvType.Movie) { + if (getType(type) == TvType.AnimeMovie) { MovieSearchResponse( - title, href, this.name, TvType.Movie, img, year + title, href, this.name, TvType.AnimeMovie, img, year ) } else { AnimeSearchResponse( @@ -152,9 +152,9 @@ class WcoProvider : MainAPI() { val type = filmInfo?.select("span")?.get(1)?.text().toString() if (title != "null") { returnValue.add( - if (getType(type) == TvType.Movie) { + if (getType(type) == TvType.AnimeMovie) { MovieSearchResponse( - title, href, this.name, TvType.Movie, img, year + title, href, this.name, TvType.AnimeMovie, img, year ) } else { AnimeSearchResponse( diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt index 27edc0fc..7b9cba45 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt @@ -9,11 +9,12 @@ import com.lagradost.cloudstream3.utils.ExtractorLink class APIRepository(val api: MainAPI) { companion object { var providersActive = HashSet() + var typesActive = HashSet() } val name: String get() = api.name val mainUrl: String get() = api.mainUrl - val hasQuickSearch: Boolean get() = api.hasQuickSearch + val hasQuickSearch: Boolean get() = api.hasQuickSearch suspend fun load(url: String): Resource { return safeApiCall { @@ -22,9 +23,10 @@ class APIRepository(val api: MainAPI) { } } - suspend fun search(query: String): Resource> { + suspend fun search(query: String): Resource> { return safeApiCall { - api.search(query) ?: throw ErrorLoadingException() + return@safeApiCall (api.search(query) + ?: throw ErrorLoadingException()).filter { typesActive.contains(it.type) }.toList() } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeChildItemAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeChildItemAdapter.kt index 9ad3b467..fe7b16cf 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeChildItemAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeChildItemAdapter.kt @@ -59,6 +59,7 @@ class HomeChildItemAdapter( textType?.text = when (card.type) { TvType.Anime -> "Anime" TvType.Movie -> "Movie" + TvType.AnimeMovie -> "Movie" TvType.ONA -> "ONA" TvType.TvSeries -> "TV" TvType.Cartoon -> "Cartoon" diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index 6266e503..d4a45a0e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -899,7 +899,7 @@ class ResultFragment : Fragment() { } } - if (d.type == TvType.Movie) { + if (d.type.isMovieType()) { val hasDownloadSupport = api.hasDownloadSupport lateFixDownloadButton(true) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt index b02504ae..9f8ed893 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt @@ -91,6 +91,7 @@ class SearchAdapter( textType?.text = when (card.type) { TvType.Anime -> "Anime" TvType.Movie -> "Movie" + TvType.AnimeMovie -> "Movie" TvType.ONA -> "ONA" TvType.TvSeries -> "TV" TvType.Cartoon -> "Cartoon" 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 0ede9a1c..9611ac48 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 @@ -8,7 +8,7 @@ import android.view.View import android.view.ViewGroup import android.view.WindowManager import android.view.inputmethod.InputMethodManager -import android.widget.ImageView +import android.widget.* import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.SearchView import androidx.fragment.app.Fragment @@ -16,20 +16,28 @@ import androidx.lifecycle.ViewModelProvider import androidx.preference.PreferenceManager import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.switchmaterial.SwitchMaterial import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.getApiSettings +import com.lagradost.cloudstream3.APIHolder.getApiTypeSettings import com.lagradost.cloudstream3.HomePageList import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.ui.APIRepository.Companion.providersActive +import com.lagradost.cloudstream3.ui.APIRepository.Companion.typesActive import com.lagradost.cloudstream3.ui.home.HomeFragment import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.loadHomepageList import com.lagradost.cloudstream3.ui.home.ParentItemAdapter +import com.lagradost.cloudstream3.utils.DataStore.getKey +import com.lagradost.cloudstream3.utils.DataStore.setKey +import com.lagradost.cloudstream3.utils.SEARCH_PROVIDER_TOGGLE import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.getGridIsCompact import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard import kotlinx.android.synthetic.main.fragment_search.* +import java.util.HashSet class SearchFragment : Fragment() { private lateinit var searchViewModel: SearchViewModel @@ -95,10 +103,175 @@ class SearchFragment : Fragment() { val searchMagIcon = main_search.findViewById(androidx.appcompat.R.id.search_mag_icon) searchMagIcon.scaleX = 0.65f searchMagIcon.scaleY = 0.65f - search_filter.setOnClickListener { + search_filter.setOnClickListener { view -> val apiNamesSetting = activity?.getApiSettings() if (apiNamesSetting != null) { val apiNames = apis.map { it.name } + val builder = + AlertDialog.Builder(view.context, R.style.AlertDialogCustom).setView(R.layout.provider_list) + + val dialog = builder.create() + dialog.show() + + val listView = dialog.findViewById(R.id.listview1)!! + val listView2 = dialog.findViewById(R.id.listview2)!! + val toggle = dialog.findViewById(R.id.toggle1)!! + val applyButton = dialog.findViewById(R.id.apply_btt)!! + val cancelButton = dialog.findViewById(R.id.cancel_btt)!! + // val applyHolder = dialog.findViewById(R.id.apply_btt_holder)!! + + toggle.text = getString(R.string.search_provider_text) + + val arrayAdapter = ArrayAdapter(view.context, R.layout.sort_bottom_single_choice) + arrayAdapter.addAll(apiNames) + + listView.adapter = arrayAdapter + listView.choiceMode = AbsListView.CHOICE_MODE_MULTIPLE + + val typeChoices = listOf( + Pair("Movies", listOf(TvType.Movie)), + Pair("TvSeries", listOf(TvType.TvSeries)), + Pair("Cartoons", listOf(TvType.Cartoon)), + Pair("Anime", listOf(TvType.Anime, TvType.ONA, TvType.AnimeMovie)) + ) + + val arrayAdapter2 = ArrayAdapter(view.context, R.layout.sort_bottom_single_choice) + arrayAdapter2.addAll(typeChoices.map { it.first }) + + listView2.adapter = arrayAdapter2 + listView2.choiceMode = AbsListView.CHOICE_MODE_MULTIPLE + + + /*fun updateMulti() { + val set = HashSet() + + for ((index, api) in apis.withIndex()) { + if (listView?.checkedItemPositions[index]) { + set.addAll(api.supportedTypes) + } + } + + if (set.size == 0) { + set.addAll(TvType.values()) + } + + for ((index, choice) in typeChoices.withIndex()) { + listView2?.setItemChecked(index, choice.second.any { set.contains(it) }) + } + }*/ + + for ((index, item) in apiNames.withIndex()) { + listView.setItemChecked(index, apiNamesSetting.contains(item)) + } + + for ((index, item) in typeChoices.withIndex()) { + listView2.setItemChecked(index, item.second.any { typesActive.contains(it) }) + } + + fun toggleSearch(isOn: Boolean) { + if (isOn) { + listView2?.visibility = View.VISIBLE + listView?.visibility = View.GONE + } else { + listView?.visibility = View.VISIBLE + listView2?.visibility = View.GONE + } + } + + val defVal = context?.getKey(SEARCH_PROVIDER_TOGGLE, true) ?: true + toggleSearch(defVal) + + toggle.isChecked = defVal + toggle.setOnCheckedChangeListener { _, isOn -> + toggleSearch(isOn) + } + + listView.setOnItemClickListener { _, _, i, _ -> + val types = HashSet() + for ((index, api) in apis.withIndex()) { + if (listView?.checkedItemPositions[index]) { + types.addAll(api.supportedTypes) + } + } + for ((typeIndex, type) in typeChoices.withIndex()) { + listView2.setItemChecked(typeIndex, type.second.any { types.contains(it) }) + } + } + + listView2.setOnItemClickListener { _, _, i, _ -> + for ((index, api) in apis.withIndex()) { + var isSupported = false + + for ((typeIndex, type) in typeChoices.withIndex()) { + if (listView2?.checkedItemPositions[typeIndex]) { + if (api.supportedTypes.any { type.second.contains(it) }) { + isSupported = true + } + } + } + + listView?.setItemChecked( + index, + isSupported + ) + } + + //updateMulti() + } + + dialog.setOnDismissListener { + context?.setKey(SEARCH_PROVIDER_TOGGLE, toggle.isChecked ?: true) + } + + applyButton.setOnClickListener { + val settingsManagerLocal = PreferenceManager.getDefaultSharedPreferences(activity) + + val activeTypes = HashSet() + for ((index, name) in typeChoices.withIndex()) { + if (listView2?.checkedItemPositions[index]) { + activeTypes.addAll(typeChoices[index].second) + } + } + + if (activeTypes.size == 0) { + activeTypes.addAll(TvType.values()) + } + + + val activeApis = HashSet() + for ((index, name) in apiNames.withIndex()) { + if (listView?.checkedItemPositions[index]) { + activeApis.add(name) + } + } + + if (activeApis.size == 0) { + activeApis.addAll(apiNames) + } + + val edit = settingsManagerLocal.edit() + edit.putStringSet( + getString(R.string.search_providers_list_key), + activeApis + ) + edit.putStringSet( + getString(R.string.search_types_list_key), + activeTypes.map { it.name }.toSet() + ) + edit.apply() + providersActive = activeApis + typesActive = activeTypes + + dialog.dismiss() + } + + cancelButton.setOnClickListener { + dialog.dismiss() + } + + //listView.setSelection(selectedIndex) + // listView.setItemChecked(selectedIndex, true) + /* val builder: AlertDialog.Builder = AlertDialog.Builder(requireContext()) builder.setMultiChoiceItems( @@ -125,7 +298,7 @@ class SearchFragment : Fragment() { } builder.setTitle("Search Providers") builder.setNegativeButton("Ok") { _, _ -> } - builder.show() + builder.show()*/ } } @@ -178,6 +351,7 @@ class SearchFragment : Fragment() { activity?.let { providersActive = it.getApiSettings() + typesActive = it.getApiTypeSettings() } main_search.setOnQueryTextFocusChangeListener { searchView, b -> @@ -205,8 +379,8 @@ class SearchFragment : Fragment() { val settingsManager = PreferenceManager.getDefaultSharedPreferences(context) val isAdvancedSearch = settingsManager.getBoolean("advanced_search", true) - search_master_recycler.visibility = if(isAdvancedSearch) View.VISIBLE else View.GONE - cardSpace.visibility = if(!isAdvancedSearch) View.VISIBLE else View.GONE + search_master_recycler.visibility = if (isAdvancedSearch) View.VISIBLE else View.GONE + cardSpace.visibility = if (!isAdvancedSearch) View.VISIBLE else View.GONE // SubtitlesFragment.push(activity) //searchViewModel.search("iron man") 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 ecce9e3f..0d959a02 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 @@ -13,7 +13,7 @@ import kotlinx.coroutines.launch data class OnGoingSearch( val apiName: String, - val data: Resource> + val data: Resource> ) class SearchViewModel : ViewModel() { @@ -56,7 +56,7 @@ class SearchViewModel : ViewModel() { if (localSearchCounter != searchCounter) return@launch val list = ArrayList() - val nestedList = currentList.map { it.data }.filterIsInstance>>().map { it.value } + val nestedList = currentList.map { it.data }.filterIsInstance>>().map { it.value } // I do it this way to move the relevant search results to the top var index = 0 diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStore.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStore.kt index 7bcf64ef..15ff14b0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStore.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStore.kt @@ -10,6 +10,7 @@ const val DOWNLOAD_HEADER_CACHE = "download_header_cache" const val DOWNLOAD_EPISODE_CACHE = "download_episode_cache" const val VIDEO_PLAYER_BRIGHTNESS = "video_player_alpha" const val HOMEPAGE_API = "home_api_used" +const val SEARCH_PROVIDER_TOGGLE = "settings_providers_toggle" const val PREFERENCES_NAME: String = "rebuild_preference" diff --git a/app/src/main/res/layout/provider_list.xml b/app/src/main/res/layout/provider_list.xml new file mode 100644 index 00000000..ad8606bb --- /dev/null +++ b/app/src/main/res/layout/provider_list.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ff31429f..60e2a824 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4,9 +4,10 @@ Search Downloads Settings - Search... + Search… Change Providers search_providers_list + search_type_list grid_format Poster No Data @@ -80,4 +81,5 @@ Reset to default value Preview Background Font + Search Providers \ No newline at end of file