mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
ui stuff
This commit is contained in:
parent
41d81a0c40
commit
2c312cd9b4
9 changed files with 155 additions and 39 deletions
|
@ -354,4 +354,26 @@ object UIHelper {
|
|||
popup.show()
|
||||
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
|
||||
}
|
||||
}
|
|
@ -873,7 +873,6 @@ class PlayerFragment : Fragment() {
|
|||
|
||||
viewModel = ViewModelProvider(requireActivity()).get(ResultViewModel::class.java)
|
||||
|
||||
|
||||
observeDirectly(viewModel.episodes) { _episodes ->
|
||||
episodes = _episodes
|
||||
if (isLoading) {
|
||||
|
|
|
@ -89,7 +89,8 @@ class EpisodeAdapter(
|
|||
|
||||
@SuppressLint("SetTextI18n")
|
||||
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) {
|
||||
val param = LinearLayout.LayoutParams(
|
||||
|
@ -125,7 +126,7 @@ class EpisodeAdapter(
|
|||
}
|
||||
|
||||
if (card.rating != null) {
|
||||
episodeRating?.text = "%.1f".format(card.rating.toFloat() / 10f).replace(",", ".")
|
||||
episodeRating?.text = "Rated: %.1f".format(card.rating.toFloat() / 10f).replace(",", ".")
|
||||
} else {
|
||||
episodeRating?.text = ""
|
||||
}
|
||||
|
|
|
@ -5,8 +5,6 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.view.LayoutInflater
|
||||
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.popCurrentPage
|
||||
import com.lagradost.cloudstream3.UIHelper.popupMenuNoIcons
|
||||
import com.lagradost.cloudstream3.UIHelper.popupMenuNoIconsAndNoStringres
|
||||
import com.lagradost.cloudstream3.mvvm.Resource
|
||||
import com.lagradost.cloudstream3.mvvm.observe
|
||||
import com.lagradost.cloudstream3.ui.WatchType
|
||||
|
@ -191,6 +190,14 @@ class ResultFragment : Fragment() {
|
|||
|
||||
var url: String? = null
|
||||
|
||||
private fun fromIndexToSeasonText(selection: Int?): String {
|
||||
return when (selection) {
|
||||
null -> "No Season"
|
||||
-2 -> "No Season"
|
||||
else -> "Season $selection"
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
@ -358,7 +365,26 @@ class ResultFragment : Fragment() {
|
|||
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
|
||||
result_episodes_text.text = "${episodes.size} Episode${if (episodes.size == 1) "" else "s"}"
|
||||
currentEpisodes = episodes
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
package com.lagradost.cloudstream3.ui.result
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.*
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiFromName
|
||||
import com.lagradost.cloudstream3.mvvm.Resource
|
||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||
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.getViewPos
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultSeason
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultWatchState
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -19,12 +18,17 @@ import kotlinx.coroutines.launch
|
|||
class ResultViewModel : ViewModel() {
|
||||
private val _resultResponse: MutableLiveData<Resource<Any?>> = MutableLiveData()
|
||||
private val _episodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData()
|
||||
private val _publicEpisodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData()
|
||||
val resultResponse: LiveData<Resource<Any?>> get() = _resultResponse
|
||||
val episodes: LiveData<List<ResultEpisode>> get() = _episodes
|
||||
val publicEpisodes: LiveData<List<ResultEpisode>> get() = _publicEpisodes
|
||||
|
||||
private val dubStatus: MutableLiveData<DubStatus> = MutableLiveData()
|
||||
|
||||
private val page: MutableLiveData<LoadResponse> = 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()
|
||||
val watchStatus: LiveData<WatchType> get() = _watchStatus
|
||||
|
@ -41,13 +45,43 @@ class ResultViewModel : ViewModel() {
|
|||
_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) {
|
||||
val current = _episodes.value ?: return
|
||||
val copy = current.map {
|
||||
val posDur = context.getViewPos(it.id)
|
||||
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
|
||||
|
@ -98,7 +132,7 @@ class ResultViewModel : ViewModel() {
|
|||
i.descript,
|
||||
))
|
||||
}
|
||||
_episodes.postValue(episodes)
|
||||
updateEpisodes(context, mainId, episodes, -1)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,8 +140,8 @@ class ResultViewModel : ViewModel() {
|
|||
val episodes = ArrayList<ResultEpisode>()
|
||||
for ((index, i) in d.episodes.withIndex()) {
|
||||
episodes.add(context.buildResultEpisode(
|
||||
(i.name
|
||||
?: (if (i.season != null && i.episode != null) "S${i.season}:E${i.episode}" else null)), // TODO ADD NAMES
|
||||
i.name,
|
||||
//?: (if (i.season != null && i.episode != null) "S${i.season}:E${i.episode}" else null)), // TODO ADD NAMES
|
||||
i.posterUrl,
|
||||
i.episode ?: (index + 1),
|
||||
i.season,
|
||||
|
@ -119,10 +153,10 @@ class ResultViewModel : ViewModel() {
|
|||
i.descript
|
||||
))
|
||||
}
|
||||
_episodes.postValue(episodes)
|
||||
updateEpisodes(context, mainId, episodes, -1)
|
||||
}
|
||||
is MovieLoadResponse -> {
|
||||
_episodes.postValue(arrayListOf(context.buildResultEpisode(
|
||||
updateEpisodes(context, mainId, arrayListOf(context.buildResultEpisode(
|
||||
null,
|
||||
null,
|
||||
0, null,
|
||||
|
@ -132,7 +166,7 @@ class ResultViewModel : ViewModel() {
|
|||
0,
|
||||
null,
|
||||
null,
|
||||
)))
|
||||
)), -1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import com.lagradost.cloudstream3.utils.DataStore.setKey
|
|||
|
||||
const val VIDEO_POS_DUR = "video_pos_dur"
|
||||
const val RESULT_WATCH_STATE = "result_watch_state"
|
||||
const val RESULT_SEASON = "result_season"
|
||||
|
||||
data class PosDur(val position: Long, val duration: Long)
|
||||
|
||||
|
@ -30,4 +31,11 @@ object DataStoreHelper {
|
|||
fun Context.getResultWatchState(id: Int): WatchType {
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -183,15 +183,15 @@
|
|||
android:id="@+id/result_bookmark_button"
|
||||
tools:text="Bookmark"
|
||||
|
||||
app:rippleColor="?attr/colorPrimary"
|
||||
app:rippleColor="?attr/bitDarkerGrayBackground"
|
||||
android:textColor="?attr/textColor"
|
||||
app:iconTint="?attr/textColor"
|
||||
android:textAllCaps="false"
|
||||
app:icon="@drawable/ic_outline_remove_red_eye_24"
|
||||
android:backgroundTint="@color/transparent"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:backgroundTint="@color/itemBackground"
|
||||
style="@style/Widget.MaterialComponents.Button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="45dp">
|
||||
android:layout_height="50dp">
|
||||
</com.google.android.material.button.MaterialButton>
|
||||
|
||||
|
||||
|
@ -294,6 +294,25 @@
|
|||
android:layout_height="45dp">
|
||||
</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
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -305,8 +324,10 @@
|
|||
android:textStyle="normal"
|
||||
android:textColor="?attr/textColor"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:paddingBottom="100dp"
|
||||
tools:listitem="@layout/result_episode"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
app:cardBackgroundColor="@color/itemBackground"
|
||||
android:id="@+id/episode_holder"
|
||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
>
|
||||
<LinearLayout
|
||||
android:padding="10dp"
|
||||
|
@ -20,8 +20,9 @@
|
|||
android:layout_width="match_parent"
|
||||
android:orientation="horizontal"
|
||||
android:layout_height="wrap_content">
|
||||
<!--app:cardCornerRadius="@dimen/roundedImageRadius"-->
|
||||
<androidx.cardview.widget.CardView
|
||||
app:cardCornerRadius="@dimen/roundedImageRadius"
|
||||
|
||||
android:layout_width="126dp"
|
||||
android:layout_height="72dp"
|
||||
>
|
||||
|
@ -41,7 +42,10 @@
|
|||
android:contentDescription="@string/play_episode">
|
||||
</ImageView>
|
||||
<androidx.core.widget.ContentLoadingProgressBar
|
||||
android:layout_marginBottom="-1.5dp"
|
||||
android:id="@+id/episode_progress"
|
||||
android:progressTint="@color/colorPrimary"
|
||||
android:progressBackgroundTint="@color/colorPrimary"
|
||||
style="@android:style/Widget.Material.ProgressBar.Horizontal"
|
||||
android:layout_width="match_parent"
|
||||
tools:progress="50"
|
||||
|
@ -58,13 +62,14 @@
|
|||
<TextView
|
||||
android:id="@+id/episode_text"
|
||||
tools:text="1. Jobless"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/textColor"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
</TextView>
|
||||
<TextView
|
||||
android:id="@+id/episode_rating"
|
||||
tools:text="8.8"
|
||||
tools:text="Rated: 8.8"
|
||||
android:textColor="@color/grayTextColor"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
<color name="colorAccent">#3b65f5</color> <!-- 818fff-->
|
||||
|
||||
<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="itemBackground">#17171B</color> <!-- 17171B 1B1B20-->
|
||||
<color name="itemBackground">#161616</color> <!-- 17171B 1B1B20-->
|
||||
|
||||
<color name="textColor">#e9eaee</color> <!--FFF-->
|
||||
<color name="grayTextColor">#9ba0a4</color> <!-- 5e5f62-->
|
||||
|
|
Loading…
Reference in a new issue