diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt index cf9bc9975c0..d09dd1150b9 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt @@ -225,7 +225,8 @@ class QuickSearchFragment : BaseFragment( val homePageList = HomePageList( ongoing.key, - dataListFiltered + dataListFiltered, + isHorizontalImages = ongoing.value.isHorizontalImages ) val expandableList = HomeViewModel.ExpandableHomepageList( @@ -268,12 +269,15 @@ class QuickSearchFragment : BaseFragment( when (it) { is Resource.Success -> { it.value.let { data -> - val adapter = - (binding.quickSearchAutofitResults.adapter as? SearchAdapter) - adapter?.submitList( - context?.filterSearchResultByFilmQuality(data.list) ?: data.list - ) - adapter?.hasNext = data.hasNext + val list = context?.filterSearchResultByFilmQuality(data.list) ?: data.list + val isHorizontal = data.isHorizontalImages + binding.quickSearchAutofitResults.spanCount = + context?.getSpanCount(isHorizontal) ?: binding.quickSearchAutofitResults.spanCount + (binding.quickSearchAutofitResults.adapter as? SearchAdapter)?.apply { + this.isHorizontal = isHorizontal + submitList(list) + hasNext = data.hasNext + } } searchExitIcon?.alpha = 1f binding.quickSearchLoadingBar.alpha = 0f diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt index 38b24b26517..2edff2951a9 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.app.Dialog import android.content.Intent import android.content.res.ColorStateList +import android.content.res.Configuration import android.graphics.Rect import android.net.Uri import android.os.Build @@ -1450,12 +1451,29 @@ open class ResultFragmentPhone : BaseFragment( private fun setRecommendations(rec: List?, validApiName: String?) { val isInvalid = rec.isNullOrEmpty() val matchAgainst = validApiName ?: rec?.firstOrNull()?.apiName + val isHorizontal = viewModel.isRecommendationsHorizontal() recommendationBinding?.apply { root.isGone = isInvalid + if (isHorizontal) { + val orientation = root.context.resources.configuration.orientation + resultRecommendationsList.spanCount = + if (orientation == Configuration.ORIENTATION_LANDSCAPE) 3 else 2 + } + val currentAdapter = resultRecommendationsList.adapter as? SearchAdapter + val adapter = if (currentAdapter != null) { + currentAdapter.apply { this.isHorizontal = isHorizontal } + } else { + SearchAdapter( + resultRecommendationsList, + isHorizontal = isHorizontal, + ) { callback -> + SearchHelper.handleSearchClickCallback(callback) + }.also { resultRecommendationsList.adapter = it } + } root.post { rec?.let { list -> - (resultRecommendationsList.adapter as? SearchAdapter)?.submitList(list.filter { it.apiName == matchAgainst }) + adapter.submitList(list.filter { it.apiName == matchAgainst }) } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt index c316fae1715..c2082543c48 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt @@ -152,11 +152,30 @@ class ResultFragmentTv : BaseFragment( private fun setRecommendations(rec: List?, validApiName: String?) { currentRecommendations = rec ?: emptyList() val isInvalid = rec.isNullOrEmpty() + val matchAgainst = validApiName ?: rec?.firstOrNull()?.apiName + val isHorizontal = viewModel.isRecommendationsHorizontal() binding?.apply { resultRecommendationsList.isGone = isInvalid resultRecommendationsHolder.isGone = isInvalid - val matchAgainst = validApiName ?: rec?.firstOrNull()?.apiName - (resultRecommendationsList.adapter as? SearchAdapter)?.submitList(rec?.filter { it.apiName == matchAgainst } + if (isHorizontal) { + resultRecommendationsList.spanCount = 5 + } else { + resultRecommendationsList.spanCount = 8 + } + val currentAdapter = resultRecommendationsList.adapter as? SearchAdapter + val adapter = if (currentAdapter != null) { + currentAdapter.apply { this.isHorizontal = isHorizontal } + } else { + SearchAdapter( + resultRecommendationsList, + isHorizontal = isHorizontal, + ) { callback -> + if (callback.action == SEARCH_ACTION_FOCUSED) { + toggleEpisodes(false) + } else SearchHelper.handleSearchClickCallback(callback) + }.also { resultRecommendationsList.adapter = it } + } + adapter.submitList(rec?.filter { it.apiName == matchAgainst } ?: emptyList()) rec?.map { it.apiName }?.distinct()?.let { apiNames -> diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt index c519e0de230..074e68c642e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt @@ -193,6 +193,7 @@ data class ResultData( val nextAiringDate: UiText?, val nextAiringEpisode: UiText?, val plotHeaderText: UiText, + val isHorizontalImages: Boolean = false, val posterHeaders: Map? = null, ) @@ -305,6 +306,7 @@ fun LoadResponse.toResultData(repo: APIRepository): ResultData { backgroundPosterUrl = backgroundPosterUrl, logoUrl = logoUrl, title = name, + isHorizontalImages = this.isHorizontalImages, typeText = txt( when (type) { TvType.TvSeries -> R.string.tv_series_singular @@ -492,6 +494,10 @@ class ResultViewModel2 : ViewModel() { MutableLiveData(null) val page: LiveData?> = _page + fun isRecommendationsHorizontal(): Boolean { + return (page.value as? Resource.Success)?.value?.isHorizontalImages == true + } + private val _episodes: MutableLiveData>?> = MutableLiveData(Resource.Loading()) val episodes: LiveData>?> = _episodes @@ -2501,8 +2507,8 @@ class ResultViewModel2 : ViewModel() { // this instantly updates the metadata on the page private fun postPage(loadResponse: LoadResponse, apiRepository: APIRepository) { - _recommendations.postValue(loadResponse.recommendations ?: emptyList()) _page.postValue(Resource.Success(loadResponse.toResultData(apiRepository))) + _recommendations.postValue(loadResponse.recommendations ?: emptyList()) } fun hasLoaded() = currentResponse != null @@ -2559,6 +2565,7 @@ class ResultViewModel2 : ViewModel() { override var duration: Int? = null, override var trailers: MutableList = mutableListOf(), override var recommendations: List? = null, + override var isHorizontalImages: Boolean = false, override var actors: List? = null, override var comingSoon: Boolean = false, override var syncData: MutableMap = mutableMapOf(), 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 7b63b6edec6..91d4633079e 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 @@ -32,7 +32,7 @@ class SearchClickCallback( class SearchAdapter( private val resView: AutofitRecyclerView, - private val isHorizontal:Boolean = false, + var isHorizontal: Boolean = false, private val clickCallback: (SearchClickCallback) -> Unit, ) : NoStateAdapter(diffCallback = BaseDiffCallback(itemSame = { a, b -> if (a.id != null || b.id != null) { @@ -48,7 +48,7 @@ class SearchAdapter( var hasNext: Boolean = false - private val coverRatio = if(isHorizontal) 1.8 else 0.68 + private val coverRatio get() = if (isHorizontal) 1.8 else 0.68 private val coverHeight: Int get() = (resView.itemWidth / coverRatio).roundToInt() 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 5f5b064b543..2f0dbe97430 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 @@ -469,9 +469,12 @@ class SearchFragment : BaseFragment( it.value.let { data -> val list = data.list if (list.isNotEmpty()) { - (binding.searchAutofitResults.adapter as? SearchAdapter)?.submitList( - list - ) + val isHorizontal = data.isHorizontalImages + binding.searchAutofitResults.spanCount = view?.context?.getSpanCount(isHorizontal) ?: currentSpan + (binding.searchAutofitResults.adapter as? SearchAdapter)?.apply { + this.isHorizontal = isHorizontal + submitList(list) + } } } searchExitIcon?.alpha = 1f @@ -512,7 +515,8 @@ class SearchFragment : BaseFragment( val homePageList = HomePageList( providerName, - dataListFiltered + dataListFiltered, + isHorizontalImages = providerData.isHorizontalImages ) HomeViewModel.ExpandableHomepageList( 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 f60588e35cd..87b45c400a8 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 @@ -27,7 +27,10 @@ import kotlinx.coroutines.withContext data class ExpandableSearchList( - var list: List, var currentPage: Int, var hasNext: Boolean, + var list: List, + var currentPage: Int, + var hasNext: Boolean, + var isHorizontalImages: Boolean = false, ) const val SEARCH_HISTORY_KEY = "search_history" @@ -163,7 +166,7 @@ class SearchViewModel : ViewModel() { val item = expandableSearches[name] ?: return null return HomeViewModel.ExpandableHomepageList( - HomePageList(name, item.list), + HomePageList(name, item.list, isHorizontalImages = item.isHorizontalImages), item.currentPage, item.hasNext ) @@ -192,7 +195,8 @@ class SearchViewModel : ViewModel() { index++ } - return ExpandableSearchList(list, 1, false) + val isHorizontal = lists.values.any { it.isHorizontalImages } + return ExpandableSearchList(list, 1, false, isHorizontal) } private fun search( @@ -237,7 +241,12 @@ class SearchViewModel : ViewModel() { if (search is Resource.Success) { val searchValue = search.value expandableSearches[a.name] = - ExpandableSearchList(searchValue.items, 1, searchValue.hasNext) + ExpandableSearchList( + searchValue.items, + 1, + searchValue.hasNext, + searchValue.isHorizontalImages + ) } _currentSearch.postValue(expandableSearches) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt index a848bee260e..b87353b649a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt @@ -209,7 +209,7 @@ object UIHelper { fun Context.getSpanCount(isHorizontal:Boolean=false): Int { // val compactView = false - val spanCountLandscape = if (isHorizontal) 3 else 6 + val spanCountLandscape = if (isHorizontal) 5 else 6 val spanCountPortrait = if (isHorizontal) 2 else 3 val orientation = resources.configuration.orientation diff --git a/library/api/jvm/library.api b/library/api/jvm/library.api index 195cd49fe55..23526b0fc67 100644 --- a/library/api/jvm/library.api +++ b/library/api/jvm/library.api @@ -712,12 +712,14 @@ public abstract interface class com/lagradost/cloudstream3/LoadResponse { public abstract fun getUniqueUrl ()Ljava/lang/String; public abstract fun getUrl ()Ljava/lang/String; public abstract fun getYear ()Ljava/lang/Integer; + public fun isHorizontalImages ()Z public abstract fun setActors (Ljava/util/List;)V public abstract fun setApiName (Ljava/lang/String;)V public abstract fun setBackgroundPosterUrl (Ljava/lang/String;)V public abstract fun setComingSoon (Z)V public abstract fun setContentRating (Ljava/lang/String;)V public abstract fun setDuration (Ljava/lang/Integer;)V + public fun setHorizontalImages (Z)V public abstract fun setLogoUrl (Ljava/lang/String;)V public abstract fun setName (Ljava/lang/String;)V public abstract fun setPlot (Ljava/lang/String;)V @@ -783,6 +785,8 @@ public final class com/lagradost/cloudstream3/LoadResponse$Companion { public final class com/lagradost/cloudstream3/LoadResponse$DefaultImpls { public static fun getRating (Lcom/lagradost/cloudstream3/LoadResponse;)Ljava/lang/Integer; + public static fun isHorizontalImages (Lcom/lagradost/cloudstream3/LoadResponse;)Z + public static fun setHorizontalImages (Lcom/lagradost/cloudstream3/LoadResponse;Z)V public static fun setRating (Lcom/lagradost/cloudstream3/LoadResponse;Ljava/lang/Integer;)V } @@ -1279,10 +1283,13 @@ public abstract interface class com/lagradost/cloudstream3/SearchResponse { public final class com/lagradost/cloudstream3/SearchResponseList { public fun (Ljava/util/List;Z)V public synthetic fun (Ljava/util/List;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/util/List;ZZ)V + public synthetic fun (Ljava/util/List;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/util/List; public final fun component2 ()Z - public final fun copy (Ljava/util/List;Z)Lcom/lagradost/cloudstream3/SearchResponseList; - public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/SearchResponseList;Ljava/util/List;ZILjava/lang/Object;)Lcom/lagradost/cloudstream3/SearchResponseList; + public final fun component3 ()Z + public final fun copy (Ljava/util/List;ZZ)Lcom/lagradost/cloudstream3/SearchResponseList; + public static synthetic fun copy$default (Lcom/lagradost/cloudstream3/SearchResponseList;Ljava/util/List;ZZILjava/lang/Object;)Lcom/lagradost/cloudstream3/SearchResponseList; public fun equals (Ljava/lang/Object;)Z public final fun getHasNext ()Z public final fun getItems ()Ljava/util/List; diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainAPI.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainAPI.kt index ca024b463a1..e8fbcee8de9 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainAPI.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainAPI.kt @@ -478,11 +478,21 @@ fun newHomePageResponse(list: List, hasNext: Boolean? = null): Hom fun newSearchResponseList( list: List, hasNext: Boolean? = null, +): SearchResponseList { + return newSearchResponseList(list, hasNext, false) +} + +@Prerelease +fun newSearchResponseList( + list: List, + hasNext: Boolean? = null, + isHorizontalImages: Boolean, ): SearchResponseList { @Suppress("DEPRECATION_ERROR") return SearchResponseList( list, - hasNext = hasNext ?: list.isNotEmpty() + hasNext = hasNext ?: list.isNotEmpty(), + isHorizontalImages = isHorizontalImages ) } @@ -490,6 +500,14 @@ fun List.toNewSearchResponseList(hasNext: Boolean? = null) : Sea return newSearchResponseList(this, hasNext) } +@Prerelease +fun List.toNewSearchResponseList( + hasNext: Boolean? = null, + isHorizontalImages: Boolean, +): SearchResponseList { + return newSearchResponseList(this, hasNext, isHorizontalImages) +} + /**Every provider will **not** have try catch built in, so handle exceptions when calling these functions*/ abstract class MainAPI { companion object { @@ -1293,8 +1311,16 @@ data class SearchResponseList @Deprecated("Use newSearchResponseList method", level = DeprecationLevel.ERROR) constructor( val items: List, - val hasNext: Boolean = false -) + val hasNext: Boolean = false, + @property:Prerelease val isHorizontalImages: Boolean = false +) { + @Suppress("DEPRECATION_ERROR") + @Deprecated("Use newSearchResponseList method", level = DeprecationLevel.ERROR) + constructor( + items: List, + hasNext: Boolean = false + ) : this(items, hasNext, false) +} /** enum class holds search quality. * @@ -1826,6 +1852,10 @@ interface LoadResponse { var trailers: MutableList var recommendations: List? + @property:Prerelease + var isHorizontalImages: Boolean + get() = false + set(value) {} var actors: List? var comingSoon: Boolean var syncData: MutableMap @@ -2290,7 +2320,10 @@ constructor( override var logoUrl: String? = null, override var contentRating: String? = null, override var uniqueUrl: String = url -) : LoadResponse +) : LoadResponse { + @property:Prerelease + override var isHorizontalImages: Boolean = false +} suspend fun MainAPI.newTorrentLoadResponse( name: String, @@ -2352,6 +2385,8 @@ constructor( override var contentRating: String? = null, override var uniqueUrl: String = url ) : LoadResponse, EpisodeResponse { + @property:Prerelease + override var isHorizontalImages: Boolean = false override fun getLatestEpisodes(): Map { return episodes.map { (status, episodes) -> @@ -2437,7 +2472,10 @@ constructor( override var logoUrl: String? = null, override var contentRating: String? = null, override var uniqueUrl: String = url -) : LoadResponse +) : LoadResponse { + @property:Prerelease + override var isHorizontalImages: Boolean = false +} suspend fun MainAPI.newLiveStreamLoadResponse( name: String, @@ -2486,7 +2524,10 @@ constructor( override var logoUrl: String? = null, override var contentRating: String? = null, override var uniqueUrl: String = url -) : LoadResponse +) : LoadResponse { + @property:Prerelease + override var isHorizontalImages: Boolean = false +} suspend fun MainAPI.newMovieLoadResponse( name: String, @@ -2702,6 +2743,8 @@ constructor( override var contentRating: String? = null, override var uniqueUrl: String = url ) : LoadResponse, EpisodeResponse { + @property:Prerelease + override var isHorizontalImages: Boolean = false override fun getLatestEpisodes(): Map { val maxSeason = episodes.maxOfOrNull { it.season ?: Int.MIN_VALUE }.takeUnless { it == Int.MIN_VALUE }