mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
viewmodel dialogs
This commit is contained in:
parent
64ea5e2f4b
commit
a99713fe0c
17 changed files with 655 additions and 192 deletions
|
@ -1114,6 +1114,7 @@ data class NextAiring(
|
||||||
data class SeasonData(
|
data class SeasonData(
|
||||||
val season: Int,
|
val season: Int,
|
||||||
val name: String? = null,
|
val name: String? = null,
|
||||||
|
val displaySeason : Int? = null, // will use season if null
|
||||||
)
|
)
|
||||||
|
|
||||||
interface EpisodeResponse {
|
interface EpisodeResponse {
|
||||||
|
|
|
@ -332,6 +332,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
if (str.contains(appString)) {
|
if (str.contains(appString)) {
|
||||||
for (api in OAuth2Apis) {
|
for (api in OAuth2Apis) {
|
||||||
if (str.contains("/${api.redirectUrl}")) {
|
if (str.contains("/${api.redirectUrl}")) {
|
||||||
|
val activity = this
|
||||||
ioSafe {
|
ioSafe {
|
||||||
Log.i(TAG, "handleAppIntent $str")
|
Log.i(TAG, "handleAppIntent $str")
|
||||||
val isSuccessful = api.handleRedirect(str)
|
val isSuccessful = api.handleRedirect(str)
|
||||||
|
@ -342,10 +343,10 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
Log.i(TAG, "failed to authenticate ${api.name}")
|
Log.i(TAG, "failed to authenticate ${api.name}")
|
||||||
}
|
}
|
||||||
|
|
||||||
this.runOnUiThread {
|
activity.runOnUiThread {
|
||||||
try {
|
try {
|
||||||
showToast(
|
showToast(
|
||||||
this,
|
activity,
|
||||||
getString(if (isSuccessful) R.string.authenticated_user else R.string.authenticated_user_fail).format(
|
getString(if (isSuccessful) R.string.authenticated_user else R.string.authenticated_user_fail).format(
|
||||||
api.name
|
api.name
|
||||||
)
|
)
|
||||||
|
|
|
@ -54,7 +54,7 @@ class GogoanimeProvider : MainAPI() {
|
||||||
secretKeyString: String,
|
secretKeyString: String,
|
||||||
encrypt: Boolean = true
|
encrypt: Boolean = true
|
||||||
): String {
|
): String {
|
||||||
println("IV: $iv, Key: $secretKeyString, encrypt: $encrypt, Message: $string")
|
//println("IV: $iv, Key: $secretKeyString, encrypt: $encrypt, Message: $string")
|
||||||
val ivParameterSpec = IvParameterSpec(iv.toByteArray())
|
val ivParameterSpec = IvParameterSpec(iv.toByteArray())
|
||||||
val secretKey = SecretKeySpec(secretKeyString.toByteArray(), "AES")
|
val secretKey = SecretKeySpec(secretKeyString.toByteArray(), "AES")
|
||||||
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||||
|
|
|
@ -51,6 +51,32 @@ fun <T> LifecycleOwner.observeDirectly(liveData: LiveData<T>, action: (t: T) ->
|
||||||
action(currentValue)
|
action(currentValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : Any> some(value: T?): Some<T> {
|
||||||
|
return if (value == null) {
|
||||||
|
Some.None
|
||||||
|
} else {
|
||||||
|
Some.Success(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class Some<out T> {
|
||||||
|
data class Success<out T>(val value: T) : Some<T>()
|
||||||
|
object None : Some<Nothing>()
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return when(this) {
|
||||||
|
is None -> "None"
|
||||||
|
is Success -> "Some(${value.toString()})"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class ResourceSome<out T> {
|
||||||
|
data class Success<out T>(val value: T) : ResourceSome<T>()
|
||||||
|
object None : ResourceSome<Nothing>()
|
||||||
|
data class Loading(val data: Any? = null) : ResourceSome<Nothing>()
|
||||||
|
}
|
||||||
|
|
||||||
sealed class Resource<out T> {
|
sealed class Resource<out T> {
|
||||||
data class Success<out T>(val value: T) : Resource<T>()
|
data class Success<out T>(val value: T) : Resource<T>()
|
||||||
data class Failure(
|
data class Failure(
|
||||||
|
|
|
@ -125,6 +125,7 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
|
|
||||||
private fun loadExtractorJob(extractorLink: ExtractorLink?) {
|
private fun loadExtractorJob(extractorLink: ExtractorLink?) {
|
||||||
currentVerifyLink?.cancel()
|
currentVerifyLink?.cancel()
|
||||||
|
|
||||||
extractorLink?.let {
|
extractorLink?.let {
|
||||||
currentVerifyLink = ioSafe {
|
currentVerifyLink = ioSafe {
|
||||||
if (it.extractorData != null) {
|
if (it.extractorData != null) {
|
||||||
|
@ -488,7 +489,9 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
.setView(R.layout.player_select_source_and_subs)
|
.setView(R.layout.player_select_source_and_subs)
|
||||||
|
|
||||||
val sourceDialog = sourceBuilder.create()
|
val sourceDialog = sourceBuilder.create()
|
||||||
|
|
||||||
selectSourceDialog = sourceDialog
|
selectSourceDialog = sourceDialog
|
||||||
|
|
||||||
sourceDialog.show()
|
sourceDialog.show()
|
||||||
val providerList = sourceDialog.sort_providers
|
val providerList = sourceDialog.sort_providers
|
||||||
val subtitleList = sourceDialog.sort_subtitles
|
val subtitleList = sourceDialog.sort_subtitles
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.lagradost.cloudstream3.ui.result
|
package com.lagradost.cloudstream3.ui.result
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.Dialog
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.Intent.*
|
import android.content.Intent.*
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
|
@ -15,6 +16,7 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.AbsListView
|
import android.widget.AbsListView
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.ImageView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
|
@ -28,15 +30,13 @@ import com.discord.panels.PanelsChildGestureRegionObserver
|
||||||
import com.google.android.gms.cast.framework.CastButtonFactory
|
import com.google.android.gms.cast.framework.CastButtonFactory
|
||||||
import com.google.android.gms.cast.framework.CastContext
|
import com.google.android.gms.cast.framework.CastContext
|
||||||
import com.google.android.gms.cast.framework.CastState
|
import com.google.android.gms.cast.framework.CastState
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiFromName
|
import com.lagradost.cloudstream3.APIHolder.getApiFromName
|
||||||
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
|
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
|
||||||
import com.lagradost.cloudstream3.CommonActivity.showToast
|
import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||||
import com.lagradost.cloudstream3.mvvm.Resource
|
import com.lagradost.cloudstream3.mvvm.*
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
|
||||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
|
||||||
import com.lagradost.cloudstream3.mvvm.observe
|
|
||||||
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
|
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
|
||||||
import com.lagradost.cloudstream3.ui.WatchType
|
import com.lagradost.cloudstream3.ui.WatchType
|
||||||
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
|
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
|
||||||
|
@ -54,8 +54,10 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
||||||
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper
|
import com.lagradost.cloudstream3.utils.UIHelper
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
||||||
|
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
|
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
|
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
|
||||||
|
@ -68,6 +70,7 @@ import kotlinx.android.synthetic.main.fragment_trailer.*
|
||||||
import kotlinx.android.synthetic.main.result_recommendations.*
|
import kotlinx.android.synthetic.main.result_recommendations.*
|
||||||
import kotlinx.android.synthetic.main.result_sync.*
|
import kotlinx.android.synthetic.main.result_sync.*
|
||||||
import kotlinx.android.synthetic.main.trailer_custom_layout.*
|
import kotlinx.android.synthetic.main.trailer_custom_layout.*
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
const val START_ACTION_NORMAL = 0
|
const val START_ACTION_NORMAL = 0
|
||||||
const val START_ACTION_RESUME_LATEST = 1
|
const val START_ACTION_RESUME_LATEST = 1
|
||||||
|
@ -206,8 +209,6 @@ class ResultFragment : ResultTrailerPlayer() {
|
||||||
private var updateUIListener: (() -> Unit)? = null
|
private var updateUIListener: (() -> Unit)? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private var currentLoadingCount =
|
|
||||||
0 // THIS IS USED TO PREVENT LATE EVENTS, AFTER DISMISS WAS CLICKED
|
|
||||||
private lateinit var viewModel: ResultViewModel2 //by activityViewModels()
|
private lateinit var viewModel: ResultViewModel2 //by activityViewModels()
|
||||||
private lateinit var syncModel: SyncViewModel
|
private lateinit var syncModel: SyncViewModel
|
||||||
|
|
||||||
|
@ -418,7 +419,8 @@ class ResultFragment : ResultTrailerPlayer() {
|
||||||
viewModel.reloadEpisodes()
|
viewModel.reloadEpisodes()
|
||||||
}
|
}
|
||||||
|
|
||||||
var apiName: String = ""
|
var loadingDialog: Dialog? = null
|
||||||
|
var popupDialog: Dialog? = null
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
@ -466,7 +468,7 @@ class ResultFragment : ResultTrailerPlayer() {
|
||||||
// activity?.fixPaddingStatusbar(result_toolbar)
|
// activity?.fixPaddingStatusbar(result_toolbar)
|
||||||
|
|
||||||
val url = arguments?.getString(URL_BUNDLE)
|
val url = arguments?.getString(URL_BUNDLE)
|
||||||
apiName = arguments?.getString(API_NAME_BUNDLE) ?: return
|
val apiName = arguments?.getString(API_NAME_BUNDLE) ?: return
|
||||||
startAction = arguments?.getInt(START_ACTION_BUNDLE) ?: START_ACTION_NORMAL
|
startAction = arguments?.getInt(START_ACTION_BUNDLE) ?: START_ACTION_NORMAL
|
||||||
startValue = arguments?.getInt(START_VALUE_BUNDLE)
|
startValue = arguments?.getInt(START_VALUE_BUNDLE)
|
||||||
val resumeEpisode = arguments?.getInt(EPISODE_BUNDLE)
|
val resumeEpisode = arguments?.getInt(EPISODE_BUNDLE)
|
||||||
|
@ -862,16 +864,16 @@ class ResultFragment : ResultTrailerPlayer() {
|
||||||
*/
|
*/
|
||||||
observe(viewModel.episodes) { episodes ->
|
observe(viewModel.episodes) { episodes ->
|
||||||
when (episodes) {
|
when (episodes) {
|
||||||
is Resource.Failure -> {
|
is ResourceSome.None -> {
|
||||||
result_episode_loading?.isVisible = false
|
result_episode_loading?.isVisible = false
|
||||||
//result_episodes?.isVisible = false
|
result_episodes?.isVisible = false
|
||||||
}
|
}
|
||||||
is Resource.Loading -> {
|
is ResourceSome.Loading -> {
|
||||||
result_episode_loading?.isVisible = true
|
result_episode_loading?.isVisible = true
|
||||||
// result_episodes?.isVisible = false
|
result_episodes?.isVisible = false
|
||||||
}
|
}
|
||||||
is Resource.Success -> {
|
is ResourceSome.Success -> {
|
||||||
//result_episodes?.isVisible = true
|
result_episodes?.isVisible = true
|
||||||
result_episode_loading?.isVisible = false
|
result_episode_loading?.isVisible = false
|
||||||
(result_episodes?.adapter as? EpisodeAdapter?)?.updateList(episodes.value)
|
(result_episodes?.adapter as? EpisodeAdapter?)?.updateList(episodes.value)
|
||||||
}
|
}
|
||||||
|
@ -879,7 +881,7 @@ class ResultFragment : ResultTrailerPlayer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
observe(viewModel.selectedSeason) { text ->
|
observe(viewModel.selectedSeason) { text ->
|
||||||
result_season_button?.setText(text)
|
result_season_button.setText(text)
|
||||||
|
|
||||||
// If the season button is visible the result season button will be next focus down
|
// If the season button is visible the result season button will be next focus down
|
||||||
if (result_season_button?.isVisible == true)
|
if (result_season_button?.isVisible == true)
|
||||||
|
@ -901,6 +903,70 @@ class ResultFragment : ResultTrailerPlayer() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
observe(viewModel.selectPopup) { popup ->
|
||||||
|
println("POPUPSTATUS:$popup")
|
||||||
|
when (popup) {
|
||||||
|
is Some.Success -> {
|
||||||
|
popupDialog?.dismissSafe(activity)
|
||||||
|
|
||||||
|
popupDialog = activity?.let { act ->
|
||||||
|
val pop = popup.value
|
||||||
|
val options = pop.getOptions(act)
|
||||||
|
val title = pop.getTitle(act)
|
||||||
|
|
||||||
|
act.showBottomDialogInstant(
|
||||||
|
options, title, {
|
||||||
|
popupDialog = null
|
||||||
|
pop.callback(context ?: return@showBottomDialogInstant, null)
|
||||||
|
}, {
|
||||||
|
popupDialog = null
|
||||||
|
pop.callback(context ?: return@showBottomDialogInstant, it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Some.None -> {
|
||||||
|
popupDialog?.dismissSafe(activity)
|
||||||
|
popupDialog = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//showBottomDialogInstant
|
||||||
|
}
|
||||||
|
|
||||||
|
observe(viewModel.loadedLinks) { load ->
|
||||||
|
|
||||||
|
when (load) {
|
||||||
|
is Some.Success -> {
|
||||||
|
if(loadingDialog?.isShowing != true) {
|
||||||
|
loadingDialog?.dismissSafe(activity)
|
||||||
|
loadingDialog = null
|
||||||
|
}
|
||||||
|
loadingDialog = loadingDialog ?: context?.let { ctx ->
|
||||||
|
val builder =
|
||||||
|
BottomSheetDialog(ctx)
|
||||||
|
builder.setContentView(R.layout.bottom_loading)
|
||||||
|
builder.setOnDismissListener {
|
||||||
|
loadingDialog = null
|
||||||
|
viewModel.cancelLinks()
|
||||||
|
}
|
||||||
|
//builder.setOnCancelListener {
|
||||||
|
// it?.dismiss()
|
||||||
|
//}
|
||||||
|
builder.setCanceledOnTouchOutside(true)
|
||||||
|
|
||||||
|
builder.show()
|
||||||
|
|
||||||
|
builder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Some.None -> {
|
||||||
|
loadingDialog?.dismissSafe(activity)
|
||||||
|
loadingDialog = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
observe(viewModel.selectedRange) { range ->
|
observe(viewModel.selectedRange) { range ->
|
||||||
result_episode_select.setText(range)
|
result_episode_select.setText(range)
|
||||||
|
|
||||||
|
@ -933,7 +999,8 @@ class ResultFragment : ResultTrailerPlayer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
observe(viewModel.rangeSelections) { range ->
|
observe(viewModel.rangeSelections) { range ->
|
||||||
result_episode_select.setOnClickListener { view ->
|
println("RANGE:$range")
|
||||||
|
result_episode_select?.setOnClickListener { view ->
|
||||||
view?.context?.let { ctx ->
|
view?.context?.let { ctx ->
|
||||||
val names = range
|
val names = range
|
||||||
.mapNotNull { (text, r) ->
|
.mapNotNull { (text, r) ->
|
||||||
|
@ -987,6 +1054,33 @@ class ResultFragment : ResultTrailerPlayer() {
|
||||||
setRecommendations(recommendations, null)
|
setRecommendations(recommendations, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
observe(viewModel.movie) { data ->
|
||||||
|
when (data) {
|
||||||
|
is ResourceSome.Success -> {
|
||||||
|
data.value.let { (text, ep) ->
|
||||||
|
result_play_movie.setText(text)
|
||||||
|
result_play_movie?.setOnClickListener {
|
||||||
|
viewModel.handleAction(
|
||||||
|
activity,
|
||||||
|
EpisodeClickEvent(ACTION_CLICK_DEFAULT, ep)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
result_play_movie?.setOnLongClickListener {
|
||||||
|
viewModel.handleAction(
|
||||||
|
activity,
|
||||||
|
EpisodeClickEvent(ACTION_SHOW_OPTIONS, ep)
|
||||||
|
)
|
||||||
|
return@setOnLongClickListener true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
result_play_movie?.isVisible = false
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
observe(viewModel.page) { data ->
|
observe(viewModel.page) { data ->
|
||||||
when (data) {
|
when (data) {
|
||||||
is Resource.Success -> {
|
is Resource.Success -> {
|
||||||
|
@ -1006,9 +1100,32 @@ class ResultFragment : ResultTrailerPlayer() {
|
||||||
result_cast_text.setText(d.actorsText)
|
result_cast_text.setText(d.actorsText)
|
||||||
result_next_airing.setText(d.nextAiringEpisode)
|
result_next_airing.setText(d.nextAiringEpisode)
|
||||||
result_next_airing_time.setText(d.nextAiringDate)
|
result_next_airing_time.setText(d.nextAiringDate)
|
||||||
|
|
||||||
result_poster.setImage(d.posterImage)
|
result_poster.setImage(d.posterImage)
|
||||||
result_play_movie.setText(d.playMovieText)
|
|
||||||
|
if (d.posterImage != null && context?.isTrueTvSettings() == false)
|
||||||
|
result_poster_holder?.setOnClickListener {
|
||||||
|
try {
|
||||||
|
context?.let { ctx ->
|
||||||
|
runBlocking {
|
||||||
|
val sourceBuilder = AlertDialog.Builder(ctx)
|
||||||
|
sourceBuilder.setView(R.layout.result_poster)
|
||||||
|
|
||||||
|
val sourceDialog = sourceBuilder.create()
|
||||||
|
sourceDialog.show()
|
||||||
|
|
||||||
|
sourceDialog.findViewById<ImageView?>(R.id.imgPoster)
|
||||||
|
?.apply {
|
||||||
|
setImage(d.posterImage)
|
||||||
|
setOnClickListener {
|
||||||
|
sourceDialog.dismissSafe()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
result_cast_items?.isVisible = d.actors != null
|
result_cast_items?.isVisible = d.actors != null
|
||||||
|
@ -1016,6 +1133,7 @@ class ResultFragment : ResultTrailerPlayer() {
|
||||||
updateList(d.actors ?: emptyList())
|
updateList(d.actors ?: emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result_open_in_browser?.isGone = d.url.isBlank()
|
||||||
result_open_in_browser?.setOnClickListener {
|
result_open_in_browser?.setOnClickListener {
|
||||||
val i = Intent(ACTION_VIEW)
|
val i = Intent(ACTION_VIEW)
|
||||||
i.data = Uri.parse(d.url)
|
i.data = Uri.parse(d.url)
|
||||||
|
@ -1238,15 +1356,14 @@ class ResultFragment : ResultTrailerPlayer() {
|
||||||
Kitsu.isEnabled =
|
Kitsu.isEnabled =
|
||||||
settingsManager.getBoolean(ctx.getString(R.string.show_kitsu_posters_key), true)
|
settingsManager.getBoolean(ctx.getString(R.string.show_kitsu_posters_key), true)
|
||||||
|
|
||||||
val tempUrl = url
|
if (url != null) {
|
||||||
if (tempUrl != null) {
|
|
||||||
result_reload_connectionerror.setOnClickListener {
|
result_reload_connectionerror.setOnClickListener {
|
||||||
viewModel.load(tempUrl, apiName, showFillers, DubStatus.Dubbed, 0, 0) //TODO FIX
|
viewModel.load(url, apiName, showFillers, DubStatus.Dubbed, 0, 0) //TODO FIX
|
||||||
}
|
}
|
||||||
|
|
||||||
result_reload_connection_open_in_browser?.setOnClickListener {
|
result_reload_connection_open_in_browser?.setOnClickListener {
|
||||||
val i = Intent(ACTION_VIEW)
|
val i = Intent(ACTION_VIEW)
|
||||||
i.data = Uri.parse(tempUrl)
|
i.data = Uri.parse(url)
|
||||||
try {
|
try {
|
||||||
startActivity(i)
|
startActivity(i)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -1256,7 +1373,7 @@ class ResultFragment : ResultTrailerPlayer() {
|
||||||
|
|
||||||
result_open_in_browser?.setOnClickListener {
|
result_open_in_browser?.setOnClickListener {
|
||||||
val i = Intent(ACTION_VIEW)
|
val i = Intent(ACTION_VIEW)
|
||||||
i.data = Uri.parse(tempUrl)
|
i.data = Uri.parse(url)
|
||||||
try {
|
try {
|
||||||
startActivity(i)
|
startActivity(i)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -1267,7 +1384,7 @@ class ResultFragment : ResultTrailerPlayer() {
|
||||||
// bloats the navigation on tv
|
// bloats the navigation on tv
|
||||||
if (context?.isTrueTvSettings() == false) {
|
if (context?.isTrueTvSettings() == false) {
|
||||||
result_meta_site?.setOnClickListener {
|
result_meta_site?.setOnClickListener {
|
||||||
it.context?.openBrowser(tempUrl)
|
it.context?.openBrowser(url)
|
||||||
}
|
}
|
||||||
result_meta_site?.isFocusable = true
|
result_meta_site?.isFocusable = true
|
||||||
} else {
|
} else {
|
||||||
|
@ -1276,7 +1393,7 @@ class ResultFragment : ResultTrailerPlayer() {
|
||||||
|
|
||||||
if (restart || !viewModel.hasLoaded()) {
|
if (restart || !viewModel.hasLoaded()) {
|
||||||
//viewModel.clear()
|
//viewModel.clear()
|
||||||
viewModel.load(tempUrl, apiName, showFillers, DubStatus.Dubbed, 0, 0) //TODO FIX
|
viewModel.load(url, apiName, showFillers, DubStatus.Dubbed, 0, 0) //TODO FIX
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,17 +36,16 @@ import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator
|
||||||
import com.lagradost.cloudstream3.ui.player.SubtitleData
|
import com.lagradost.cloudstream3.ui.player.SubtitleData
|
||||||
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment
|
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment
|
||||||
import com.lagradost.cloudstream3.utils.*
|
import com.lagradost.cloudstream3.utils.*
|
||||||
|
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.isConnectedToChromecast
|
import com.lagradost.cloudstream3.utils.AppUtils.isConnectedToChromecast
|
||||||
import com.lagradost.cloudstream3.utils.CastHelper.startCast
|
import com.lagradost.cloudstream3.utils.CastHelper.startCast
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.checkWrite
|
import com.lagradost.cloudstream3.utils.UIHelper.checkWrite
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.navigate
|
import com.lagradost.cloudstream3.utils.UIHelper.navigate
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.requestRW
|
import com.lagradost.cloudstream3.utils.UIHelper.requestRW
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
@ -86,7 +85,6 @@ data class ResultData(
|
||||||
val yearText: UiText?,
|
val yearText: UiText?,
|
||||||
val nextAiringDate: UiText?,
|
val nextAiringDate: UiText?,
|
||||||
val nextAiringEpisode: UiText?,
|
val nextAiringEpisode: UiText?,
|
||||||
val playMovieText: UiText?,
|
|
||||||
val plotHeaderText: UiText,
|
val plotHeaderText: UiText,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -118,7 +116,7 @@ fun LoadResponse.toResultData(repo: APIRepository): ResultData {
|
||||||
val hours: Long = TimeUnit.SECONDS.toHours(seconds) - days * 24
|
val hours: Long = TimeUnit.SECONDS.toHours(seconds) - days * 24
|
||||||
val minute =
|
val minute =
|
||||||
TimeUnit.SECONDS.toMinutes(seconds) - TimeUnit.SECONDS.toHours(seconds) * 60
|
TimeUnit.SECONDS.toMinutes(seconds) - TimeUnit.SECONDS.toHours(seconds) * 60
|
||||||
nextAiringEpisode = when {
|
nextAiringDate = when {
|
||||||
days > 0 -> {
|
days > 0 -> {
|
||||||
txt(
|
txt(
|
||||||
R.string.next_episode_time_day_format,
|
R.string.next_episode_time_day_format,
|
||||||
|
@ -138,11 +136,11 @@ fun LoadResponse.toResultData(repo: APIRepository): ResultData {
|
||||||
)
|
)
|
||||||
else -> null
|
else -> null
|
||||||
}?.also {
|
}?.also {
|
||||||
nextAiringDate = txt(R.string.next_episode_format, airing.episode)
|
nextAiringEpisode = txt(R.string.next_episode_format, airing.episode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val dur = duration
|
||||||
return ResultData(
|
return ResultData(
|
||||||
syncData = syncData,
|
syncData = syncData,
|
||||||
plotHeaderText = txt(
|
plotHeaderText = txt(
|
||||||
|
@ -151,14 +149,6 @@ fun LoadResponse.toResultData(repo: APIRepository): ResultData {
|
||||||
else -> R.string.result_plot
|
else -> R.string.result_plot
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
playMovieText = txt(
|
|
||||||
when (this.type) {
|
|
||||||
TvType.Live -> R.string.play_livestream_button
|
|
||||||
TvType.Torrent -> R.string.play_torrent_button
|
|
||||||
TvType.Movie, TvType.AnimeMovie -> R.string.play_movie_button
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
),
|
|
||||||
nextAiringDate = nextAiringDate,
|
nextAiringDate = nextAiringDate,
|
||||||
nextAiringEpisode = nextAiringEpisode,
|
nextAiringEpisode = nextAiringEpisode,
|
||||||
posterImage = img(
|
posterImage = img(
|
||||||
|
@ -192,7 +182,7 @@ fun LoadResponse.toResultData(repo: APIRepository): ResultData {
|
||||||
TvType.Live -> R.string.live_singular
|
TvType.Live -> R.string.live_singular
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
yearText = txt(year),
|
yearText = txt(year?.toString()),
|
||||||
apiName = txt(apiName),
|
apiName = txt(apiName),
|
||||||
ratingText = rating?.div(1000f)?.let { txt(R.string.rating_format, it) },
|
ratingText = rating?.div(1000f)?.let { txt(R.string.rating_format, it) },
|
||||||
vpnText = txt(
|
vpnText = txt(
|
||||||
|
@ -204,7 +194,10 @@ fun LoadResponse.toResultData(repo: APIRepository): ResultData {
|
||||||
),
|
),
|
||||||
metaText =
|
metaText =
|
||||||
if (repo.providerType == ProviderType.MetaProvider) txt(R.string.provider_info_meta) else null,
|
if (repo.providerType == ProviderType.MetaProvider) txt(R.string.provider_info_meta) else null,
|
||||||
durationText = txt(R.string.duration_format, duration),
|
durationText = if (dur == null || dur <= 0) null else txt(
|
||||||
|
R.string.duration_format,
|
||||||
|
dur
|
||||||
|
),
|
||||||
onGoingText = if (this is EpisodeResponse) {
|
onGoingText = if (this is EpisodeResponse) {
|
||||||
txt(
|
txt(
|
||||||
when (showStatus) {
|
when (showStatus) {
|
||||||
|
@ -245,21 +238,43 @@ sealed class SelectPopup {
|
||||||
val map: Int?,
|
val map: Int?,
|
||||||
val callback: (Int?) -> Unit
|
val callback: (Int?) -> Unit
|
||||||
) : SelectPopup()
|
) : SelectPopup()
|
||||||
|
}
|
||||||
|
|
||||||
fun SelectPopup.transformResult(context: Context, input: Int?): Int? {
|
fun SelectPopup.callback(context: Context, input: Int?) {
|
||||||
if (input == null) return null
|
val ret = transformResult(context, input)
|
||||||
return when (this) {
|
return when (this) {
|
||||||
is SelectArray -> context.resources.getIntArray(map ?: return input).getOrNull(input)
|
is SelectPopup.SelectArray -> callback(ret)
|
||||||
?: input
|
is SelectPopup.SelectText -> callback(ret)
|
||||||
is SelectText -> input
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun SelectPopup.getOptions(context: Context): List<String> {
|
fun SelectPopup.transformResult(context: Context, input: Int?): Int? {
|
||||||
return when (this) {
|
if (input == null) return null
|
||||||
is SelectArray -> context.resources.getStringArray(options).toList()
|
return when (this) {
|
||||||
is SelectText -> options.map { it.asString(context) }
|
is SelectPopup.SelectArray -> context.resources.getIntArray(map ?: return input)
|
||||||
|
.getOrNull(input)
|
||||||
|
?: input
|
||||||
|
is SelectPopup.SelectText -> input
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun SelectPopup.getTitle(context: Context): String {
|
||||||
|
return when (this) {
|
||||||
|
is SelectPopup.SelectArray -> text.asString(context)
|
||||||
|
is SelectPopup.SelectText -> text.asString(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun SelectPopup.getOptions(context: Context): List<String> {
|
||||||
|
return when (this) {
|
||||||
|
is SelectPopup.SelectArray -> {
|
||||||
|
val cmap = this.map?.let { context.resources.getIntArray(it) }
|
||||||
|
context.resources.getStringArray(options).toList().filterIndexed { index, s ->
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
is SelectPopup.SelectText -> options.map { it.asString(context) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,6 +289,8 @@ class ResultViewModel2 : ViewModel() {
|
||||||
/** map<dub, map<season, List<episode>>> */
|
/** map<dub, map<season, List<episode>>> */
|
||||||
private var currentEpisodes: Map<EpisodeIndexer, List<ResultEpisode>> = mapOf()
|
private var currentEpisodes: Map<EpisodeIndexer, List<ResultEpisode>> = mapOf()
|
||||||
private var currentRanges: Map<EpisodeIndexer, List<EpisodeRange>> = mapOf()
|
private var currentRanges: Map<EpisodeIndexer, List<EpisodeRange>> = mapOf()
|
||||||
|
private var currentSeasons: Set<Int> = setOf()
|
||||||
|
private var currentDubStatus: Set<DubStatus> = setOf()
|
||||||
private var currentMeta: SyncAPI.SyncResult? = null
|
private var currentMeta: SyncAPI.SyncResult? = null
|
||||||
private var currentSync: Map<String, String>? = null
|
private var currentSync: Map<String, String>? = null
|
||||||
private var currentIndex: EpisodeIndexer? = null
|
private var currentIndex: EpisodeIndexer? = null
|
||||||
|
@ -294,13 +311,17 @@ class ResultViewModel2 : ViewModel() {
|
||||||
MutableLiveData(Resource.Loading())
|
MutableLiveData(Resource.Loading())
|
||||||
val page: LiveData<Resource<ResultData>> = _page
|
val page: LiveData<Resource<ResultData>> = _page
|
||||||
|
|
||||||
private val _episodes: MutableLiveData<Resource<List<ResultEpisode>>> =
|
private val _episodes: MutableLiveData<ResourceSome<List<ResultEpisode>>> =
|
||||||
MutableLiveData(Resource.Loading())
|
MutableLiveData(ResourceSome.Loading())
|
||||||
val episodes: LiveData<Resource<List<ResultEpisode>>> = _episodes
|
val episodes: LiveData<ResourceSome<List<ResultEpisode>>> = _episodes
|
||||||
|
|
||||||
private val _episodesCountText: MutableLiveData<UiText?> =
|
private val _movie: MutableLiveData<ResourceSome<Pair<UiText, ResultEpisode>>> =
|
||||||
MutableLiveData(null)
|
MutableLiveData(ResourceSome.None)
|
||||||
val episodesCountText: LiveData<UiText?> = _episodesCountText
|
val movie: LiveData<ResourceSome<Pair<UiText, ResultEpisode>>> = _movie
|
||||||
|
|
||||||
|
private val _episodesCountText: MutableLiveData<Some<UiText>> =
|
||||||
|
MutableLiveData(Some.None)
|
||||||
|
val episodesCountText: LiveData<Some<UiText>> = _episodesCountText
|
||||||
|
|
||||||
private val _trailers: MutableLiveData<List<TrailerData>> = MutableLiveData(mutableListOf())
|
private val _trailers: MutableLiveData<List<TrailerData>> = MutableLiveData(mutableListOf())
|
||||||
val trailers: LiveData<List<TrailerData>> = _trailers
|
val trailers: LiveData<List<TrailerData>> = _trailers
|
||||||
|
@ -318,24 +339,23 @@ class ResultViewModel2 : ViewModel() {
|
||||||
MutableLiveData(emptyList())
|
MutableLiveData(emptyList())
|
||||||
val seasonSelections: LiveData<List<Pair<UiText?, Int>>> = _seasonSelections
|
val seasonSelections: LiveData<List<Pair<UiText?, Int>>> = _seasonSelections
|
||||||
|
|
||||||
|
|
||||||
private val _recommendations: MutableLiveData<List<SearchResponse>> =
|
private val _recommendations: MutableLiveData<List<SearchResponse>> =
|
||||||
MutableLiveData(emptyList())
|
MutableLiveData(emptyList())
|
||||||
val recommendations: LiveData<List<SearchResponse>> = _recommendations
|
val recommendations: LiveData<List<SearchResponse>> = _recommendations
|
||||||
|
|
||||||
private val _selectedRange: MutableLiveData<UiText?> =
|
private val _selectedRange: MutableLiveData<Some<UiText>> =
|
||||||
MutableLiveData(null)
|
MutableLiveData(Some.None)
|
||||||
val selectedRange: LiveData<UiText?> = _selectedRange
|
val selectedRange: LiveData<Some<UiText>> = _selectedRange
|
||||||
|
|
||||||
private val _selectedSeason: MutableLiveData<UiText?> =
|
private val _selectedSeason: MutableLiveData<Some<UiText>> =
|
||||||
MutableLiveData(null)
|
MutableLiveData(Some.None)
|
||||||
val selectedSeason: LiveData<UiText?> = _selectedSeason
|
val selectedSeason: LiveData<Some<UiText>> = _selectedSeason
|
||||||
|
|
||||||
private val _selectedDubStatus: MutableLiveData<UiText?> = MutableLiveData(null)
|
private val _selectedDubStatus: MutableLiveData<Some<UiText>> = MutableLiveData(Some.None)
|
||||||
val selectedDubStatus: LiveData<UiText?> = _selectedDubStatus
|
val selectedDubStatus: LiveData<Some<UiText>> = _selectedDubStatus
|
||||||
|
|
||||||
private val _loadedLinks: MutableLiveData<LinkProgress?> = MutableLiveData(null)
|
private val _loadedLinks: MutableLiveData<Some<LinkProgress>> = MutableLiveData(Some.None)
|
||||||
val loadedLinks: LiveData<LinkProgress?> = _loadedLinks
|
val loadedLinks: LiveData<Some<LinkProgress>> = _loadedLinks
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "RVM2"
|
const val TAG = "RVM2"
|
||||||
|
@ -680,8 +700,8 @@ class ResultViewModel2 : ViewModel() {
|
||||||
private val _watchStatus: MutableLiveData<WatchType> = MutableLiveData(WatchType.NONE)
|
private val _watchStatus: MutableLiveData<WatchType> = MutableLiveData(WatchType.NONE)
|
||||||
val watchStatus: LiveData<WatchType> get() = _watchStatus
|
val watchStatus: LiveData<WatchType> get() = _watchStatus
|
||||||
|
|
||||||
private val _selectPopup: MutableLiveData<SelectPopup?> = MutableLiveData(null)
|
private val _selectPopup: MutableLiveData<Some<SelectPopup>> = MutableLiveData(Some.None)
|
||||||
val selectPopup: LiveData<SelectPopup?> get() = _selectPopup
|
val selectPopup: LiveData<Some<SelectPopup>> get() = _selectPopup
|
||||||
|
|
||||||
fun updateWatchStatus(status: WatchType) {
|
fun updateWatchStatus(status: WatchType) {
|
||||||
val currentId = currentId ?: return
|
val currentId = currentId ?: return
|
||||||
|
@ -707,14 +727,15 @@ class ResultViewModel2 : ViewModel() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun startChromecast(
|
private fun startChromecast(
|
||||||
activity: Activity?,
|
activity: Activity?,
|
||||||
result: ResultEpisode,
|
result: ResultEpisode,
|
||||||
isVisible: Boolean = true
|
isVisible: Boolean = true
|
||||||
) {
|
) {
|
||||||
if (activity == null) return
|
if (activity == null) return
|
||||||
val data = loadLinks(result, isVisible = isVisible, isCasting = true)
|
loadLinks(result, isVisible = isVisible, isCasting = true) { data ->
|
||||||
startChromecast(activity, result, data.links, data.subs, 0)
|
startChromecast(activity, result, data.links, data.subs, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startChromecast(
|
private fun startChromecast(
|
||||||
|
@ -742,51 +763,100 @@ class ResultViewModel2 : ViewModel() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val popupCallback: ((Int) -> Unit)? = null
|
|
||||||
|
|
||||||
fun cancelLinks() {
|
fun cancelLinks() {
|
||||||
|
println("called::cancelLinks")
|
||||||
currentLoadLinkJob?.cancel()
|
currentLoadLinkJob?.cancel()
|
||||||
_loadedLinks.postValue(null)
|
currentLoadLinkJob = null
|
||||||
|
_loadedLinks.postValue(Some.None)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun postPopup(text: UiText, options: List<UiText>, callback: suspend (Int?) -> Unit) {
|
||||||
|
_selectPopup.postValue(
|
||||||
|
some(SelectPopup.SelectText(
|
||||||
|
text,
|
||||||
|
options
|
||||||
|
) { value ->
|
||||||
|
viewModelScope.launch {
|
||||||
|
_selectPopup.postValue(Some.None)
|
||||||
|
callback.invoke(value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun postPopup(
|
||||||
|
text: UiText,
|
||||||
|
options: Int,
|
||||||
|
values: Int,
|
||||||
|
callback: suspend (Int?) -> Unit
|
||||||
|
) {
|
||||||
|
_selectPopup.postValue(
|
||||||
|
some(SelectPopup.SelectArray(
|
||||||
|
text,
|
||||||
|
options,
|
||||||
|
values
|
||||||
|
) { value ->
|
||||||
|
viewModelScope.launch {
|
||||||
|
_selectPopup.value = Some.None
|
||||||
|
callback.invoke(value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadLinks(
|
||||||
|
result: ResultEpisode,
|
||||||
|
isVisible: Boolean,
|
||||||
|
isCasting: Boolean,
|
||||||
|
clearCache: Boolean = false,
|
||||||
|
work: suspend (CoroutineScope.(LinkLoadingResult) -> Unit)
|
||||||
|
) {
|
||||||
|
currentLoadLinkJob?.cancel()
|
||||||
|
currentLoadLinkJob = ioSafe {
|
||||||
|
val links = loadLinks(
|
||||||
|
result,
|
||||||
|
isVisible = isVisible,
|
||||||
|
isCasting = isCasting,
|
||||||
|
clearCache = clearCache
|
||||||
|
)
|
||||||
|
if (!this.isActive) return@ioSafe
|
||||||
|
work(links)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var currentLoadLinkJob: Job? = null
|
private var currentLoadLinkJob: Job? = null
|
||||||
private suspend fun acquireSingleLink(
|
private fun acquireSingleLink(
|
||||||
result: ResultEpisode,
|
result: ResultEpisode,
|
||||||
isCasting: Boolean,
|
isCasting: Boolean,
|
||||||
text: UiText,
|
text: UiText,
|
||||||
callback: (Pair<LinkLoadingResult, Int>) -> Unit,
|
callback: (Pair<LinkLoadingResult, Int>) -> Unit,
|
||||||
) {
|
) {
|
||||||
currentLoadLinkJob = viewModelScope.launch {
|
loadLinks(result, isVisible = true, isCasting = isCasting) { links ->
|
||||||
val links = loadLinks(result, isVisible = true, isCasting = isCasting)
|
postPopup(
|
||||||
|
text,
|
||||||
_selectPopup.postValue(
|
links.links.map { txt("${it.name} ${Qualities.getStringByInt(it.quality)}") }) {
|
||||||
SelectPopup.SelectText(
|
callback.invoke(links to (it ?: return@postPopup))
|
||||||
text,
|
}
|
||||||
links.links.map { txt("${it.name} ${Qualities.getStringByInt(it.quality)}") }) {
|
|
||||||
callback.invoke(links to (it ?: return@SelectText))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun acquireSingleSubtitle(
|
private fun acquireSingleSubtitle(
|
||||||
result: ResultEpisode,
|
result: ResultEpisode,
|
||||||
isCasting: Boolean,
|
isCasting: Boolean,
|
||||||
text: UiText,
|
text: UiText,
|
||||||
callback: (Pair<LinkLoadingResult, Int>) -> Unit,
|
callback: (Pair<LinkLoadingResult, Int>) -> Unit,
|
||||||
) {
|
) {
|
||||||
currentLoadLinkJob = viewModelScope.launch {
|
loadLinks(result, isVisible = true, isCasting = isCasting) { links ->
|
||||||
val links = loadLinks(result, isVisible = true, isCasting = isCasting)
|
postPopup(
|
||||||
|
text,
|
||||||
_selectPopup.postValue(
|
links.subs.map { txt(it.name) })
|
||||||
SelectPopup.SelectText(
|
{
|
||||||
text,
|
callback.invoke(links to (it ?: return@postPopup))
|
||||||
links.subs.map { txt(it.name) }) {
|
}
|
||||||
callback.invoke(links to (it ?: return@SelectText))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun loadLinks(
|
suspend fun CoroutineScope.loadLinks(
|
||||||
result: ResultEpisode,
|
result: ResultEpisode,
|
||||||
isVisible: Boolean,
|
isVisible: Boolean,
|
||||||
isCasting: Boolean,
|
isCasting: Boolean,
|
||||||
|
@ -797,11 +867,12 @@ class ResultViewModel2 : ViewModel() {
|
||||||
val links: MutableSet<ExtractorLink> = mutableSetOf()
|
val links: MutableSet<ExtractorLink> = mutableSetOf()
|
||||||
val subs: MutableSet<SubtitleData> = mutableSetOf()
|
val subs: MutableSet<SubtitleData> = mutableSetOf()
|
||||||
fun updatePage() {
|
fun updatePage() {
|
||||||
if (isVisible) {
|
if (isVisible && isActive) {
|
||||||
_loadedLinks.postValue(LinkProgress(links.size, subs.size))
|
_loadedLinks.postValue(some(LinkProgress(links.size, subs.size)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
updatePage()
|
||||||
tempGenerator.generateLinks(clearCache, isCasting, { (link, _) ->
|
tempGenerator.generateLinks(clearCache, isCasting, { (link, _) ->
|
||||||
if (link != null) {
|
if (link != null) {
|
||||||
links += link
|
links += link
|
||||||
|
@ -814,7 +885,7 @@ class ResultViewModel2 : ViewModel() {
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logError(e)
|
logError(e)
|
||||||
} finally {
|
} finally {
|
||||||
_loadedLinks.postValue(null)
|
_loadedLinks.postValue(Some.None)
|
||||||
}
|
}
|
||||||
|
|
||||||
return LinkLoadingResult(sortUrls(links), sortSubs(subs))
|
return LinkLoadingResult(sortUrls(links), sortSubs(subs))
|
||||||
|
@ -884,20 +955,22 @@ class ResultViewModel2 : ViewModel() {
|
||||||
private suspend fun handleEpisodeClickEvent(activity: Activity?, click: EpisodeClickEvent) {
|
private suspend fun handleEpisodeClickEvent(activity: Activity?, click: EpisodeClickEvent) {
|
||||||
when (click.action) {
|
when (click.action) {
|
||||||
ACTION_SHOW_OPTIONS -> {
|
ACTION_SHOW_OPTIONS -> {
|
||||||
_selectPopup.postValue(
|
postPopup(
|
||||||
SelectPopup.SelectArray(
|
txt(
|
||||||
txt(""), // TODO FIX
|
activity?.getNameFull(
|
||||||
R.array.episode_long_click_options,
|
click.data.name,
|
||||||
R.array.episode_long_click_options_values
|
click.data.episode,
|
||||||
) { result ->
|
click.data.season
|
||||||
if (result == null) return@SelectArray
|
) ?: ""
|
||||||
viewModelScope.launch {
|
), // TODO FIX
|
||||||
handleEpisodeClickEvent(
|
R.array.episode_long_click_options,
|
||||||
activity,
|
R.array.episode_long_click_options_values
|
||||||
click.copy(action = result)
|
) { result ->
|
||||||
)
|
handleEpisodeClickEvent(
|
||||||
}
|
activity,
|
||||||
})
|
click.copy(action = result ?: return@postPopup)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ACTION_CLICK_DEFAULT -> {
|
ACTION_CLICK_DEFAULT -> {
|
||||||
activity?.let { ctx ->
|
activity?.let { ctx ->
|
||||||
|
@ -986,7 +1059,14 @@ class ResultViewModel2 : ViewModel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ACTION_RELOAD_EPISODE -> {
|
ACTION_RELOAD_EPISODE -> {
|
||||||
loadLinks(click.data, isVisible = false, isCasting = false, clearCache = true)
|
ioSafe {
|
||||||
|
loadLinks(
|
||||||
|
click.data,
|
||||||
|
isVisible = false,
|
||||||
|
isCasting = false,
|
||||||
|
clearCache = true
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ACTION_CHROME_CAST_MIRROR -> {
|
ACTION_CHROME_CAST_MIRROR -> {
|
||||||
acquireSingleLink(
|
acquireSingleLink(
|
||||||
|
@ -1030,8 +1110,12 @@ class ResultViewModel2 : ViewModel() {
|
||||||
startChromecast(activity, click.data)
|
startChromecast(activity, click.data)
|
||||||
}
|
}
|
||||||
ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> {
|
ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> {
|
||||||
currentLoadLinkJob = viewModelScope.launch {
|
loadLinks(click.data, isVisible = true, isCasting = true) { links ->
|
||||||
playWithVlc(activity, loadLinks(click.data, true, true), click.data.id)
|
playWithVlc(
|
||||||
|
activity,
|
||||||
|
links,
|
||||||
|
click.data.id
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ACTION_PLAY_EPISODE_IN_PLAYER -> {
|
ACTION_PLAY_EPISODE_IN_PLAYER -> {
|
||||||
|
@ -1176,6 +1260,13 @@ class ResultViewModel2 : ViewModel() {
|
||||||
postEpisodeRange(currentIndex?.copy(season = season), currentRange)
|
postEpisodeRange(currentIndex?.copy(season = season), currentRange)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getMovie(): ResultEpisode? {
|
||||||
|
return currentEpisodes.entries.firstOrNull()?.value?.firstOrNull()?.let { ep ->
|
||||||
|
val posDur = DataStoreHelper.getViewPos(ep.id)
|
||||||
|
ep.copy(position = posDur?.position ?: 0, duration = posDur?.duration ?: 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun getEpisodes(indexer: EpisodeIndexer, range: EpisodeRange): List<ResultEpisode> {
|
private fun getEpisodes(indexer: EpisodeIndexer, range: EpisodeRange): List<ResultEpisode> {
|
||||||
val startIndex = range.startIndex
|
val startIndex = range.startIndex
|
||||||
val length = range.length
|
val length = range.length
|
||||||
|
@ -1192,15 +1283,50 @@ class ResultViewModel2 : ViewModel() {
|
||||||
?: emptyList()
|
?: emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun postMovie() {
|
||||||
|
val response = currentResponse
|
||||||
|
_episodes.postValue(ResourceSome.None)
|
||||||
|
|
||||||
|
if (response == null) {
|
||||||
|
_movie.postValue(ResourceSome.None)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val text = txt(
|
||||||
|
when (response.type) {
|
||||||
|
TvType.Torrent -> R.string.play_torrent_button
|
||||||
|
else -> {
|
||||||
|
if (response.type.isLiveStream())
|
||||||
|
R.string.play_livestream_button
|
||||||
|
else if (response.type.isMovieType()) // this wont break compatibility as you only need to override isMovieType
|
||||||
|
R.string.play_movie_button
|
||||||
|
else null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
val data = getMovie()
|
||||||
|
_episodes.postValue(ResourceSome.None)
|
||||||
|
if (text == null || data == null) {
|
||||||
|
_movie.postValue(ResourceSome.None)
|
||||||
|
} else {
|
||||||
|
_movie.postValue(ResourceSome.Success(text to data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun reloadEpisodes() {
|
fun reloadEpisodes() {
|
||||||
_episodes.postValue(
|
if (currentResponse?.isMovie() == true) {
|
||||||
Resource.Success(
|
postMovie()
|
||||||
getEpisodes(
|
} else {
|
||||||
currentIndex ?: return,
|
_episodes.postValue(
|
||||||
currentRange ?: return
|
ResourceSome.Success(
|
||||||
|
getEpisodes(
|
||||||
|
currentIndex ?: return,
|
||||||
|
currentRange ?: return
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
_movie.postValue(ResourceSome.None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun postEpisodeRange(indexer: EpisodeIndexer?, range: EpisodeRange?) {
|
private fun postEpisodeRange(indexer: EpisodeIndexer?, range: EpisodeRange?) {
|
||||||
|
@ -1208,45 +1334,79 @@ class ResultViewModel2 : ViewModel() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val size = currentEpisodes[indexer]?.size
|
val episodes = currentEpisodes[indexer]
|
||||||
|
val ranges = currentRanges[indexer]
|
||||||
_episodesCountText.postValue(
|
val size = episodes?.size
|
||||||
txt(
|
val isMovie = currentResponse?.isMovie() == true
|
||||||
R.string.episode_format,
|
|
||||||
txt(if (size == 1) R.string.episode else R.string.episodes),
|
|
||||||
size
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
currentIndex = indexer
|
currentIndex = indexer
|
||||||
currentRange = range
|
currentRange = range
|
||||||
|
|
||||||
|
|
||||||
|
_rangeSelections.postValue(ranges?.map { r ->
|
||||||
|
val text = txt(R.string.episodes_range, r.startEpisode, r.endEpisode)
|
||||||
|
text to r
|
||||||
|
} ?: emptyList())
|
||||||
|
|
||||||
|
_episodesCountText.postValue(
|
||||||
|
some(
|
||||||
|
if (isMovie) null else
|
||||||
|
txt(
|
||||||
|
R.string.episode_format,
|
||||||
|
size,
|
||||||
|
txt(if (size == 1) R.string.episode else R.string.episodes),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
_selectedSeason.postValue(
|
_selectedSeason.postValue(
|
||||||
when (indexer.season) {
|
some(
|
||||||
0 -> txt(R.string.no_season)
|
if (isMovie || currentSeasons.size <= 1) null else
|
||||||
else -> txt(R.string.season_format, R.string.season, indexer.season) //TODO FIX
|
when (indexer.season) {
|
||||||
}
|
0 -> txt(R.string.no_season)
|
||||||
|
else -> txt(
|
||||||
|
R.string.season_format,
|
||||||
|
txt(R.string.season),
|
||||||
|
indexer.season
|
||||||
|
) //TODO FIX DISPLAYNAME
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
_selectedRange.postValue(
|
_selectedRange.postValue(
|
||||||
if ((currentRanges[indexer]?.size ?: 0) > 1) {
|
some(
|
||||||
txt(R.string.episodes_range, range.startEpisode, range.endEpisode)
|
if (isMovie) null else if ((currentRanges[indexer]?.size ?: 0) > 1) {
|
||||||
} else {
|
txt(R.string.episodes_range, range.startEpisode, range.endEpisode)
|
||||||
null
|
} else {
|
||||||
}
|
null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
_selectedDubStatus.postValue(
|
||||||
|
some(
|
||||||
|
if (isMovie || currentDubStatus.size <= 1) null else
|
||||||
|
txt(indexer.dubStatus)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
_selectedDubStatus.postValue(txt(indexer.dubStatus))
|
|
||||||
|
|
||||||
//TODO SET KEYS
|
//TODO SET KEYS
|
||||||
preferStartEpisode = range.startEpisode
|
preferStartEpisode = range.startEpisode
|
||||||
preferStartSeason = indexer.season
|
preferStartSeason = indexer.season
|
||||||
preferDubStatus = indexer.dubStatus
|
preferDubStatus = indexer.dubStatus
|
||||||
|
|
||||||
generator = currentEpisodes[indexer]?.let { list ->
|
generator = if (isMovie) {
|
||||||
RepoLinkGenerator(list)
|
getMovie()?.let { RepoLinkGenerator(listOf(it)) }
|
||||||
|
} else {
|
||||||
|
episodes?.let { list ->
|
||||||
|
RepoLinkGenerator(list)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val ret = getEpisodes(indexer, range)
|
if (isMovie) {
|
||||||
_episodes.postValue(Resource.Success(ret))
|
postMovie()
|
||||||
|
} else {
|
||||||
|
val ret = getEpisodes(indexer, range)
|
||||||
|
_episodes.postValue(ResourceSome.Success(ret))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun postSuccessful(
|
private suspend fun postSuccessful(
|
||||||
|
@ -1262,11 +1422,13 @@ class ResultViewModel2 : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun postEpisodes(loadResponse: LoadResponse, updateFillers: Boolean) {
|
private suspend fun postEpisodes(loadResponse: LoadResponse, updateFillers: Boolean) {
|
||||||
_episodes.postValue(Resource.Loading())
|
_episodes.postValue(ResourceSome.Loading())
|
||||||
|
|
||||||
val mainId = loadResponse.getId()
|
val mainId = loadResponse.getId()
|
||||||
currentId = mainId
|
currentId = mainId
|
||||||
|
|
||||||
|
_watchStatus.postValue(getResultWatchState(mainId))
|
||||||
|
|
||||||
if (updateFillers && loadResponse is AnimeLoadResponse) {
|
if (updateFillers && loadResponse is AnimeLoadResponse) {
|
||||||
updateFillers(loadResponse.name)
|
updateFillers(loadResponse.name)
|
||||||
}
|
}
|
||||||
|
@ -1425,10 +1587,30 @@ class ResultViewModel2 : ViewModel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val seasonsSelection = mutableSetOf<Int>()
|
||||||
|
val dubSelection = mutableSetOf<DubStatus>()
|
||||||
|
allEpisodes.keys.forEach { key ->
|
||||||
|
seasonsSelection += key.season
|
||||||
|
dubSelection += key.dubStatus
|
||||||
|
}
|
||||||
|
currentDubStatus = dubSelection
|
||||||
|
currentSeasons = seasonsSelection
|
||||||
|
_dubSubSelections.postValue(dubSelection.map { txt(it) to it })
|
||||||
|
if (loadResponse is EpisodeResponse) {
|
||||||
|
_seasonSelections.postValue(seasonsSelection.map { seasonNumber ->
|
||||||
|
val name =
|
||||||
|
loadResponse.seasonNames?.firstOrNull { it.season == seasonNumber }?.name?.let { seasonData ->
|
||||||
|
txt(seasonData)
|
||||||
|
} ?: txt(R.string.season_format, txt(R.string.season), seasonNumber)
|
||||||
|
name to seasonNumber
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
currentEpisodes = allEpisodes
|
currentEpisodes = allEpisodes
|
||||||
val ranges = getRanges(allEpisodes)
|
val ranges = getRanges(allEpisodes)
|
||||||
currentRanges = ranges
|
currentRanges = ranges
|
||||||
|
|
||||||
|
|
||||||
// this takes the indexer most preferable by the user given the current sorting
|
// this takes the indexer most preferable by the user given the current sorting
|
||||||
val min = ranges.keys.minByOrNull { index ->
|
val min = ranges.keys.minByOrNull { index ->
|
||||||
kotlin.math.abs(
|
kotlin.math.abs(
|
||||||
|
@ -1464,7 +1646,7 @@ class ResultViewModel2 : ViewModel() {
|
||||||
) =
|
) =
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_page.postValue(Resource.Loading(url))
|
_page.postValue(Resource.Loading(url))
|
||||||
_episodes.postValue(Resource.Loading(url))
|
_episodes.postValue(ResourceSome.Loading())
|
||||||
|
|
||||||
preferDubStatus = dubStatus
|
preferDubStatus = dubStatus
|
||||||
currentShowFillers = showFillers
|
currentShowFillers = showFillers
|
||||||
|
|
|
@ -8,6 +8,7 @@ import androidx.annotation.DrawableRes
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import com.lagradost.cloudstream3.mvvm.Some
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.html
|
import com.lagradost.cloudstream3.utils.AppUtils.html
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||||
|
@ -152,3 +153,11 @@ fun TextView?.setTextHtml(text: UiText?) {
|
||||||
this.text = str.html()
|
this.text = str.html()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun TextView?.setTextHtml(text: Some<UiText>) {
|
||||||
|
setTextHtml(if(text is Some.Success) text.value else null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun TextView?.setText(text: Some<UiText>) {
|
||||||
|
setText(if(text is Some.Success) text.value else null)
|
||||||
|
}
|
|
@ -179,21 +179,21 @@ object AppUtils {
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
fun Context.addProgramsToContinueWatching(data: List<DataStoreHelper.ResumeWatchingResult>) {
|
fun Context.addProgramsToContinueWatching(data: List<DataStoreHelper.ResumeWatchingResult>) {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
||||||
|
val context = this
|
||||||
ioSafe {
|
ioSafe {
|
||||||
data.forEach { episodeInfo ->
|
data.forEach { episodeInfo ->
|
||||||
try {
|
try {
|
||||||
val (program, id) = getWatchNextProgramByVideoId(episodeInfo.url, this)
|
val (program, id) = getWatchNextProgramByVideoId(episodeInfo.url, context)
|
||||||
val nextProgram = buildWatchNextProgramUri(this, episodeInfo)
|
val nextProgram = buildWatchNextProgramUri(context, episodeInfo)
|
||||||
|
|
||||||
// If the program is already in the Watch Next row, update it
|
// If the program is already in the Watch Next row, update it
|
||||||
if (program != null && id != null) {
|
if (program != null && id != null) {
|
||||||
PreviewChannelHelper(this).updateWatchNextProgram(
|
PreviewChannelHelper(context).updateWatchNextProgram(
|
||||||
nextProgram,
|
nextProgram,
|
||||||
id,
|
id,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
PreviewChannelHelper(this)
|
PreviewChannelHelper(context)
|
||||||
.publishWatchNextProgram(nextProgram)
|
.publishWatchNextProgram(nextProgram)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|
|
@ -12,7 +12,7 @@ object Coroutines {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ioSafe(work: suspend (() -> Unit)): Job {
|
fun ioSafe(work: suspend (CoroutineScope.() -> Unit)): Job {
|
||||||
return CoroutineScope(Dispatchers.IO).launch {
|
return CoroutineScope(Dispatchers.IO).launch {
|
||||||
try {
|
try {
|
||||||
work()
|
work()
|
||||||
|
@ -22,7 +22,7 @@ object Coroutines {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun <T> ioWork(work: suspend (() -> T)): T {
|
suspend fun <T> ioWork(work: suspend (CoroutineScope.() -> T)): T {
|
||||||
return withContext(Dispatchers.IO) {
|
return withContext(Dispatchers.IO) {
|
||||||
work()
|
work()
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,9 @@ import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSet
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
|
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||||
|
import kotlinx.android.synthetic.main.add_account_input.*
|
||||||
|
import kotlinx.android.synthetic.main.add_account_input.text1
|
||||||
|
import kotlinx.android.synthetic.main.bottom_selection_dialog_direct.*
|
||||||
|
|
||||||
object SingleSelectionHelper {
|
object SingleSelectionHelper {
|
||||||
fun Activity?.showOptionSelectStringRes(
|
fun Activity?.showOptionSelectStringRes(
|
||||||
|
@ -21,7 +24,7 @@ object SingleSelectionHelper {
|
||||||
tvOptions: List<Int> = listOf(),
|
tvOptions: List<Int> = listOf(),
|
||||||
callback: (Pair<Boolean, Int>) -> Unit
|
callback: (Pair<Boolean, Int>) -> Unit
|
||||||
) {
|
) {
|
||||||
if(this == null) return
|
if (this == null) return
|
||||||
|
|
||||||
this.showOptionSelect(
|
this.showOptionSelect(
|
||||||
view,
|
view,
|
||||||
|
@ -39,7 +42,7 @@ object SingleSelectionHelper {
|
||||||
tvOptions: List<String>,
|
tvOptions: List<String>,
|
||||||
callback: (Pair<Boolean, Int>) -> Unit
|
callback: (Pair<Boolean, Int>) -> Unit
|
||||||
) {
|
) {
|
||||||
if(this == null) return
|
if (this == null) return
|
||||||
|
|
||||||
if (this.isTvSettings()) {
|
if (this.isTvSettings()) {
|
||||||
val builder =
|
val builder =
|
||||||
|
@ -86,42 +89,44 @@ object SingleSelectionHelper {
|
||||||
showApply: Boolean,
|
showApply: Boolean,
|
||||||
isMultiSelect: Boolean,
|
isMultiSelect: Boolean,
|
||||||
callback: (List<Int>) -> Unit,
|
callback: (List<Int>) -> Unit,
|
||||||
dismissCallback: () -> Unit
|
dismissCallback: () -> Unit,
|
||||||
|
itemLayout: Int = R.layout.sort_bottom_single_choice
|
||||||
) {
|
) {
|
||||||
if(this == null) return
|
if (this == null) return
|
||||||
|
|
||||||
val realShowApply = showApply || isMultiSelect
|
val realShowApply = showApply || isMultiSelect
|
||||||
val listView = dialog.findViewById<ListView>(R.id.listview1)!!
|
val listView = dialog.listview1//.findViewById<ListView>(R.id.listview1)!!
|
||||||
val textView = dialog.findViewById<TextView>(R.id.text1)!!
|
val textView = dialog.text1//.findViewById<TextView>(R.id.text1)!!
|
||||||
val applyButton = dialog.findViewById<TextView>(R.id.apply_btt)!!
|
val applyButton = dialog.apply_btt//.findViewById<TextView>(R.id.apply_btt)
|
||||||
val cancelButton = dialog.findViewById<TextView>(R.id.cancel_btt)!!
|
val cancelButton = dialog.cancel_btt//findViewById<TextView>(R.id.cancel_btt)
|
||||||
val applyHolder = dialog.findViewById<LinearLayout>(R.id.apply_btt_holder)!!
|
val applyHolder = dialog.apply_btt_holder//.findViewById<LinearLayout>(R.id.apply_btt_holder)
|
||||||
|
|
||||||
applyHolder.isVisible = realShowApply
|
applyHolder?.isVisible = realShowApply
|
||||||
if (!realShowApply) {
|
if (!realShowApply) {
|
||||||
val params = listView.layoutParams as LinearLayout.LayoutParams
|
val params = listView.layoutParams as LinearLayout.LayoutParams
|
||||||
params.setMargins(listView.marginLeft, listView.marginTop, listView.marginRight, 0)
|
params.setMargins(listView.marginLeft, listView.marginTop, listView.marginRight, 0)
|
||||||
listView.layoutParams = params
|
listView.layoutParams = params
|
||||||
}
|
}
|
||||||
|
|
||||||
textView.text = name
|
textView?.text = name
|
||||||
|
textView?.isGone = name.isBlank()
|
||||||
|
|
||||||
val arrayAdapter = ArrayAdapter<String>(this, R.layout.sort_bottom_single_choice)
|
val arrayAdapter = ArrayAdapter<String>(this, itemLayout)
|
||||||
arrayAdapter.addAll(items)
|
arrayAdapter.addAll(items)
|
||||||
|
|
||||||
listView.adapter = arrayAdapter
|
listView?.adapter = arrayAdapter
|
||||||
if (isMultiSelect) {
|
if (isMultiSelect) {
|
||||||
listView.choiceMode = AbsListView.CHOICE_MODE_MULTIPLE
|
listView?.choiceMode = AbsListView.CHOICE_MODE_MULTIPLE
|
||||||
} else {
|
} else {
|
||||||
listView.choiceMode = AbsListView.CHOICE_MODE_SINGLE
|
listView?.choiceMode = AbsListView.CHOICE_MODE_SINGLE
|
||||||
}
|
}
|
||||||
|
|
||||||
for (select in selectedIndex) {
|
for (select in selectedIndex) {
|
||||||
listView.setItemChecked(select, true)
|
listView?.setItemChecked(select, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedIndex.minOrNull()?.let {
|
selectedIndex.minOrNull()?.let {
|
||||||
listView.setSelection(it)
|
listView?.setSelection(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
// var lastSelectedIndex = if(selectedIndex.isNotEmpty()) selectedIndex.first() else -1
|
// var lastSelectedIndex = if(selectedIndex.isNotEmpty()) selectedIndex.first() else -1
|
||||||
|
@ -130,7 +135,7 @@ object SingleSelectionHelper {
|
||||||
dismissCallback.invoke()
|
dismissCallback.invoke()
|
||||||
}
|
}
|
||||||
|
|
||||||
listView.setOnItemClickListener { _, _, which, _ ->
|
listView?.setOnItemClickListener { _, _, which, _ ->
|
||||||
// lastSelectedIndex = which
|
// lastSelectedIndex = which
|
||||||
if (realShowApply) {
|
if (realShowApply) {
|
||||||
if (!isMultiSelect) {
|
if (!isMultiSelect) {
|
||||||
|
@ -142,7 +147,7 @@ object SingleSelectionHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (realShowApply) {
|
if (realShowApply) {
|
||||||
applyButton.setOnClickListener {
|
applyButton?.setOnClickListener {
|
||||||
val list = ArrayList<Int>()
|
val list = ArrayList<Int>()
|
||||||
for (index in 0 until listView.count) {
|
for (index in 0 until listView.count) {
|
||||||
if (listView.checkedItemPositions[index])
|
if (listView.checkedItemPositions[index])
|
||||||
|
@ -151,7 +156,7 @@ object SingleSelectionHelper {
|
||||||
callback.invoke(list)
|
callback.invoke(list)
|
||||||
dialog.dismissSafe(this)
|
dialog.dismissSafe(this)
|
||||||
}
|
}
|
||||||
cancelButton.setOnClickListener {
|
cancelButton?.setOnClickListener {
|
||||||
dialog.dismissSafe(this)
|
dialog.dismissSafe(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,7 +171,7 @@ object SingleSelectionHelper {
|
||||||
callback: (String) -> Unit,
|
callback: (String) -> Unit,
|
||||||
dismissCallback: () -> Unit
|
dismissCallback: () -> Unit
|
||||||
) {
|
) {
|
||||||
if(this == null) return
|
if (this == null) return
|
||||||
|
|
||||||
val inputView = dialog.findViewById<EditText>(R.id.nginx_text_input)!!
|
val inputView = dialog.findViewById<EditText>(R.id.nginx_text_input)!!
|
||||||
val textView = dialog.findViewById<TextView>(R.id.text1)!!
|
val textView = dialog.findViewById<TextView>(R.id.text1)!!
|
||||||
|
@ -205,7 +210,7 @@ object SingleSelectionHelper {
|
||||||
dismissCallback: () -> Unit,
|
dismissCallback: () -> Unit,
|
||||||
callback: (List<Int>) -> Unit,
|
callback: (List<Int>) -> Unit,
|
||||||
) {
|
) {
|
||||||
if(this == null) return
|
if (this == null) return
|
||||||
|
|
||||||
val builder =
|
val builder =
|
||||||
AlertDialog.Builder(this, R.style.AlertDialogCustom)
|
AlertDialog.Builder(this, R.style.AlertDialogCustom)
|
||||||
|
@ -224,7 +229,7 @@ object SingleSelectionHelper {
|
||||||
dismissCallback: () -> Unit,
|
dismissCallback: () -> Unit,
|
||||||
callback: (Int) -> Unit,
|
callback: (Int) -> Unit,
|
||||||
) {
|
) {
|
||||||
if(this == null) return
|
if (this == null) return
|
||||||
|
|
||||||
val builder =
|
val builder =
|
||||||
AlertDialog.Builder(this, R.style.AlertDialogCustom)
|
AlertDialog.Builder(this, R.style.AlertDialogCustom)
|
||||||
|
@ -271,6 +276,31 @@ object SingleSelectionHelper {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Activity.showBottomDialogInstant(
|
||||||
|
items: List<String>,
|
||||||
|
name: String,
|
||||||
|
dismissCallback: () -> Unit,
|
||||||
|
callback: (Int) -> Unit,
|
||||||
|
): BottomSheetDialog {
|
||||||
|
val builder =
|
||||||
|
BottomSheetDialog(this)
|
||||||
|
builder.setContentView(R.layout.bottom_selection_dialog_direct)
|
||||||
|
|
||||||
|
builder.show()
|
||||||
|
showDialog(
|
||||||
|
builder,
|
||||||
|
items,
|
||||||
|
listOf(),
|
||||||
|
name,
|
||||||
|
showApply = false,
|
||||||
|
isMultiSelect = false,
|
||||||
|
callback = { if (it.isNotEmpty()) callback.invoke(it.first()) },
|
||||||
|
dismissCallback = dismissCallback,
|
||||||
|
itemLayout = R.layout.sort_bottom_single_choice_no_checkmark
|
||||||
|
)
|
||||||
|
return builder
|
||||||
|
}
|
||||||
|
|
||||||
fun Activity.showNginxTextInputDialog(
|
fun Activity.showNginxTextInputDialog(
|
||||||
name: String,
|
name: String,
|
||||||
value: String,
|
value: String,
|
||||||
|
|
33
app/src/main/res/layout/bottom_loading.xml
Normal file
33
app/src/main/res/layout/bottom_loading.xml
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text1"
|
||||||
|
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||||
|
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textColor="?attr/textColor"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_rowWeight="1"
|
||||||
|
android:text="@string/loading_chromecast"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<androidx.core.widget.ContentLoadingProgressBar
|
||||||
|
android:layout_marginBottom="-6.5dp"
|
||||||
|
android:indeterminate="true"
|
||||||
|
style="@android:style/Widget.Material.ProgressBar.Horizontal"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:indeterminateTint="?attr/colorPrimary"
|
||||||
|
android:id="@+id/progressBar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:progressTint="?attr/colorPrimary"
|
||||||
|
android:layout_height="15dp">
|
||||||
|
</androidx.core.widget.ContentLoadingProgressBar>
|
||||||
|
</LinearLayout>
|
34
app/src/main/res/layout/bottom_selection_dialog_direct.xml
Normal file
34
app/src/main/res/layout/bottom_selection_dialog_direct.xml
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text1"
|
||||||
|
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||||
|
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textColor="?attr/textColor"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_rowWeight="1"
|
||||||
|
tools:text="Test"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:nextFocusRight="@id/cancel_btt"
|
||||||
|
android:nextFocusLeft="@id/apply_btt"
|
||||||
|
|
||||||
|
android:id="@+id/listview1"
|
||||||
|
android:layout_marginBottom="60dp"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:requiresFadingEdge="vertical"
|
||||||
|
tools:listitem="@layout/sort_bottom_single_choice_no_checkmark"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_rowWeight="1" />
|
||||||
|
</LinearLayout>
|
|
@ -843,7 +843,6 @@
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/result_next_airing_holder"
|
android:id="@+id/result_next_airing_holder"
|
||||||
android:layout_gravity="start"
|
android:layout_gravity="start"
|
||||||
android:paddingBottom="15dp"
|
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
|
@ -12,6 +12,6 @@
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:src="@drawable/default_cover"
|
android:src="@drawable/default_cover"
|
||||||
android:background="#fffff0"
|
android:background="?attr/primaryGrayBackground"
|
||||||
android:contentDescription="@string/poster_image" />
|
android:contentDescription="@string/poster_image" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<!--<CheckedTextView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@android:id/text1"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?android:attr/listPreferredItemHeightSmall"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceListItemSmall"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textColor="?attr/textColor"
|
||||||
|
tools:text="Example Text"
|
||||||
|
android:background="?attr/bitDarkerGrayBackground"
|
||||||
|
android:checkMark="?android:attr/listChoiceIndicatorSingle"
|
||||||
|
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||||
|
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
style="@style/NoCheckLabel"
|
||||||
|
tools:text="hello"
|
||||||
|
android:textStyle="normal"
|
||||||
|
android:textColor="?attr/textColor"
|
||||||
|
android:id="@android:id/text1" />
|
|
@ -448,7 +448,15 @@
|
||||||
<item name="android:textColor">?attr/textColor</item>
|
<item name="android:textColor">?attr/textColor</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="CheckLabel" parent="@style/AppTextViewStyle">
|
<style name="CheckLabel" parent="@style/NoCheckLabel">
|
||||||
|
|
||||||
|
<!-- <item name="drawableTint">@color/check_selection_color</item>-->
|
||||||
|
<!-- Set color in the drawable instead of tint to allow multiple drawables-->
|
||||||
|
<item name="android:checkMark">?android:attr/listChoiceIndicatorSingle</item>
|
||||||
|
<item name="drawableStartCompat">@drawable/ic_baseline_check_24_listview</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="NoCheckLabel" parent="@style/AppTextViewStyle">
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">match_parent</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="android:minHeight">?android:attr/listPreferredItemHeightSmall</item>
|
<item name="android:minHeight">?android:attr/listPreferredItemHeightSmall</item>
|
||||||
|
@ -458,15 +466,13 @@
|
||||||
<item name="android:gravity">center_vertical</item>
|
<item name="android:gravity">center_vertical</item>
|
||||||
<item name="android:paddingStart">12dp</item>
|
<item name="android:paddingStart">12dp</item>
|
||||||
<item name="android:paddingEnd">12dp</item>
|
<item name="android:paddingEnd">12dp</item>
|
||||||
<item name="android:checkMark">?android:attr/listChoiceIndicatorSingle</item>
|
|
||||||
<item name="android:ellipsize">marquee</item>
|
<item name="android:ellipsize">marquee</item>
|
||||||
<item name="android:foreground">?attr/selectableItemBackgroundBorderless</item>
|
<item name="android:foreground">?attr/selectableItemBackgroundBorderless</item>
|
||||||
<item name="android:drawablePadding">20dp</item>
|
<item name="android:drawablePadding">20dp</item>
|
||||||
<!-- <item name="drawableTint">@color/check_selection_color</item>-->
|
|
||||||
<!-- Set color in the drawable instead of tint to allow multiple drawables-->
|
|
||||||
<item name="drawableStartCompat">@drawable/ic_baseline_check_24_listview</item>
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<style name="BlackButton" parent="NiceButton">
|
<style name="BlackButton" parent="NiceButton">
|
||||||
<item name="strokeColor">?attr/textColor</item>
|
<item name="strokeColor">?attr/textColor</item>
|
||||||
<item name="backgroundTint">?attr/iconGrayBackground</item>
|
<item name="backgroundTint">?attr/iconGrayBackground</item>
|
||||||
|
|
Loading…
Reference in a new issue