AquaStream/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt

452 lines
18 KiB
Kotlin
Raw Normal View History

2021-05-16 18:28:00 +00:00
package com.lagradost.cloudstream3.ui.result
2021-05-18 13:43:32 +00:00
import android.annotation.SuppressLint
2021-06-15 16:07:20 +00:00
import android.content.Context
2021-06-10 15:15:14 +00:00
import android.content.Intent
import android.net.Uri
2021-05-16 18:28:00 +00:00
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
2021-06-06 18:06:01 +00:00
import android.view.View.GONE
import android.view.View.VISIBLE
2021-05-16 18:28:00 +00:00
import android.view.ViewGroup
2021-06-10 15:15:14 +00:00
import android.widget.Toast
2021-05-18 13:43:32 +00:00
import androidx.appcompat.app.AlertDialog
2021-05-22 22:25:56 +00:00
import androidx.appcompat.app.AppCompatActivity
2021-05-18 13:43:32 +00:00
import androidx.core.widget.NestedScrollView
2021-05-16 18:28:00 +00:00
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
2021-05-18 13:43:32 +00:00
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.request.RequestOptions.bitmapTransform
2021-06-06 18:06:01 +00:00
import com.google.android.gms.cast.framework.CastButtonFactory
import com.google.android.gms.cast.framework.CastContext
import com.google.android.gms.cast.framework.CastState
import com.google.android.material.button.MaterialButton
2021-06-14 00:00:29 +00:00
import com.lagradost.cloudstream3.*
2021-06-16 00:15:07 +00:00
import com.lagradost.cloudstream3.UIHelper.colorFromAttribute
2021-05-18 13:43:32 +00:00
import com.lagradost.cloudstream3.UIHelper.fixPaddingStatusbar
2021-06-06 18:06:01 +00:00
import com.lagradost.cloudstream3.UIHelper.isCastApiAvailable
2021-06-15 23:25:58 +00:00
import com.lagradost.cloudstream3.UIHelper.popupMenu
import com.lagradost.cloudstream3.UIHelper.popupMenuNoIcons
2021-05-18 13:43:32 +00:00
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.observe
2021-06-15 23:25:58 +00:00
import com.lagradost.cloudstream3.ui.WatchType
2021-05-22 22:25:56 +00:00
import com.lagradost.cloudstream3.ui.player.PlayerData
import com.lagradost.cloudstream3.ui.player.PlayerFragment
2021-06-14 16:58:43 +00:00
import com.lagradost.cloudstream3.utils.CastHelper.startCast
2021-06-15 16:07:20 +00:00
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
2021-05-20 15:22:28 +00:00
import com.lagradost.cloudstream3.utils.ExtractorLink
2021-05-18 13:43:32 +00:00
import jp.wasabeef.glide.transformations.BlurTransformation
import kotlinx.android.synthetic.main.fragment_result.*
2021-06-14 00:00:29 +00:00
2021-05-18 13:43:32 +00:00
2021-06-06 18:06:01 +00:00
const val MAX_SYNO_LENGH = 300
2021-05-18 13:43:32 +00:00
data class ResultEpisode(
val name: String?,
2021-06-10 15:15:14 +00:00
val poster: String?,
2021-05-18 13:43:32 +00:00
val episode: Int,
2021-05-28 13:38:06 +00:00
val season: Int?,
2021-06-14 16:58:43 +00:00
val data: String,
2021-05-18 13:43:32 +00:00
val apiName: String,
val id: Int,
2021-05-22 22:25:56 +00:00
val index: Int,
2021-06-15 16:07:20 +00:00
val position: Long, // time in MS
2021-06-10 15:15:14 +00:00
val duration: Long, // duration in MS
2021-05-18 13:43:32 +00:00
)
2021-05-16 18:28:00 +00:00
2021-06-15 16:07:20 +00:00
fun ResultEpisode.getRealPosition(): Long {
if (duration <= 0) return 0
val percentage = position * 100 / duration
if (percentage <= 5 || percentage >= 95) return 0
return position
}
fun Context.buildResultEpisode(
name: String?,
poster: String?,
episode: Int,
season: Int?,
data: String,
apiName: String,
id: Int,
index: Int,
): ResultEpisode {
val posDur = getViewPos(id)
return ResultEpisode(name,
poster,
episode,
season,
data,
apiName,
id,
index,
posDur?.position ?: 0,
posDur?.duration ?: 0)
}
2021-06-10 15:15:14 +00:00
fun ResultEpisode.getWatchProgress(): Float {
2021-06-15 16:07:20 +00:00
return position.toFloat() / duration
2021-06-10 15:15:14 +00:00
}
2021-05-16 18:28:00 +00:00
class ResultFragment : Fragment() {
2021-05-22 22:25:56 +00:00
companion object {
fun newInstance(url: String, slug: String, apiName: String) =
ResultFragment().apply {
arguments = Bundle().apply {
putString("url", url)
putString("slug", slug)
putString("apiName", apiName)
}
2021-05-18 13:43:32 +00:00
}
2021-05-22 22:25:56 +00:00
}
2021-06-15 16:07:20 +00:00
private var currentLoadingCount = 0 // THIS IS USED TO PREVENT LATE EVENTS, AFTER DISMISS WAS CLICKED
2021-05-16 18:28:00 +00:00
private lateinit var viewModel: ResultViewModel
2021-05-20 20:56:21 +00:00
private var allEpisodes: HashMap<Int, ArrayList<ExtractorLink>> = HashMap()
2021-06-15 16:07:20 +00:00
private var currentHeaderName: String? = null
private var currentEpisodes: List<ResultEpisode>? = null
2021-05-16 18:28:00 +00:00
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
2021-05-18 13:43:32 +00:00
savedInstanceState: Bundle?,
2021-05-16 18:28:00 +00:00
): View? {
viewModel =
2021-05-22 22:25:56 +00:00
ViewModelProvider(requireActivity()).get(ResultViewModel::class.java)
2021-05-16 18:28:00 +00:00
return inflater.inflate(R.layout.fragment_result, container, false)
}
2021-05-22 22:25:56 +00:00
override fun onDestroy() {
//requireActivity().viewModelStore.clear() // REMEMBER THE CLEAR
super.onDestroy()
2021-06-16 00:15:07 +00:00
activity?.let {
it.window?.navigationBarColor =
it.colorFromAttribute(R.attr.darkBackground)
}
}
override fun onResume() {
super.onResume()
activity?.let {
it.window?.navigationBarColor =
it.colorFromAttribute(R.attr.bitDarkerGrayBackground)
}
2021-05-22 22:25:56 +00:00
}
2021-06-15 16:07:20 +00:00
private var currentPoster: String? = null
2021-06-10 15:15:14 +00:00
2021-05-18 13:43:32 +00:00
@SuppressLint("SetTextI18n")
2021-05-16 18:28:00 +00:00
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
2021-05-18 13:43:32 +00:00
activity?.fixPaddingStatusbar(result_scroll)
activity?.fixPaddingStatusbar(result_barstatus)
2021-06-06 18:06:01 +00:00
if (activity?.isCastApiAvailable() == true) {
CastButtonFactory.setUpMediaRouteButton(activity, media_route_button)
val castContext = CastContext.getSharedInstance(requireActivity().applicationContext)
if (castContext.castState != CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = VISIBLE
castContext.addCastStateListener { state ->
if (media_route_button != null) {
if (state == CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = GONE else {
if (media_route_button.visibility == GONE) media_route_button.visibility = VISIBLE
}
}
}
}
2021-05-20 15:22:28 +00:00
// activity?.fixPaddingStatusbar(result_toolbar)
2021-05-18 13:43:32 +00:00
val url = arguments?.getString("url")
val slug = arguments?.getString("slug")
val apiName = arguments?.getString("apiName")
2021-06-06 18:06:01 +00:00
result_scroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, _ ->
2021-05-20 15:22:28 +00:00
if (result_poster_blur == null) return@OnScrollChangeListener
2021-06-06 18:06:01 +00:00
result_poster_blur.alpha = maxOf(0f, (0.7f - scrollY / 1000f))
result_poster_blur_holder.translationY = -scrollY.toFloat()
//result_barstatus.alpha = scrollY / 200f
//result_barstatus.visibility = if (scrollY > 0) View.VISIBLE else View.GONE§
2021-05-18 13:43:32 +00:00
})
result_toolbar.setNavigationIcon(R.drawable.ic_baseline_arrow_back_24)
result_toolbar.setNavigationOnClickListener {
activity?.onBackPressed()
}
2021-05-23 17:07:43 +00:00
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder>? = activity?.let { it ->
EpisodeAdapter(
it,
ArrayList(),
result_episodes,
) { episodeClick ->
2021-06-14 16:58:43 +00:00
//val id = episodeClick.data.id
2021-05-23 17:07:43 +00:00
val index = episodeClick.data.index
val buildInPlayer = true
2021-06-15 16:07:20 +00:00
currentLoadingCount++
2021-06-10 15:15:14 +00:00
when (episodeClick.action) {
ACTION_CHROME_CAST_EPISODE -> {
2021-06-15 16:07:20 +00:00
val currentLoad = currentLoadingCount
val builder = AlertDialog.Builder(requireContext(), R.style.AlertDialogCustomTransparent)
val customLayout = layoutInflater.inflate(R.layout.dialog_loading, null)
2021-06-10 19:43:05 +00:00
builder.setView(customLayout)
2021-06-14 16:58:43 +00:00
val dialog = builder.create()
2021-06-15 16:07:20 +00:00
2021-06-14 16:58:43 +00:00
dialog.show()
2021-06-15 16:07:20 +00:00
dialog.setOnDismissListener {
currentLoadingCount++
}
2021-06-15 23:25:58 +00:00
// Toast.makeText(activity, "Loading links", Toast.LENGTH_SHORT).show()
2021-06-10 15:15:14 +00:00
2021-06-14 16:58:43 +00:00
viewModel.loadEpisode(episodeClick.data, true) { data ->
2021-06-15 23:25:58 +00:00
if (currentLoadingCount != currentLoad) return@loadEpisode
2021-06-14 16:58:43 +00:00
dialog.dismiss()
2021-06-10 15:15:14 +00:00
when (data) {
is Resource.Failure -> {
Toast.makeText(activity, "Failed to load links", Toast.LENGTH_SHORT).show()
}
is Resource.Success -> {
2021-06-14 16:58:43 +00:00
val eps = currentEpisodes ?: return@loadEpisode
context?.startCast(
apiName ?: return@loadEpisode,
currentHeaderName,
currentPoster,
episodeClick.data.index,
eps,
2021-06-15 16:07:20 +00:00
sortUrls(data.value),
startTime = episodeClick.data.getRealPosition()
)
2021-05-23 17:07:43 +00:00
}
}
}
2021-06-10 15:15:14 +00:00
}
ACTION_PLAY_EPISODE_IN_PLAYER -> {
if (buildInPlayer) {
(requireActivity() as AppCompatActivity).supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.enter_anim,
R.anim.exit_anim,
R.anim.pop_enter,
R.anim.pop_exit)
2021-06-15 16:07:20 +00:00
.add(R.id.homeRoot,
PlayerFragment.newInstance(PlayerData(index, null, 0),
episodeClick.data.getRealPosition())
)
2021-06-10 15:15:14 +00:00
.commit()
}
}
ACTION_RELOAD_EPISODE -> {
/*viewModel.load(episodeClick.data) { res ->
2021-05-23 17:07:43 +00:00
if (res is Resource.Success) {
playEpisode(allEpisodes[id], index)
}
}*/
}
2021-06-10 15:15:14 +00:00
2021-05-23 17:07:43 +00:00
}
}
}
result_episodes.adapter = adapter
result_episodes.layoutManager = GridLayoutManager(context, 1)
2021-06-15 23:25:58 +00:00
result_bookmark_button.setOnClickListener {
it.popupMenuNoIcons(
items = WatchType.values()
.map { watchType -> Pair(watchType.internalId, watchType.stringRes) },
2021-06-16 00:15:07 +00:00
//.map { watchType -> Triple(watchType.internalId, watchType.iconRes, watchType.stringRes) },
2021-06-15 23:25:58 +00:00
) {
context?.let { localContext ->
viewModel.updateWatchStatus(localContext, WatchType.fromInternalId(this.itemId))
}
}
}
observe(viewModel.watchStatus) {
//result_bookmark_button.setIconResource(it.iconRes)
result_bookmark_button.text = getString(it.stringRes)
}
2021-05-20 20:56:21 +00:00
observe(viewModel.allEpisodes) {
allEpisodes = it
}
2021-05-22 22:25:56 +00:00
observe(viewModel.episodes) { episodes ->
2021-05-28 13:38:06 +00:00
if (result_episodes == null || result_episodes.adapter == null) return@observe
2021-06-06 18:06:01 +00:00
result_episodes_text.text = "${episodes.size} Episode${if (episodes.size == 1) "" else "s"}"
2021-06-14 16:58:43 +00:00
currentEpisodes = episodes
2021-05-22 22:25:56 +00:00
(result_episodes.adapter as EpisodeAdapter).cardList = episodes
(result_episodes.adapter as EpisodeAdapter).notifyDataSetChanged()
}
2021-05-18 13:43:32 +00:00
observe(viewModel.resultResponse) { data ->
when (data) {
is Resource.Success -> {
val d = data.value
if (d is LoadResponse) {
result_bookmark_button.text = "Watching"
2021-06-14 00:00:29 +00:00
currentHeaderName = d.name
2021-06-10 15:15:14 +00:00
currentPoster = d.posterUrl
result_openinbrower.setOnClickListener {
val i = Intent(Intent.ACTION_VIEW)
i.data = Uri.parse(d.url)
startActivity(i)
}
result_share.setOnClickListener {
val i = Intent(Intent.ACTION_SEND)
i.type = "text/plain"
i.putExtra(Intent.EXTRA_SUBJECT, d.name)
i.putExtra(Intent.EXTRA_TEXT, d.url)
startActivity(Intent.createChooser(i, d.name))
}
2021-05-18 13:43:32 +00:00
if (d.year != null) {
2021-06-15 16:07:20 +00:00
result_year.visibility = VISIBLE
2021-05-18 13:43:32 +00:00
result_year.text = d.year.toString()
} else {
2021-06-15 16:07:20 +00:00
result_year.visibility = GONE
2021-05-18 13:43:32 +00:00
}
if (d.posterUrl != null) {
val glideUrl =
GlideUrl(d.posterUrl)
2021-06-06 18:06:01 +00:00
requireContext().let {
Glide.with(it)
.load(glideUrl)
.into(result_poster)
2021-05-18 13:43:32 +00:00
Glide.with(it)
.load(glideUrl)
2021-06-06 18:06:01 +00:00
.apply(bitmapTransform(BlurTransformation(80, 3)))
2021-05-18 13:43:32 +00:00
.into(result_poster_blur)
}
}
2021-05-22 22:25:56 +00:00
fun playEpisode(data: ArrayList<ExtractorLink>?, episodeIndex: Int) {
2021-05-20 20:56:21 +00:00
if (data != null) {
2021-05-20 21:25:41 +00:00
/*
2021-05-22 22:25:56 +00:00
if (activity?.checkWrite() != true) {
activity?.requestRW()
if (activity?.checkWrite() == true) return
}
val outputDir = context!!.cacheDir
val outputFile = File.createTempFile("mirrorlist", ".m3u8", outputDir)
var text = "#EXTM3U";
for (link in data.sortedBy { -it.quality }) {
text += "\n#EXTINF:, ${link.name}\n${link.url}"
}
outputFile.writeText(text)
val VLC_PACKAGE = "org.videolan.vlc"
val VLC_INTENT_ACTION_RESULT = "org.videolan.vlc.player.result"
val VLC_COMPONENT: ComponentName =
ComponentName(VLC_PACKAGE, "org.videolan.vlc.gui.video.VideoPlayerActivity")
val REQUEST_CODE = 42
val FROM_START = -1
val FROM_PROGRESS = -2
val vlcIntent = Intent(VLC_INTENT_ACTION_RESULT)
vlcIntent.setPackage(VLC_PACKAGE)
vlcIntent.addFlags(FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
vlcIntent.addFlags(FLAG_GRANT_PREFIX_URI_PERMISSION)
vlcIntent.addFlags(FLAG_GRANT_READ_URI_PERMISSION)
vlcIntent.addFlags(FLAG_GRANT_WRITE_URI_PERMISSION)
vlcIntent.setDataAndType(FileProvider.getUriForFile(activity!!,
activity!!.applicationContext.packageName + ".provider",
outputFile), "video/*")
val startId = FROM_PROGRESS
var position = startId
if (startId == FROM_START) {
position = 1
} else if (startId == FROM_PROGRESS) {
position = 0
}
vlcIntent.putExtra("position", position)
//vlcIntent.putExtra("title", episodeName)
vlcIntent.setComponent(VLC_COMPONENT)
activity?.startActivityForResult(vlcIntent, REQUEST_CODE)
*/
*/
2021-05-20 20:56:21 +00:00
}
}
2021-05-22 22:25:56 +00:00
if (d.plot != null) {
var syno = d.plot!!
if (syno.length > MAX_SYNO_LENGH) {
syno = syno.substring(0, MAX_SYNO_LENGH) + "..."
2021-05-18 13:43:32 +00:00
}
2021-05-22 22:25:56 +00:00
result_descript.setOnClickListener {
val builder: AlertDialog.Builder = AlertDialog.Builder(requireContext())
builder.setMessage(d.plot).setTitle("Synopsis")
.show()
2021-05-18 13:43:32 +00:00
}
2021-05-22 22:25:56 +00:00
result_descript.text = syno
2021-05-18 13:43:32 +00:00
} else {
2021-05-22 22:25:56 +00:00
result_descript.text = "No Plot found"
}
2021-06-06 18:06:01 +00:00
result_tag.removeAllViews()
2021-06-15 16:07:20 +00:00
result_tag_holder.visibility = GONE
result_status.visibility = GONE
2021-06-06 18:06:01 +00:00
2021-05-22 22:25:56 +00:00
when (d) {
is AnimeLoadResponse -> {
2021-06-15 16:07:20 +00:00
result_status.visibility = VISIBLE
2021-06-06 18:06:01 +00:00
result_status.text = when (d.showStatus) {
null -> ""
ShowStatus.Ongoing -> "Ongoing"
ShowStatus.Completed -> "Completed"
}
// val preferEnglish = true
//val titleName = (if (preferEnglish) d.engName else d.japName) ?: d.name
val titleName = d.name
2021-05-22 22:25:56 +00:00
result_title.text = titleName
result_toolbar.title = titleName
2021-06-06 18:06:01 +00:00
if (d.tags == null) {
2021-06-15 16:07:20 +00:00
result_tag_holder.visibility = GONE
2021-06-06 18:06:01 +00:00
} else {
2021-06-15 16:07:20 +00:00
result_tag_holder.visibility = VISIBLE
2021-06-06 18:06:01 +00:00
for ((index, tag) in d.tags.withIndex()) {
val viewBtt = layoutInflater.inflate(R.layout.result_tag, null)
val btt = viewBtt.findViewById<MaterialButton>(R.id.result_tag_card)
btt.text = tag
result_tag.addView(viewBtt, index)
}
}
2021-05-22 22:25:56 +00:00
}
else -> result_title.text = d.name
2021-05-18 13:43:32 +00:00
}
}
}
is Resource.Failure -> {
}
}
}
if (viewModel.resultResponse.value == null && apiName != null && slug != null)
2021-06-15 16:07:20 +00:00
viewModel.load(requireContext(), slug, apiName)
2021-05-16 18:28:00 +00:00
}
}