This commit is contained in:
LagradOst 2021-08-12 02:04:58 +02:00
parent 5e619c18cc
commit 2839d59a28
11 changed files with 174 additions and 169 deletions

View file

@ -13,8 +13,8 @@ android {
applicationId "com.lagradost.cloudstream3" applicationId "com.lagradost.cloudstream3"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 30 targetSdkVersion 30
versionCode 12 versionCode 13
versionName "1.2.1" versionName "1.2.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View file

@ -1,87 +0,0 @@
package com.lagradost.cloudstream3
import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.mvvm.safeApiCall
class AllProvider : MainAPI() {
override val name: String
get() = "All Sources"
var providersActive = HashSet<String>()
override fun quickSearch(query: String): ArrayList<SearchResponse>? {
val list = apis.filter { a ->
a.name != this.name && (providersActive.size == 0 || providersActive.contains(a.name))
}.filter { a -> a.hasQuickSearch }.pmap { a ->
normalSafeApiCall {
a.quickSearch(query)
}
}
var maxCount = 0
var providerCount = 0
for (res in list) {
if (res != null) {
if (res.size > maxCount) {
maxCount = res.size
}
providerCount++
}
}
println("PROV: " + providerCount + "|" + maxCount)
if (providerCount == 0) return null
if (maxCount == 0) return ArrayList()
val result = ArrayList<SearchResponse>()
for (i in 0..maxCount) {
for (res in list) {
if (res != null) {
if (i < res.size) {
result.add(res[i])
}
}
}
}
return result
}
override fun search(query: String): ArrayList<SearchResponse>? {
val list = apis.filter { a ->
a.name != this.name && (providersActive.size == 0 || providersActive.contains(a.name))
}.pmap { a ->
normalSafeApiCall {
a.search(query)
}
}
var maxCount = 0
var providerCount = 0
for (res in list) {
if (res != null) {
if (res.size > maxCount) {
maxCount = res.size
}
providerCount++
}
}
if (providerCount == 0) return null
if (maxCount == 0) return ArrayList()
val result = ArrayList<SearchResponse>()
for (i in 0..maxCount) {
for (res in list) {
if (res != null) {
if (i < res.size) {
result.add(res[i])
}
}
}
}
return result
}
}

View file

@ -25,8 +25,6 @@ object APIHolder {
val unixTime: Long val unixTime: Long
get() = System.currentTimeMillis() / 1000L get() = System.currentTimeMillis() / 1000L
val allApi = AllProvider()
private const val defProvider = 0 private const val defProvider = 0
val apis = arrayListOf( val apis = arrayListOf(

View file

@ -7,8 +7,13 @@ import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
class APIRepository(val api: MainAPI) { class APIRepository(val api: MainAPI) {
companion object {
var providersActive = HashSet<String>()
}
val name: String get() = api.name val name: String get() = api.name
val mainUrl: String get() = api.mainUrl val mainUrl: String get() = api.mainUrl
val hasQuickSearch: Boolean get() = api.hasQuickSearch
suspend fun load(url: String): Resource<LoadResponse> { suspend fun load(url: String): Resource<LoadResponse> {
return safeApiCall { return safeApiCall {

View file

@ -1,6 +1,8 @@
package com.lagradost.cloudstream3.ui.home package com.lagradost.cloudstream3.ui.home
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.net.Uri import android.net.Uri
@ -45,6 +47,50 @@ import kotlinx.android.synthetic.main.fragment_home.*
const val HOME_BOOKMARK_VALUE = "home_bookmarked_last" const val HOME_BOOKMARK_VALUE = "home_bookmarked_last"
class HomeFragment : Fragment() { class HomeFragment : Fragment() {
companion object {
val configEvent = Event<Int>()
var currentSpan = 1
fun Activity.loadHomepageList(item: HomePageList) {
val context = this
val bottomSheetDialogBuilder = BottomSheetDialog(context)
bottomSheetDialogBuilder.setContentView(R.layout.home_episodes_expanded)
val title = bottomSheetDialogBuilder.findViewById<TextView>(R.id.home_expanded_text)!!
title.text = item.name
val recycle = bottomSheetDialogBuilder.findViewById<AutofitRecyclerView>(R.id.home_expanded_recycler)!!
val titleHolder = bottomSheetDialogBuilder.findViewById<FrameLayout>(R.id.home_expanded_drag_down)!!
titleHolder.setOnClickListener {
bottomSheetDialogBuilder.dismiss()
}
// Span settings
recycle.spanCount = currentSpan
recycle.adapter = SearchAdapter(item.list, recycle) { callback ->
handleSearchClickCallback(this, callback)
if (callback.action == SEARCH_ACTION_LOAD) {
bottomSheetDialogBuilder.dismiss()
}
}
val spanListener = { span: Int ->
recycle.spanCount = span
(recycle.adapter as SearchAdapter).notifyDataSetChanged()
}
configEvent += spanListener
bottomSheetDialogBuilder.setOnDismissListener {
configEvent -= spanListener
}
(recycle.adapter as SearchAdapter).notifyDataSetChanged()
bottomSheetDialogBuilder.show()
}
}
private lateinit var homeViewModel: HomeViewModel private lateinit var homeViewModel: HomeViewModel
override fun onCreateView( override fun onCreateView(
@ -58,8 +104,6 @@ class HomeFragment : Fragment() {
return inflater.inflate(R.layout.fragment_home, container, false) return inflater.inflate(R.layout.fragment_home, container, false)
} }
private val configEvent = Event<Int>()
private var currentSpan = 1
private var currentHomePage: HomePageResponse? = null private var currentHomePage: HomePageResponse? = null
var currentMainIndex = 0 var currentMainIndex = 0
var currentMainList: ArrayList<SearchResponse> = ArrayList() var currentMainList: ArrayList<SearchResponse> = ArrayList()
@ -254,48 +298,11 @@ class HomeFragment : Fragment() {
} }
} }
fun loadHomepageList(item: HomePageList) {
val bottomSheetDialogBuilder = BottomSheetDialog(view.context)
bottomSheetDialogBuilder.setContentView(R.layout.home_episodes_expanded)
val title = bottomSheetDialogBuilder.findViewById<TextView>(R.id.home_expanded_text)!!
title.text = item.name
val recycle = bottomSheetDialogBuilder.findViewById<AutofitRecyclerView>(R.id.home_expanded_recycler)!!
val titleHolder = bottomSheetDialogBuilder.findViewById<FrameLayout>(R.id.home_expanded_drag_down)!!
titleHolder.setOnClickListener {
bottomSheetDialogBuilder.dismiss()
}
// Span settings
recycle.spanCount = currentSpan
recycle.adapter = SearchAdapter(item.list, recycle) { callback ->
handleSearchClickCallback(activity, callback)
if (callback.action == SEARCH_ACTION_LOAD) {
bottomSheetDialogBuilder.dismiss()
}
}
val spanListener = { span: Int ->
recycle.spanCount = span
(recycle.adapter as SearchAdapter).notifyDataSetChanged()
}
configEvent += spanListener
bottomSheetDialogBuilder.setOnDismissListener {
configEvent -= spanListener
}
(recycle.adapter as SearchAdapter).notifyDataSetChanged()
bottomSheetDialogBuilder.show()
}
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> = ParentItemAdapter(listOf(), { callback -> val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> = ParentItemAdapter(listOf(), { callback ->
handleSearchClickCallback(activity, callback) handleSearchClickCallback(activity, callback)
}, { item -> }, { item ->
loadHomepageList(item) activity?.loadHomepageList(item)
}) })
observe(homeViewModel.availableWatchStatusTypes) { availableWatchStatusTypes -> observe(homeViewModel.availableWatchStatusTypes) { availableWatchStatusTypes ->
@ -319,7 +326,7 @@ class HomeFragment : Fragment() {
home_bookmarked_child_recyclerview?.adapter?.notifyDataSetChanged() home_bookmarked_child_recyclerview?.adapter?.notifyDataSetChanged()
home_bookmarked_child_more_info.setOnClickListener { home_bookmarked_child_more_info.setOnClickListener {
loadHomepageList( activity?.loadHomepageList(
HomePageList( HomePageList(
home_bookmarked_parent_item_title?.text?.toString() ?: getString(R.string.error_bookmarks_text), home_bookmarked_parent_item_title?.text?.toString() ?: getString(R.string.error_bookmarks_text),
bookmarks bookmarks

View file

@ -14,16 +14,20 @@ import androidx.appcompat.widget.SearchView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.APIHolder.allApi
import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.getApiSettings import com.lagradost.cloudstream3.APIHolder.getApiSettings
import com.lagradost.cloudstream3.HomePageList
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.utils.UIHelper.getGridIsCompact
import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment import com.lagradost.cloudstream3.ui.APIRepository.Companion.providersActive
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.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.utils.UIHelper.getGridIsCompact
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
import kotlinx.android.synthetic.main.fragment_search.* import kotlinx.android.synthetic.main.fragment_search.*
@ -49,11 +53,14 @@ class SearchFragment : Fragment() {
val spanCountPortrait = if (compactView) 1 else 3 val spanCountPortrait = if (compactView) 1 else 3
val orientation = resources.configuration.orientation val orientation = resources.configuration.orientation
if (orientation == Configuration.ORIENTATION_LANDSCAPE) { val currentSpan = if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
cardSpace.spanCount = spanCountLandscape spanCountLandscape
} else { } else {
cardSpace.spanCount = spanCountPortrait spanCountPortrait
} }
cardSpace.spanCount = currentSpan
HomeFragment.currentSpan = currentSpan
HomeFragment.configEvent.invoke(currentSpan)
} }
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
@ -113,7 +120,7 @@ class SearchFragment : Fragment() {
apiNames.filter { a -> apiNamesSettingLocal.contains(a) }.toSet() apiNames.filter { a -> apiNamesSettingLocal.contains(a) }.toSet()
) )
edit.apply() edit.apply()
allApi.providersActive = apiNamesSettingLocal providersActive = apiNamesSettingLocal
} }
} }
builder.setTitle("Search Providers") builder.setTitle("Search Providers")
@ -138,9 +145,11 @@ class SearchFragment : Fragment() {
when (it) { when (it) {
is Resource.Success -> { is Resource.Success -> {
it.value.let { data -> it.value.let { data ->
if (data != null) { if (data.isNotEmpty()) {
(cardSpace?.adapter as SearchAdapter?)?.cardList = data.filterNotNull() (cardSpace?.adapter as SearchAdapter?)?.apply {
cardSpace?.adapter?.notifyDataSetChanged() cardList = data.toList()
notifyDataSetChanged()
}
} }
} }
searchExitIcon.alpha = 1f searchExitIcon.alpha = 1f
@ -157,8 +166,18 @@ class SearchFragment : Fragment() {
} }
} }
} }
observe(searchViewModel.currentSearch) { list ->
(search_master_recycler?.adapter as ParentItemAdapter?)?.apply {
items = list.map {
HomePageList(it.apiName, if (it.data is Resource.Success) it.data.value else ArrayList())
}
notifyDataSetChanged()
}
}
activity?.let { activity?.let {
allApi.providersActive = it.getApiSettings() providersActive = it.getApiSettings()
} }
main_search.setOnQueryTextFocusChangeListener { searchView, b -> main_search.setOnQueryTextFocusChangeListener { searchView, b ->
@ -173,6 +192,22 @@ class SearchFragment : Fragment() {
} }
} }
main_search.onActionViewExpanded() main_search.onActionViewExpanded()
val masterAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder> = ParentItemAdapter(listOf(), { callback ->
SearchHelper.handleSearchClickCallback(activity, callback)
}, { item ->
activity?.loadHomepageList(item)
})
search_master_recycler.adapter = masterAdapter
search_master_recycler.layoutManager = GridLayoutManager(context, 1)
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
// SubtitlesFragment.push(activity) // SubtitlesFragment.push(activity)
//searchViewModel.search("iron man") //searchViewModel.search("iron man")
//(activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro") //(activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro")

View file

@ -4,18 +4,27 @@ 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.allApi import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.ui.APIRepository import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.APIRepository.Companion.providersActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
data class OnGoingSearch(
val apiName: String,
val data: Resource<ArrayList<SearchResponse>>
)
class SearchViewModel : ViewModel() { class SearchViewModel : ViewModel() {
private val _searchResponse: MutableLiveData<Resource<ArrayList<SearchResponse>>> = MutableLiveData() private val _searchResponse: MutableLiveData<Resource<ArrayList<SearchResponse>>> = MutableLiveData()
val searchResponse: LiveData<Resource<ArrayList<SearchResponse>>> get() = _searchResponse val searchResponse: LiveData<Resource<ArrayList<SearchResponse>>> get() = _searchResponse
private val _currentSearch: MutableLiveData<ArrayList<OnGoingSearch>> = MutableLiveData()
val currentSearch: LiveData<ArrayList<OnGoingSearch>> get() = _currentSearch
var searchCounter = 0 var searchCounter = 0
private val repo = APIRepository(allApi) private val repos = apis.map { APIRepository(it) }
private fun clearSearch() { private fun clearSearch() {
_searchResponse.postValue(Resource.Success(ArrayList())) _searchResponse.postValue(Resource.Success(ArrayList()))
@ -23,29 +32,50 @@ class SearchViewModel : ViewModel() {
fun search(query: String) = viewModelScope.launch { fun search(query: String) = viewModelScope.launch {
searchCounter++ searchCounter++
if(query.length <= 1) { if (query.length <= 1) {
clearSearch() clearSearch()
return@launch return@launch
} }
val localSearchCounter = searchCounter val localSearchCounter = searchCounter
_searchResponse.postValue(Resource.Loading()) _searchResponse.postValue(Resource.Loading())
val data = repo.search(query)
if(localSearchCounter != searchCounter) return@launch val currentList = ArrayList<OnGoingSearch>()
_searchResponse.postValue(data)
_currentSearch.postValue(ArrayList())
repos.filter { a ->
(providersActive.size == 0 || providersActive.contains(a.name))
}.map { a ->
currentList.add(OnGoingSearch(a.name, a.search(query)))
if (localSearchCounter == searchCounter) {
_currentSearch.postValue(currentList)
}
}
_currentSearch.postValue(currentList)
if (localSearchCounter != searchCounter) return@launch
val list = ArrayList<SearchResponse>()
val nestedList = currentList.map { it.data }.filterIsInstance<Resource.Success<ArrayList<SearchResponse>>>().map { it.value }
// I do it this way to move the relevant search results to the top
var index = 0
while (true) {
var added = 0
for (sublist in nestedList) {
if(sublist.size > index) {
list.add(sublist[index])
added++
}
}
if(added == 0) break
index++
}
_searchResponse.postValue(Resource.Success(list))
} }
fun quickSearch(query: String) = viewModelScope.launch { fun quickSearch(query: String) = viewModelScope.launch {
searchCounter++
if(query.length <= 1) {
clearSearch()
return@launch return@launch
} }
val localSearchCounter = searchCounter
_searchResponse.postValue(Resource.Loading())
val data = repo.quickSearch(query)
if(localSearchCounter != searchCounter) return@launch
_searchResponse.postValue(data)
}
} }

View file

@ -1,8 +1,8 @@
<vector <vector
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:name="vector" android:name="vector"
android:width="850dp" android:width="24dp"
android:height="850dp" android:height="24dp"
android:viewportWidth="850" android:viewportWidth="850"
android:viewportHeight="850"> android:viewportHeight="850">
<path <path

View file

@ -73,7 +73,11 @@
android:id="@+id/cardSpace" android:id="@+id/cardSpace"
tools:listitem="@layout/search_result_grid" tools:listitem="@layout/search_result_grid"
android:orientation="vertical" android:orientation="vertical"
> />
</com.lagradost.cloudstream3.ui.AutofitRecyclerView> <androidx.recyclerview.widget.RecyclerView
android:id="@+id/search_master_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/homepage_parent"
/>
</LinearLayout> </LinearLayout>

View file

@ -23,9 +23,9 @@
<color name="white">#FFF</color> <color name="white">#FFF</color>
<color name="dubColor">#3d50fa</color> <!--3b65f5 f18c82 8294F1--> <color name="dubColor">#3d50fa</color> <!--3b65f5 f18c82 8294F1-->
<color name="dubColorBg">#4D3B65F5</color> <color name="dubColorBg">#803B65F5</color>
<color name="subColor">#F54A3B</color> <!--F53B66 FA3D79--> <color name="subColor">#F54A3B</color> <!--F53B66 FA3D79-->
<color name="subColorBg">#4DF53B66</color> <color name="subColorBg">#80F53B66</color>
<color name="typeColor">#F54A3B</color> <color name="typeColor">#F54A3B</color>
<color name="typeColorBg">#4DF54A3B</color> <color name="typeColorBg">#4DF54A3B</color>

View file

@ -58,6 +58,19 @@
/> />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory
android:key="search"
android:title="Search"
app:isPreferenceVisible="true"
>
<SwitchPreference
android:icon="@drawable/search_icon"
app:key="advanced_search"
android:title="Advanced Search"
android:summary="Gives you the search results separated by provider"
app:defaultValue="true"
/>
</PreferenceCategory>
<PreferenceCategory <PreferenceCategory
android:key="info" android:key="info"