This commit is contained in:
LagradOst 2021-06-27 00:15:19 +02:00
parent 41d81a0c40
commit 2c312cd9b4
9 changed files with 155 additions and 39 deletions

View file

@ -354,4 +354,26 @@ object UIHelper {
popup.show() popup.show()
return popup return popup
} }
inline fun View.popupMenuNoIconsAndNoStringres(
items: List<Pair<Int, String>>,
noinline onMenuItemClick: MenuItem.() -> Unit,
): PopupMenu {
val ctw = ContextThemeWrapper(context, R.style.PopupMenu)
val popup = PopupMenu(ctw, this, Gravity.NO_GRAVITY, R.attr.actionOverflowMenuStyle, 0)
items.forEach { (id, stringRes) ->
popup.menu.add(0, id, 0, stringRes)
}
(popup.menu as? MenuBuilder)?.setOptionalIconsVisible(true)
popup.setOnMenuItemClickListener {
it.onMenuItemClick()
true
}
popup.show()
return popup
}
} }

View file

@ -873,7 +873,6 @@ class PlayerFragment : Fragment() {
viewModel = ViewModelProvider(requireActivity()).get(ResultViewModel::class.java) viewModel = ViewModelProvider(requireActivity()).get(ResultViewModel::class.java)
observeDirectly(viewModel.episodes) { _episodes -> observeDirectly(viewModel.episodes) { _episodes ->
episodes = _episodes episodes = _episodes
if (isLoading) { if (isLoading) {

View file

@ -89,7 +89,8 @@ class EpisodeAdapter(
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
fun bind(card: ResultEpisode) { fun bind(card: ResultEpisode) {
episodeText.text = card.name ?: "Episode ${card.episode}" val name = if (card.name == null) "Episode ${card.episode}" else "${card.episode}. ${card.name}"
episodeText.text = name
fun setWidth(v: View, procentage: Float) { fun setWidth(v: View, procentage: Float) {
val param = LinearLayout.LayoutParams( val param = LinearLayout.LayoutParams(
@ -125,7 +126,7 @@ class EpisodeAdapter(
} }
if (card.rating != null) { if (card.rating != null) {
episodeRating?.text = "%.1f".format(card.rating.toFloat() / 10f).replace(",", ".") episodeRating?.text = "Rated: %.1f".format(card.rating.toFloat() / 10f).replace(",", ".")
} else { } else {
episodeRating?.text = "" episodeRating?.text = ""
} }

View file

@ -5,8 +5,6 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.text.Spannable
import android.text.SpannableString
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -39,6 +37,7 @@ import com.lagradost.cloudstream3.UIHelper.getStatusBarHeight
import com.lagradost.cloudstream3.UIHelper.isCastApiAvailable import com.lagradost.cloudstream3.UIHelper.isCastApiAvailable
import com.lagradost.cloudstream3.UIHelper.popCurrentPage import com.lagradost.cloudstream3.UIHelper.popCurrentPage
import com.lagradost.cloudstream3.UIHelper.popupMenuNoIcons import com.lagradost.cloudstream3.UIHelper.popupMenuNoIcons
import com.lagradost.cloudstream3.UIHelper.popupMenuNoIconsAndNoStringres
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.WatchType import com.lagradost.cloudstream3.ui.WatchType
@ -191,6 +190,14 @@ class ResultFragment : Fragment() {
var url: String? = null var url: String? = null
private fun fromIndexToSeasonText(selection: Int?): String {
return when (selection) {
null -> "No Season"
-2 -> "No Season"
else -> "Season $selection"
}
}
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -358,7 +365,26 @@ class ResultFragment : Fragment() {
allEpisodes = it allEpisodes = it
} }
observe(viewModel.episodes) { episodes -> observe(viewModel.selectedSeason) { season ->
result_season_button?.text = fromIndexToSeasonText(season)
}
observe(viewModel.seasonSelections) { seasonList ->
result_season_button?.visibility = if (seasonList.size <= 1) GONE else VISIBLE
result_season_button?.setOnClickListener {
result_season_button?.popupMenuNoIconsAndNoStringres(
items = seasonList
.map { Pair(it ?: -2, fromIndexToSeasonText(it)) },
) {
val id = this.itemId
context?.let {
viewModel.changeSeason(it, if (id == -2) null else id)
}
}
}
}
observe(viewModel.publicEpisodes) { episodes ->
if (result_episodes == null || result_episodes.adapter == null) return@observe if (result_episodes == null || result_episodes.adapter == null) return@observe
result_episodes_text.text = "${episodes.size} Episode${if (episodes.size == 1) "" else "s"}" result_episodes_text.text = "${episodes.size} Episode${if (episodes.size == 1) "" else "s"}"
currentEpisodes = episodes currentEpisodes = episodes

View file

@ -1,17 +1,16 @@
package com.lagradost.cloudstream3.ui.result package com.lagradost.cloudstream3.ui.result
import android.content.Context import android.content.Context
import androidx.lifecycle.LiveData import androidx.lifecycle.*
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getApiFromName import com.lagradost.cloudstream3.APIHolder.getApiFromName
import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.safeApiCall import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultSeason
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultSeason
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultWatchState import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultWatchState
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -19,12 +18,17 @@ import kotlinx.coroutines.launch
class ResultViewModel : ViewModel() { class ResultViewModel : ViewModel() {
private val _resultResponse: MutableLiveData<Resource<Any?>> = MutableLiveData() private val _resultResponse: MutableLiveData<Resource<Any?>> = MutableLiveData()
private val _episodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData() private val _episodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData()
private val _publicEpisodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData()
val resultResponse: LiveData<Resource<Any?>> get() = _resultResponse val resultResponse: LiveData<Resource<Any?>> get() = _resultResponse
val episodes: LiveData<List<ResultEpisode>> get() = _episodes val episodes: LiveData<List<ResultEpisode>> get() = _episodes
val publicEpisodes: LiveData<List<ResultEpisode>> get() = _publicEpisodes
private val dubStatus: MutableLiveData<DubStatus> = MutableLiveData() private val dubStatus: MutableLiveData<DubStatus> = MutableLiveData()
private val page: MutableLiveData<LoadResponse> = MutableLiveData() private val page: MutableLiveData<LoadResponse> = MutableLiveData()
private val id: MutableLiveData<Int> = MutableLiveData() private val id: MutableLiveData<Int> = MutableLiveData()
val selectedSeason: MutableLiveData<Int> = MutableLiveData(-2)
val seasonSelections: MutableLiveData<List<Int?>> = MutableLiveData()
private val _watchStatus: MutableLiveData<WatchType> = MutableLiveData() private val _watchStatus: MutableLiveData<WatchType> = MutableLiveData()
val watchStatus: LiveData<WatchType> get() = _watchStatus val watchStatus: LiveData<WatchType> get() = _watchStatus
@ -41,13 +45,43 @@ class ResultViewModel : ViewModel() {
_watchStatus.postValue(currentWatch) _watchStatus.postValue(currentWatch)
} }
private fun filterEpisodes(context: Context, list: List<ResultEpisode>?, selection: Int?) {
if (list == null) return
val seasonTypes = HashMap<Int?, Boolean>()
for (i in list) {
if (!seasonTypes.containsKey(i.season)) {
seasonTypes[i.season] = true
}
}
val seasons = seasonTypes.toList().map { it.first }
seasonSelections.postValue(seasons)
val realSelection = if (!seasonTypes.containsKey(selection)) seasons[0] else selection
val internalId = id.value
if (internalId != null) context.setResultSeason(internalId, realSelection)
selectedSeason.postValue(realSelection ?: -2)
_publicEpisodes.postValue(list.filter { it.season == realSelection })
}
fun changeSeason(context: Context, selection: Int?) {
filterEpisodes(context, _episodes.value, selection)
}
private fun updateEpisodes(context: Context, localId: Int?, list: List<ResultEpisode>, selection: Int?) {
_episodes.postValue(list)
filterEpisodes(context,
list,
if (selection == -1) context.getResultSeason(localId ?: id.value ?: return) else selection)
}
fun reloadEpisodes(context: Context) { fun reloadEpisodes(context: Context) {
val current = _episodes.value ?: return val current = _episodes.value ?: return
val copy = current.map { val copy = current.map {
val posDur = context.getViewPos(it.id) val posDur = context.getViewPos(it.id)
it.copy(position = posDur?.position ?: 0, duration = posDur?.duration ?: 0) it.copy(position = posDur?.position ?: 0, duration = posDur?.duration ?: 0)
} }
_episodes.postValue(copy) updateEpisodes(context, null, copy, selectedSeason.value)
} }
// THIS SHOULD AT LEAST CLEAN IT UP, SO APIS CAN SWITCH DOMAIN // THIS SHOULD AT LEAST CLEAN IT UP, SO APIS CAN SWITCH DOMAIN
@ -98,7 +132,7 @@ class ResultViewModel : ViewModel() {
i.descript, i.descript,
)) ))
} }
_episodes.postValue(episodes) updateEpisodes(context, mainId, episodes, -1)
} }
} }
@ -106,8 +140,8 @@ class ResultViewModel : ViewModel() {
val episodes = ArrayList<ResultEpisode>() val episodes = ArrayList<ResultEpisode>()
for ((index, i) in d.episodes.withIndex()) { for ((index, i) in d.episodes.withIndex()) {
episodes.add(context.buildResultEpisode( episodes.add(context.buildResultEpisode(
(i.name i.name,
?: (if (i.season != null && i.episode != null) "S${i.season}:E${i.episode}" else null)), // TODO ADD NAMES //?: (if (i.season != null && i.episode != null) "S${i.season}:E${i.episode}" else null)), // TODO ADD NAMES
i.posterUrl, i.posterUrl,
i.episode ?: (index + 1), i.episode ?: (index + 1),
i.season, i.season,
@ -119,10 +153,10 @@ class ResultViewModel : ViewModel() {
i.descript i.descript
)) ))
} }
_episodes.postValue(episodes) updateEpisodes(context, mainId, episodes, -1)
} }
is MovieLoadResponse -> { is MovieLoadResponse -> {
_episodes.postValue(arrayListOf(context.buildResultEpisode( updateEpisodes(context, mainId, arrayListOf(context.buildResultEpisode(
null, null,
null, null,
0, null, 0, null,
@ -132,7 +166,7 @@ class ResultViewModel : ViewModel() {
0, 0,
null, null,
null, null,
))) )), -1)
} }
} }
} }

View file

@ -7,6 +7,7 @@ import com.lagradost.cloudstream3.utils.DataStore.setKey
const val VIDEO_POS_DUR = "video_pos_dur" const val VIDEO_POS_DUR = "video_pos_dur"
const val RESULT_WATCH_STATE = "result_watch_state" const val RESULT_WATCH_STATE = "result_watch_state"
const val RESULT_SEASON = "result_season"
data class PosDur(val position: Long, val duration: Long) data class PosDur(val position: Long, val duration: Long)
@ -30,4 +31,11 @@ object DataStoreHelper {
fun Context.getResultWatchState(id: Int): WatchType { fun Context.getResultWatchState(id: Int): WatchType {
return WatchType.fromInternalId(getKey<Int>("$currentAccount/$RESULT_WATCH_STATE", id.toString(), null)) return WatchType.fromInternalId(getKey<Int>("$currentAccount/$RESULT_WATCH_STATE", id.toString(), null))
} }
fun Context.getResultSeason(id: Int): Int {
return getKey("$currentAccount/$RESULT_SEASON", id.toString(), -1)!!
}
fun Context.setResultSeason(id: Int, value : Int?) {
return setKey("$currentAccount/$RESULT_SEASON", id.toString(), value)
}
} }

View file

@ -183,15 +183,15 @@
android:id="@+id/result_bookmark_button" android:id="@+id/result_bookmark_button"
tools:text="Bookmark" tools:text="Bookmark"
app:rippleColor="?attr/colorPrimary" app:rippleColor="?attr/bitDarkerGrayBackground"
android:textColor="?attr/textColor" android:textColor="?attr/textColor"
app:iconTint="?attr/textColor" app:iconTint="?attr/textColor"
android:textAllCaps="false" android:textAllCaps="false"
app:icon="@drawable/ic_outline_remove_red_eye_24" app:icon="@drawable/ic_outline_remove_red_eye_24"
android:backgroundTint="@color/transparent" android:backgroundTint="@color/itemBackground"
style="@style/Widget.MaterialComponents.Button.OutlinedButton" style="@style/Widget.MaterialComponents.Button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="45dp"> android:layout_height="50dp">
</com.google.android.material.button.MaterialButton> </com.google.android.material.button.MaterialButton>
@ -294,6 +294,25 @@
android:layout_height="45dp"> android:layout_height="45dp">
</com.google.android.material.button.MaterialButton> </com.google.android.material.button.MaterialButton>
<LinearLayout android:orientation="horizontal" android:gravity="center_vertical" android:layout_width="match_parent" android:layout_height="wrap_content">
<com.google.android.material.button.MaterialButton
android:visibility="visible"
android:layout_gravity="center_vertical"
app:cornerRadius="4dp"
android:id="@+id/result_season_button"
tools:text="Bookmark"
app:rippleColor="?attr/bitDarkerGrayBackground"
android:textColor="?attr/textColor"
app:iconTint="?attr/textColor"
android:textAllCaps="false"
android:backgroundTint="@color/itemBackground"
style="@style/Widget.MaterialComponents.Button"
android:layout_width="wrap_content"
android:layout_marginEnd="10dp"
android:layout_height="50dp">
</com.google.android.material.button.MaterialButton>
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -305,8 +324,10 @@
android:textStyle="normal" android:textStyle="normal"
android:textColor="?attr/textColor" android:textColor="?attr/textColor"
/> />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:layout_marginTop="10dp" android:layout_marginTop="0dp"
android:paddingBottom="100dp" android:paddingBottom="100dp"
tools:listitem="@layout/result_episode" tools:listitem="@layout/result_episode"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -9,7 +9,7 @@
app:cardBackgroundColor="@color/itemBackground" app:cardBackgroundColor="@color/itemBackground"
android:id="@+id/episode_holder" android:id="@+id/episode_holder"
android:foreground="?android:attr/selectableItemBackgroundBorderless" android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:layout_marginBottom="5dp" android:layout_marginBottom="10dp"
> >
<LinearLayout <LinearLayout
android:padding="10dp" android:padding="10dp"
@ -20,8 +20,9 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:orientation="horizontal" android:orientation="horizontal"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<!--app:cardCornerRadius="@dimen/roundedImageRadius"-->
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
app:cardCornerRadius="@dimen/roundedImageRadius"
android:layout_width="126dp" android:layout_width="126dp"
android:layout_height="72dp" android:layout_height="72dp"
> >
@ -41,7 +42,10 @@
android:contentDescription="@string/play_episode"> android:contentDescription="@string/play_episode">
</ImageView> </ImageView>
<androidx.core.widget.ContentLoadingProgressBar <androidx.core.widget.ContentLoadingProgressBar
android:layout_marginBottom="-1.5dp"
android:id="@+id/episode_progress" android:id="@+id/episode_progress"
android:progressTint="@color/colorPrimary"
android:progressBackgroundTint="@color/colorPrimary"
style="@android:style/Widget.Material.ProgressBar.Horizontal" style="@android:style/Widget.Material.ProgressBar.Horizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
tools:progress="50" tools:progress="50"
@ -58,13 +62,14 @@
<TextView <TextView
android:id="@+id/episode_text" android:id="@+id/episode_text"
tools:text="1. Jobless" tools:text="1. Jobless"
android:textStyle="bold"
android:textColor="@color/textColor" android:textColor="@color/textColor"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
</TextView> </TextView>
<TextView <TextView
android:id="@+id/episode_rating" android:id="@+id/episode_rating"
tools:text="8.8" tools:text="Rated: 8.8"
android:textColor="@color/grayTextColor" android:textColor="@color/grayTextColor"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content">

View file

@ -10,9 +10,9 @@
<color name="colorAccent">#3b65f5</color> <!-- 818fff--> <color name="colorAccent">#3b65f5</color> <!-- 818fff-->
<color name="darkBackground">#2B2C30</color> <!--0f0f10 0E0E10 303135 2B2C30--> <color name="darkBackground">#2B2C30</color> <!--0f0f10 0E0E10 303135 2B2C30-->
<color name="bitDarkerGrayBackground">#1C1C20</color> <!--191a1f 19181E 202125 1C1C20--> <color name="bitDarkerGrayBackground">#111111</color> <!--1C1C20 191a1f 19181E 202125 1C1C20-->
<color name="grayBackground">#1C1C20</color> <!--141419 202125--> <color name="grayBackground">#1C1C20</color> <!--141419 202125-->
<color name="itemBackground">#17171B</color> <!-- 17171B 1B1B20--> <color name="itemBackground">#161616</color> <!-- 17171B 1B1B20-->
<color name="textColor">#e9eaee</color> <!--FFF--> <color name="textColor">#e9eaee</color> <!--FFF-->
<color name="grayTextColor">#9ba0a4</color> <!-- 5e5f62--> <color name="grayTextColor">#9ba0a4</color> <!-- 5e5f62-->