mirror of
https://github.com/recloudstream/cloudstream.git
synced 2025-05-18 03:36:19 +08:00
Feature: episode sort button (#1565)
- Add sort direction indicator (▲/▼) to button text
This commit is contained in:

committed by
GitHub

parent
846ff38c3b
commit
ab23f23c71
@ -2,6 +2,7 @@ package com.lagradost.cloudstream3.ui.result
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Rect
|
||||
@ -36,6 +37,7 @@ import com.lagradost.cloudstream3.LoadResponse
|
||||
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.SearchResponse
|
||||
import com.lagradost.cloudstream3.databinding.BottomSelectionDialogBinding
|
||||
import com.lagradost.cloudstream3.databinding.FragmentResultBinding
|
||||
import com.lagradost.cloudstream3.databinding.FragmentResultSwipeBinding
|
||||
import com.lagradost.cloudstream3.databinding.ResultRecommendationsBinding
|
||||
@ -253,6 +255,7 @@ open class ResultFragmentPhone : FullScreenPlayer() {
|
||||
|
||||
var selectSeason: String? = null
|
||||
var selectEpisodeRange: String? = null
|
||||
var selectSort: EpisodeSortType? = null
|
||||
|
||||
private fun setUrl(url: String?) {
|
||||
if (url == null) {
|
||||
@ -358,7 +361,7 @@ open class ResultFragmentPhone : FullScreenPlayer() {
|
||||
binding?.resultSearch?.setOnClickListener {
|
||||
QuickSearchFragment.pushSearch(activity, storedData.name)
|
||||
}
|
||||
|
||||
|
||||
resultBinding?.apply {
|
||||
resultReloadConnectionerror.setOnClickListener {
|
||||
viewModel.load(
|
||||
@ -406,8 +409,32 @@ open class ResultFragmentPhone : FullScreenPlayer() {
|
||||
{ downloadClickEvent ->
|
||||
DownloadButtonSetup.handleDownloadClick(downloadClickEvent)
|
||||
}
|
||||
|
||||
)
|
||||
|
||||
observeNullable(viewModel.selectedSorting) {
|
||||
resultSortButton.setText(it)
|
||||
}
|
||||
|
||||
observe(viewModel.sortSelections) { sort ->
|
||||
resultBinding?.resultSortButton?.setOnClickListener { view ->
|
||||
view?.context?.let { ctx ->
|
||||
val names = sort
|
||||
.mapNotNull { (text, r) ->
|
||||
r to (text.asStringNull(ctx) ?: return@mapNotNull null)
|
||||
}
|
||||
|
||||
activity?.showDialog(
|
||||
names.map { it.second },
|
||||
viewModel.selectedSortingIndex.value ?: -1,
|
||||
"",
|
||||
false,
|
||||
{}) { itemId ->
|
||||
viewModel.setSort(names[itemId].first)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resultScroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
||||
val dy = scrollY - oldScrollY
|
||||
@ -458,8 +485,12 @@ open class ResultFragmentPhone : FullScreenPlayer() {
|
||||
}
|
||||
|
||||
val name = (viewModel.page.value as? Resource.Success)?.value?.title
|
||||
?: com.lagradost.cloudstream3.utils.txt(R.string.no_data).asStringNull(context) ?: ""
|
||||
showToast(com.lagradost.cloudstream3.utils.txt(message, name), Toast.LENGTH_SHORT)
|
||||
?: com.lagradost.cloudstream3.utils.txt(R.string.no_data)
|
||||
.asStringNull(context) ?: ""
|
||||
showToast(
|
||||
com.lagradost.cloudstream3.utils.txt(message, name),
|
||||
Toast.LENGTH_SHORT
|
||||
)
|
||||
}
|
||||
context?.let { openBatteryOptimizationSettings(it) }
|
||||
}
|
||||
@ -474,8 +505,12 @@ open class ResultFragmentPhone : FullScreenPlayer() {
|
||||
}
|
||||
|
||||
val name = (viewModel.page.value as? Resource.Success)?.value?.title
|
||||
?: com.lagradost.cloudstream3.utils.txt(R.string.no_data).asStringNull(context) ?: ""
|
||||
showToast(com.lagradost.cloudstream3.utils.txt(message, name), Toast.LENGTH_SHORT)
|
||||
?: com.lagradost.cloudstream3.utils.txt(R.string.no_data)
|
||||
.asStringNull(context) ?: ""
|
||||
showToast(
|
||||
com.lagradost.cloudstream3.utils.txt(message, name),
|
||||
Toast.LENGTH_SHORT
|
||||
)
|
||||
}
|
||||
}
|
||||
mediaRouteButton.apply {
|
||||
@ -627,6 +662,7 @@ open class ResultFragmentPhone : FullScreenPlayer() {
|
||||
// no failure?
|
||||
resultEpisodeLoading.isVisible = episodes is Resource.Loading
|
||||
resultEpisodes.isVisible = episodes is Resource.Success
|
||||
resultSortButton.isVisible = episodes is Resource.Success
|
||||
if (episodes is Resource.Success) {
|
||||
(resultEpisodes.adapter as? EpisodeAdapter)?.updateList(episodes.value)
|
||||
}
|
||||
@ -713,10 +749,23 @@ open class ResultFragmentPhone : FullScreenPlayer() {
|
||||
resultNextAiring.setText(d.nextAiringEpisode)
|
||||
resultNextAiringTime.setText(d.nextAiringDate)
|
||||
resultPoster.loadImage(d.posterImage, headers = d.posterHeaders) {
|
||||
error{ getImageFromDrawable(context?: return@error null, R.drawable.default_cover) }
|
||||
error {
|
||||
getImageFromDrawable(
|
||||
context ?: return@error null,
|
||||
R.drawable.default_cover
|
||||
)
|
||||
}
|
||||
}
|
||||
resultPosterBackground.loadImage(d.posterBackgroundImage, headers = d.posterHeaders) {
|
||||
error{ getImageFromDrawable(context?: return@error null, R.drawable.default_cover) }
|
||||
resultPosterBackground.loadImage(
|
||||
d.posterBackgroundImage,
|
||||
headers = d.posterHeaders
|
||||
) {
|
||||
error {
|
||||
getImageFromDrawable(
|
||||
context ?: return@error null,
|
||||
R.drawable.default_cover
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
var isExpanded = false
|
||||
@ -790,7 +839,10 @@ open class ResultFragmentPhone : FullScreenPlayer() {
|
||||
resultReloadConnectionOpenInBrowser.isVisible = data is Resource.Failure
|
||||
|
||||
resultTitle.setOnLongClickListener {
|
||||
clipboardHelper(com.lagradost.cloudstream3.utils.txt(R.string.title), resultTitle.text)
|
||||
clipboardHelper(
|
||||
com.lagradost.cloudstream3.utils.txt(R.string.title),
|
||||
resultTitle.text
|
||||
)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
@ -145,6 +145,15 @@ enum class LibraryListType {
|
||||
SUBSCRIPTIONS
|
||||
}
|
||||
|
||||
enum class EpisodeSortType {
|
||||
NUMBER_ASC,
|
||||
NUMBER_DESC,
|
||||
RATING_HIGH_LOW,
|
||||
RATING_LOW_HIGH,
|
||||
DATE_NEWEST,
|
||||
DATE_OLDEST
|
||||
}
|
||||
|
||||
fun txt(status: DubStatus?): UiText? {
|
||||
return txt(
|
||||
when (status) {
|
||||
@ -399,6 +408,7 @@ class ResultViewModel2 : ViewModel() {
|
||||
private var currentMeta: SyncAPI.SyncResult? = null
|
||||
private var currentSync: Map<String, String>? = null
|
||||
private var currentIndex: EpisodeIndexer? = null
|
||||
private var currentSorting: EpisodeSortType? = null
|
||||
private var currentRange: EpisodeRange? = null
|
||||
private var currentShowFillers: Boolean = false
|
||||
var currentRepo: APIRepository? = null
|
||||
@ -452,6 +462,18 @@ class ResultViewModel2 : ViewModel() {
|
||||
MutableLiveData(null)
|
||||
val selectedRange: LiveData<UiText?> = _selectedRange
|
||||
|
||||
private val _selectedSorting: MutableLiveData<UiText?> =
|
||||
MutableLiveData(null)
|
||||
val selectedSorting: LiveData<UiText?> = _selectedSorting
|
||||
|
||||
private val _selectedSortingIndex: MutableLiveData<Int> =
|
||||
MutableLiveData(-1)
|
||||
val selectedSortingIndex: LiveData<Int> = _selectedSortingIndex
|
||||
|
||||
private val _sortSelections: MutableLiveData<List<Pair<UiText, EpisodeSortType>>> =
|
||||
MutableLiveData(emptyList())
|
||||
val sortSelections: LiveData<List<Pair<UiText, EpisodeSortType>>> = _sortSelections
|
||||
|
||||
private val _selectedSeason: MutableLiveData<UiText?> =
|
||||
MutableLiveData(null)
|
||||
val selectedSeason: LiveData<UiText?> = _selectedSeason
|
||||
@ -770,13 +792,17 @@ class ResultViewModel2 : ViewModel() {
|
||||
val generator = RepoLinkGenerator(listOf(episode))
|
||||
val currentLinks = mutableSetOf<ExtractorLink>()
|
||||
val currentSubs = mutableSetOf<SubtitleData>()
|
||||
generator.generateLinks(clearCache = false, allowedTypes = LOADTYPE_INAPP_DOWNLOAD, callback = {
|
||||
it.first?.let { link ->
|
||||
currentLinks.add(link)
|
||||
}
|
||||
}, subtitleCallback = { sub ->
|
||||
currentSubs.add(sub)
|
||||
})
|
||||
generator.generateLinks(
|
||||
clearCache = false,
|
||||
allowedTypes = LOADTYPE_INAPP_DOWNLOAD,
|
||||
callback = {
|
||||
it.first?.let { link ->
|
||||
currentLinks.add(link)
|
||||
}
|
||||
},
|
||||
subtitleCallback = { sub ->
|
||||
currentSubs.add(sub)
|
||||
})
|
||||
|
||||
if (currentLinks.isEmpty()) {
|
||||
main {
|
||||
@ -921,7 +947,12 @@ class ResultViewModel2 : ViewModel() {
|
||||
isVisible: Boolean = true
|
||||
) {
|
||||
if (activity == null) return
|
||||
loadLinks(result, isVisible = isVisible, sourceTypes = LOADTYPE_CHROMECAST, isCasting = true) { data ->
|
||||
loadLinks(
|
||||
result,
|
||||
isVisible = isVisible,
|
||||
sourceTypes = LOADTYPE_CHROMECAST,
|
||||
isCasting = true
|
||||
) { data ->
|
||||
startChromecast(activity, result, data.links, data.subs, 0)
|
||||
}
|
||||
}
|
||||
@ -1344,7 +1375,8 @@ class ResultViewModel2 : ViewModel() {
|
||||
}
|
||||
try {
|
||||
updatePage()
|
||||
tempGenerator.generateLinks(clearCache,
|
||||
tempGenerator.generateLinks(
|
||||
clearCache,
|
||||
allowedTypes = sourceTypes,
|
||||
callback = { (link, _) ->
|
||||
if (link != null) {
|
||||
@ -1353,10 +1385,11 @@ class ResultViewModel2 : ViewModel() {
|
||||
}
|
||||
},
|
||||
subtitleCallback = { sub ->
|
||||
subs += sub
|
||||
updatePage()
|
||||
},
|
||||
isCasting = isCasting)
|
||||
subs += sub
|
||||
updatePage()
|
||||
},
|
||||
isCasting = isCasting
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
logError(e)
|
||||
} finally {
|
||||
@ -1806,15 +1839,21 @@ class ResultViewModel2 : ViewModel() {
|
||||
}
|
||||
|
||||
fun changeDubStatus(status: DubStatus) {
|
||||
postEpisodeRange(currentIndex?.copy(dubStatus = status), currentRange)
|
||||
postEpisodeRange(currentIndex?.copy(dubStatus = status), currentRange, currentSorting)
|
||||
}
|
||||
|
||||
fun changeRange(range: EpisodeRange) {
|
||||
postEpisodeRange(currentIndex, range)
|
||||
postEpisodeRange(currentIndex, range, currentSorting)
|
||||
}
|
||||
|
||||
fun changeSeason(season: Int) {
|
||||
postEpisodeRange(currentIndex?.copy(season = season), currentRange)
|
||||
postEpisodeRange(currentIndex?.copy(season = season), currentRange, currentSorting)
|
||||
}
|
||||
|
||||
fun setSort(sortType: EpisodeSortType) {
|
||||
// we only update here as postEpisodeRange might change the sorting mode if it does not fit
|
||||
DataStoreHelper.resultsSortingMode = sortType
|
||||
postEpisodeRange(currentIndex, currentRange, sortType)
|
||||
}
|
||||
|
||||
private fun getMovie(): ResultEpisode? {
|
||||
@ -1824,26 +1863,37 @@ class ResultViewModel2 : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getEpisodes(indexer: EpisodeIndexer, range: EpisodeRange): List<ResultEpisode> {
|
||||
val startIndex = range.startIndex
|
||||
val length = range.length
|
||||
|
||||
return currentEpisodes[indexer]
|
||||
?.let { list ->
|
||||
val start = minOf(list.size, startIndex)
|
||||
val end = minOf(list.size, start + length)
|
||||
list.subList(start, end).map {
|
||||
val posDur = getViewPos(it.id)
|
||||
val watchState =
|
||||
getVideoWatchState(it.id) ?: VideoWatchState.None
|
||||
it.copy(
|
||||
position = posDur?.position ?: 0,
|
||||
duration = posDur?.duration ?: 0,
|
||||
videoWatchState = watchState
|
||||
)
|
||||
}
|
||||
private fun getEpisodes(
|
||||
indexer: EpisodeIndexer,
|
||||
range: EpisodeRange,
|
||||
): List<ResultEpisode> {
|
||||
return currentEpisodes[indexer]?.let { list ->
|
||||
val start = minOf(list.size, range.startIndex)
|
||||
val end = minOf(list.size, start + range.length)
|
||||
list.subList(start, end).map {
|
||||
val posDur = getViewPos(it.id)
|
||||
val watchState = getVideoWatchState(it.id) ?: VideoWatchState.None
|
||||
it.copy(
|
||||
position = posDur?.position ?: 0,
|
||||
duration = posDur?.duration ?: 0,
|
||||
videoWatchState = watchState
|
||||
)
|
||||
}
|
||||
?: emptyList()
|
||||
} ?: emptyList()
|
||||
}
|
||||
|
||||
private fun getSortedEpisodes(
|
||||
episodes: List<ResultEpisode>,
|
||||
sorting: EpisodeSortType
|
||||
): List<ResultEpisode> {
|
||||
return when (sorting) {
|
||||
EpisodeSortType.NUMBER_ASC -> episodes.sortedBy { it.episode }
|
||||
EpisodeSortType.NUMBER_DESC -> episodes.sortedByDescending { it.episode }
|
||||
EpisodeSortType.RATING_HIGH_LOW -> episodes.sortedByDescending { it.rating ?: 0 }
|
||||
EpisodeSortType.RATING_LOW_HIGH -> episodes.sortedBy { it.rating ?: 0 }
|
||||
EpisodeSortType.DATE_NEWEST -> episodes.sortedByDescending { it.airDate }
|
||||
EpisodeSortType.DATE_OLDEST -> episodes.sortedBy { it.airDate }
|
||||
}
|
||||
}
|
||||
|
||||
private fun postMovie() {
|
||||
@ -1882,9 +1932,11 @@ class ResultViewModel2 : ViewModel() {
|
||||
} else {
|
||||
_episodes.postValue(
|
||||
Resource.Success(
|
||||
getEpisodes(
|
||||
currentIndex ?: return,
|
||||
currentRange ?: return
|
||||
getSortedEpisodes(
|
||||
getEpisodes(
|
||||
currentIndex ?: return,
|
||||
currentRange ?: return,
|
||||
), currentSorting ?: return
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -1912,8 +1964,24 @@ class ResultViewModel2 : ViewModel() {
|
||||
_favoriteStatus.postValue(isFavorite)
|
||||
}
|
||||
|
||||
private fun postEpisodeRange(indexer: EpisodeIndexer?, range: EpisodeRange?) {
|
||||
if (range == null || indexer == null) {
|
||||
private fun shouldEnableSort(type: EpisodeSortType, episodes: List<ResultEpisode>?): Boolean {
|
||||
if (episodes.isNullOrEmpty()) return false
|
||||
return when (type) {
|
||||
EpisodeSortType.NUMBER_ASC, EpisodeSortType.NUMBER_DESC -> true
|
||||
EpisodeSortType.RATING_HIGH_LOW, EpisodeSortType.RATING_LOW_HIGH ->
|
||||
episodes.any { it.rating != null }
|
||||
|
||||
EpisodeSortType.DATE_NEWEST, EpisodeSortType.DATE_OLDEST ->
|
||||
episodes.any { it.airDate != null }
|
||||
}
|
||||
}
|
||||
|
||||
private fun postEpisodeRange(
|
||||
indexer: EpisodeIndexer?,
|
||||
range: EpisodeRange?,
|
||||
sorting: EpisodeSortType?
|
||||
) {
|
||||
if (range == null || indexer == null || sorting == null) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -1921,10 +1989,10 @@ class ResultViewModel2 : ViewModel() {
|
||||
|
||||
if (ranges?.contains(range) != true) {
|
||||
// if the current ranges does not include the range then select the range with the closest matching start episode
|
||||
// this usually happends when dub has less episodes then sub -> the range does not exist
|
||||
// this usually happens when dub has less episodes then sub -> the range does not exist
|
||||
ranges?.minByOrNull { kotlin.math.abs(it.startEpisode - range.startEpisode) }
|
||||
?.let { r ->
|
||||
postEpisodeRange(indexer, r)
|
||||
postEpisodeRange(indexer, r, sorting)
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -2027,16 +2095,64 @@ class ResultViewModel2 : ViewModel() {
|
||||
}
|
||||
|
||||
if (isMovie) {
|
||||
_sortSelections.postValue(emptyList())
|
||||
_selectedSortingIndex.postValue(-1)
|
||||
_selectedSorting.postValue(null)
|
||||
|
||||
postMovie()
|
||||
} else {
|
||||
val ret = getEpisodes(indexer, range)
|
||||
/*if (ret.isEmpty()) {
|
||||
val index = ranges?.indexOf(range)
|
||||
if(index != null && index > 0) {
|
||||
|
||||
if (ret.size <= 1) {
|
||||
// we cant sort on an empty list or a list with only 1 episode
|
||||
_sortSelections.postValue(emptyList())
|
||||
_selectedSortingIndex.postValue(-1)
|
||||
_selectedSorting.postValue(null)
|
||||
_episodes.postValue(Resource.Success(ret))
|
||||
} else {
|
||||
val sortOptions = mutableListOf<Pair<UiText, EpisodeSortType>>().apply {
|
||||
// Episode number sorting is always available
|
||||
add(txt(R.string.sort_episodes_number_asc) to EpisodeSortType.NUMBER_ASC)
|
||||
add(txt(R.string.sort_episodes_number_desc) to EpisodeSortType.NUMBER_DESC)
|
||||
|
||||
// Only add rating options if any episodes have ratings
|
||||
if (shouldEnableSort(EpisodeSortType.RATING_HIGH_LOW, ret)) {
|
||||
add(txt(R.string.sort_episodes_rating_high_low) to EpisodeSortType.RATING_HIGH_LOW)
|
||||
add(txt(R.string.sort_episodes_rating_low_high) to EpisodeSortType.RATING_LOW_HIGH)
|
||||
}
|
||||
|
||||
// Only add air date options if any episodes have air dates
|
||||
if (shouldEnableSort(EpisodeSortType.DATE_NEWEST, ret)) {
|
||||
add(txt(R.string.sort_episodes_date_newest) to EpisodeSortType.DATE_NEWEST)
|
||||
add(txt(R.string.sort_episodes_date_oldest) to EpisodeSortType.DATE_OLDEST)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
_episodes.postValue(Resource.Success(ret))
|
||||
|
||||
var sortIndex = sortOptions.indexOfFirst { it.second == sorting }
|
||||
|
||||
// correct the sorting order so if we have a selected that is not possible we just choose the default NUMBER_ASC
|
||||
val correctedSorting = if (sortIndex == -1) {
|
||||
sortIndex = 0
|
||||
EpisodeSortType.NUMBER_ASC
|
||||
} else {
|
||||
sorting
|
||||
}
|
||||
|
||||
currentSorting = correctedSorting
|
||||
_sortSelections.postValue(sortOptions)
|
||||
_selectedSortingIndex.postValue(sortIndex)
|
||||
_selectedSorting.postValue(
|
||||
when (correctedSorting) {
|
||||
EpisodeSortType.NUMBER_ASC -> txt(R.string.sort_button_episode, "↑")
|
||||
EpisodeSortType.NUMBER_DESC -> txt(R.string.sort_button_episode, "↓")
|
||||
EpisodeSortType.RATING_HIGH_LOW -> txt(R.string.sort_button_rating, "↓")
|
||||
EpisodeSortType.RATING_LOW_HIGH -> txt(R.string.sort_button_rating, "↑")
|
||||
EpisodeSortType.DATE_NEWEST -> txt(R.string.sort_button_date, "↓")
|
||||
EpisodeSortType.DATE_OLDEST -> txt(R.string.sort_button_date, "↑")
|
||||
}
|
||||
)
|
||||
_episodes.postValue(Resource.Success(getSortedEpisodes(ret, correctedSorting)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2303,7 +2419,7 @@ class ResultViewModel2 : ViewModel() {
|
||||
it.startEpisode >= (preferStartEpisode ?: 0)
|
||||
} ?: ranger?.lastOrNull()
|
||||
|
||||
postEpisodeRange(min, range)
|
||||
postEpisodeRange(min, range, DataStoreHelper.resultsSortingMode)
|
||||
postResume()
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager
|
||||
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
||||
import com.lagradost.cloudstream3.ui.WatchType
|
||||
import com.lagradost.cloudstream3.ui.library.ListSorting
|
||||
import com.lagradost.cloudstream3.ui.result.EpisodeSortType
|
||||
import com.lagradost.cloudstream3.ui.result.VideoWatchState
|
||||
import com.lagradost.cloudstream3.utils.AppContextUtils.filterProviderByPreferredMedia
|
||||
import java.util.Calendar
|
||||
@ -42,6 +43,7 @@ const val RESULT_RESUME_WATCHING_HAS_MIGRATED = "result_resume_watching_migrated
|
||||
const val RESULT_EPISODE = "result_episode"
|
||||
const val RESULT_SEASON = "result_season"
|
||||
const val RESULT_DUB = "result_dub"
|
||||
const val KEY_RESULT_SORT = "result_sort"
|
||||
|
||||
|
||||
class UserPreferenceDelegate<T : Any>(
|
||||
@ -119,6 +121,12 @@ object DataStoreHelper {
|
||||
var playBackSpeed : Float by UserPreferenceDelegate("playback_speed", 1.0f)
|
||||
var resizeMode : Int by UserPreferenceDelegate("resize_mode", 0)
|
||||
var librarySortingMode : Int by UserPreferenceDelegate("library_sorting_mode", ListSorting.AlphabeticalA.ordinal)
|
||||
private var _resultsSortingMode : Int by UserPreferenceDelegate("results_sorting_mode", EpisodeSortType.NUMBER_ASC.ordinal)
|
||||
var resultsSortingMode : EpisodeSortType
|
||||
get() = EpisodeSortType.entries.getOrNull(_resultsSortingMode) ?: EpisodeSortType.NUMBER_ASC
|
||||
set(value) {
|
||||
_resultsSortingMode = value.ordinal
|
||||
}
|
||||
|
||||
data class Account(
|
||||
@JsonProperty("keyIndex")
|
||||
|
@ -4,6 +4,7 @@ import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.marginEnd
|
||||
import com.lagradost.cloudstream3.R
|
||||
import kotlin.math.max
|
||||
@ -32,10 +33,12 @@ class FlowLayout : ViewGroup {
|
||||
val childCount = this.childCount
|
||||
for (i in 0 until childCount) {
|
||||
val child = getChildAt(i)
|
||||
if (!child.isVisible) {
|
||||
continue
|
||||
}
|
||||
measureChild(child, widthMeasureSpec, heightMeasureSpec)
|
||||
val childWidth = child.measuredWidth
|
||||
val childHeight = child.measuredHeight
|
||||
currentHeight = max(currentHeight, currentChildHookPointy + childHeight)
|
||||
|
||||
//check if child can be placed in the current row, else go to next line
|
||||
if (currentChildHookPointx + childWidth - child.marginEnd - child.paddingEnd > realWidth) {
|
||||
@ -44,8 +47,10 @@ class FlowLayout : ViewGroup {
|
||||
|
||||
//reset for new line
|
||||
currentChildHookPointx = 0
|
||||
currentChildHookPointy += childHeight
|
||||
currentChildHookPointy += childHeight + itemSpacing
|
||||
}
|
||||
|
||||
currentHeight = max(currentHeight, currentChildHookPointy + childHeight)
|
||||
val nextChildHookPointx =
|
||||
currentChildHookPointx + childWidth + if (childWidth == 0) 0 else itemSpacing
|
||||
|
||||
|
@ -360,14 +360,14 @@
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/result_meta_site"
|
||||
android:layout_gravity="center_vertical"
|
||||
style="@style/SmallBlackButton"
|
||||
android:layout_gravity="center_vertical"
|
||||
tools:text="Gogoanime" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/result_meta_content_rating"
|
||||
android:layout_gravity="center_vertical"
|
||||
style="@style/SmallBlackButton"
|
||||
android:layout_gravity="center_vertical"
|
||||
tools:text="PG-13" />
|
||||
|
||||
<TextView
|
||||
@ -409,8 +409,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="10"
|
||||
android:foreground="@drawable/outline_drawable"
|
||||
android:maxLines="10"
|
||||
android:nextFocusUp="@id/result_back"
|
||||
android:nextFocusDown="@id/result_bookmark_Button"
|
||||
android:paddingTop="5dp"
|
||||
@ -733,19 +733,20 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
<com.lagradost.cloudstream3.widget.FlowLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
android:orientation="horizontal"
|
||||
app:itemSpacing="10dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/result_season_button"
|
||||
style="@style/MultiSelectButton"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:drawableEnd="@drawable/ic_baseline_keyboard_arrow_down_24"
|
||||
android:nextFocusLeft="@id/result_episode_select"
|
||||
android:nextFocusRight="@id/result_episode_select"
|
||||
@ -761,8 +762,8 @@
|
||||
android:id="@+id/result_episode_select"
|
||||
style="@style/MultiSelectButton"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:drawableEnd="@drawable/ic_baseline_keyboard_arrow_down_24"
|
||||
android:nextFocusLeft="@id/result_season_button"
|
||||
android:nextFocusRight="@id/result_season_button"
|
||||
@ -778,8 +779,8 @@
|
||||
android:id="@+id/result_dub_select"
|
||||
style="@style/MultiSelectButton"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:drawableEnd="@drawable/ic_baseline_keyboard_arrow_down_24"
|
||||
android:nextFocusLeft="@id/result_season_button"
|
||||
android:nextFocusRight="@id/result_season_button"
|
||||
@ -788,7 +789,23 @@
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="5dp"
|
||||
android:visibility="gone"
|
||||
tools:text="Dubbed"
|
||||
tools:text="Dubbed1"
|
||||
tools:visibility="visible" />
|
||||
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/result_sort_button"
|
||||
style="@style/MultiSelectButton"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:nextFocusLeft="@id/result_dub_select"
|
||||
android:nextFocusUp="@id/result_description"
|
||||
android:nextFocusDown="@id/result_episodes"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="5dp"
|
||||
android:text="Sort"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
@ -796,13 +813,15 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:textColor="?attr/textColor"
|
||||
android:textSize="17sp"
|
||||
android:textStyle="normal"
|
||||
tools:text="8 Episodes" />
|
||||
</LinearLayout>
|
||||
</com.lagradost.cloudstream3.widget.FlowLayout>
|
||||
|
||||
|
||||
<!--TODO add next airing-->
|
||||
|
@ -714,6 +714,15 @@
|
||||
<string name="sort_updated_old">Updated (Old to New)</string>
|
||||
<string name="sort_alphabetical_a">Alphabetical (A to Z)</string>
|
||||
<string name="sort_alphabetical_z">Alphabetical (Z to A)</string>
|
||||
<string name="sort_episodes_number_asc">Episode (Ascending)</string>
|
||||
<string name="sort_episodes_number_desc">Episode (Descending)</string>
|
||||
<string name="sort_episodes_rating_high_low">Rating (Highest)</string>
|
||||
<string name="sort_episodes_rating_low_high">Rating (Lowest)</string>
|
||||
<string name="sort_episodes_date_newest">Air Date (Newest)</string>
|
||||
<string name="sort_episodes_date_oldest">Air Date (Oldest)</string>
|
||||
<string name="sort_button_episode">Ep %s</string>
|
||||
<string name="sort_button_rating">Rating %s</string>
|
||||
<string name="sort_button_date">Date %s</string>
|
||||
<string name="select_library">Select Library</string>
|
||||
<string name="open_with">Open with</string>
|
||||
<string name="empty_library_no_accounts_message">Your library is empty :(
|
||||
|
Reference in New Issue
Block a user