forked from recloudstream/cloudstream
idk anymore tbh
This commit is contained in:
parent
7b81ec01b5
commit
8a49401dd0
15 changed files with 607 additions and 152 deletions
|
@ -68,4 +68,10 @@ dependencies {
|
||||||
implementation 'jp.wasabeef:glide-transformations:4.0.0'
|
implementation 'jp.wasabeef:glide-transformations:4.0.0'
|
||||||
|
|
||||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
|
||||||
|
|
||||||
|
// Exoplayer
|
||||||
|
implementation 'com.google.android.exoplayer:exoplayer:2.14.0'
|
||||||
|
implementation 'com.google.android.exoplayer:extension-cast:2.14.0'
|
||||||
|
implementation "com.google.android.exoplayer:extension-mediasession:2.14.0"
|
||||||
|
implementation "com.google.android.exoplayer:extension-leanback:2.14.0"
|
||||||
}
|
}
|
|
@ -24,7 +24,7 @@ object APIHolder {
|
||||||
ShiroProvider()
|
ShiroProvider()
|
||||||
)
|
)
|
||||||
|
|
||||||
fun getApiFromName(apiName: String): MainAPI {
|
fun getApiFromName(apiName: String?): MainAPI {
|
||||||
for (api in apis) {
|
for (api in apis) {
|
||||||
if (apiName == api.name)
|
if (apiName == api.name)
|
||||||
return api
|
return api
|
||||||
|
@ -53,7 +53,7 @@ abstract class MainAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
// callback is fired once a link is found, will return true if method is executed successfully
|
// callback is fired once a link is found, will return true if method is executed successfully
|
||||||
open fun loadLinks(data: Any, isCasting : Boolean, callback: (ExtractorLink) -> Unit): Boolean {
|
open fun loadLinks(data: Any, isCasting: Boolean, callback: (ExtractorLink) -> Unit): Boolean {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,8 +81,8 @@ enum class ShowStatus {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class DubStatus {
|
enum class DubStatus {
|
||||||
HasDub,
|
Dubbed,
|
||||||
HasSub,
|
Subbed,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class TvType {
|
enum class TvType {
|
||||||
|
@ -148,6 +148,7 @@ interface LoadResponse {
|
||||||
val type: TvType
|
val type: TvType
|
||||||
val posterUrl: String?
|
val posterUrl: String?
|
||||||
val year: Int?
|
val year: Int?
|
||||||
|
val plot: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
data class AnimeLoadResponse(
|
data class AnimeLoadResponse(
|
||||||
|
@ -165,8 +166,8 @@ data class AnimeLoadResponse(
|
||||||
val subEpisodes: ArrayList<Any>?,
|
val subEpisodes: ArrayList<Any>?,
|
||||||
val showStatus: ShowStatus?,
|
val showStatus: ShowStatus?,
|
||||||
|
|
||||||
|
override val plot: String?,
|
||||||
val tags: ArrayList<String>?,
|
val tags: ArrayList<String>?,
|
||||||
val plot: String?,
|
|
||||||
val synonyms: ArrayList<String>?,
|
val synonyms: ArrayList<String>?,
|
||||||
|
|
||||||
val malId: Int?,
|
val malId: Int?,
|
||||||
|
@ -182,6 +183,7 @@ data class MovieLoadResponse(
|
||||||
|
|
||||||
override val posterUrl: String?,
|
override val posterUrl: String?,
|
||||||
override val year: Int?,
|
override val year: Int?,
|
||||||
|
override val plot: String?,
|
||||||
|
|
||||||
val imdbId: Int?,
|
val imdbId: Int?,
|
||||||
) : LoadResponse
|
) : LoadResponse
|
||||||
|
@ -195,6 +197,7 @@ data class TvSeriesLoadResponse(
|
||||||
|
|
||||||
override val posterUrl: String?,
|
override val posterUrl: String?,
|
||||||
override val year: Int?,
|
override val year: Int?,
|
||||||
|
override val plot: String?,
|
||||||
|
|
||||||
val showStatus: ShowStatus?,
|
val showStatus: ShowStatus?,
|
||||||
val imdbId: Int?,
|
val imdbId: Int?,
|
||||||
|
|
|
@ -3,13 +3,23 @@ package com.lagradost.cloudstream3
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.lifecycle.ViewModelStore
|
||||||
|
import androidx.lifecycle.ViewModelStoreOwner
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.navigation.ui.AppBarConfiguration
|
import androidx.navigation.ui.AppBarConfiguration
|
||||||
import androidx.navigation.ui.setupWithNavController
|
import androidx.navigation.ui.setupWithNavController
|
||||||
import com.lagradost.cloudstream3.UIHelper.checkWrite
|
import com.lagradost.cloudstream3.UIHelper.checkWrite
|
||||||
import com.lagradost.cloudstream3.UIHelper.requestRW
|
import com.lagradost.cloudstream3.UIHelper.requestRW
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {/*, ViewModelStoreOwner {
|
||||||
|
private val appViewModelStore: ViewModelStore by lazy {
|
||||||
|
ViewModelStore()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getViewModelStore(): ViewModelStore {
|
||||||
|
return appViewModelStore
|
||||||
|
}*/
|
||||||
|
|
||||||
private fun AppCompatActivity.backPressed(): Boolean {
|
private fun AppCompatActivity.backPressed(): Boolean {
|
||||||
val currentFragment = supportFragmentManager.fragments.last {
|
val currentFragment = supportFragmentManager.fragments.last {
|
||||||
it.isVisible
|
it.isVisible
|
||||||
|
|
|
@ -36,7 +36,7 @@ object UIHelper {
|
||||||
this.runOnUiThread {
|
this.runOnUiThread {
|
||||||
this.supportFragmentManager.beginTransaction()
|
this.supportFragmentManager.beginTransaction()
|
||||||
.setCustomAnimations(R.anim.enter_anim, R.anim.exit_anim, R.anim.pop_enter, R.anim.pop_exit)
|
.setCustomAnimations(R.anim.enter_anim, R.anim.exit_anim, R.anim.pop_enter, R.anim.pop_exit)
|
||||||
.add(R.id.homeRoot, ResultFragment().newInstance(url, slug, apiName))
|
.add(R.id.homeRoot, ResultFragment.newInstance(url, slug, apiName))
|
||||||
.commit()
|
.commit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,9 +149,9 @@ class ShiroProvider : MainAPI() {
|
||||||
val set: EnumSet<DubStatus> = EnumSet.noneOf(DubStatus::class.java)
|
val set: EnumSet<DubStatus> = EnumSet.noneOf(DubStatus::class.java)
|
||||||
|
|
||||||
if (isDubbed)
|
if (isDubbed)
|
||||||
set.add(DubStatus.HasDub)
|
set.add(DubStatus.Dubbed)
|
||||||
else
|
else
|
||||||
set.add(DubStatus.HasSub)
|
set.add(DubStatus.Subbed)
|
||||||
val episodeCount = i.episodeCount.toInt()
|
val episodeCount = i.episodeCount.toInt()
|
||||||
|
|
||||||
returnValue.add(AnimeSearchResponse(
|
returnValue.add(AnimeSearchResponse(
|
||||||
|
|
|
@ -0,0 +1,172 @@
|
||||||
|
package com.lagradost.cloudstream3.ui.player
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.animation.AlphaAnimation
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||||
|
import com.fasterxml.jackson.databind.json.JsonMapper
|
||||||
|
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||||
|
import com.google.android.exoplayer2.SimpleExoPlayer
|
||||||
|
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout
|
||||||
|
import com.lagradost.cloudstream3.LoadResponse
|
||||||
|
import com.lagradost.cloudstream3.R
|
||||||
|
import com.lagradost.cloudstream3.mvvm.Resource
|
||||||
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
|
import com.lagradost.cloudstream3.ui.result.ResultEpisode
|
||||||
|
import com.lagradost.cloudstream3.ui.result.ResultViewModel
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStore
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
||||||
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
|
import java.io.FileDescriptor
|
||||||
|
import java.io.PrintWriter
|
||||||
|
|
||||||
|
const val STATE_RESUME_WINDOW = "resumeWindow"
|
||||||
|
const val STATE_RESUME_POSITION = "resumePosition"
|
||||||
|
const val STATE_PLAYER_FULLSCREEN = "playerFullscreen"
|
||||||
|
const val STATE_PLAYER_PLAYING = "playerOnPlay"
|
||||||
|
const val ACTION_MEDIA_CONTROL = "media_control"
|
||||||
|
const val EXTRA_CONTROL_TYPE = "control_type"
|
||||||
|
const val PLAYBACK_SPEED = "playback_speed"
|
||||||
|
const val RESIZE_MODE_KEY = "resize_mode" // Last used resize mode
|
||||||
|
const val PLAYBACK_SPEED_KEY = "playback_speed" // Last used playback speed
|
||||||
|
|
||||||
|
enum class PlayerEventType(val value: Int) {
|
||||||
|
Stop(-1),
|
||||||
|
Pause(0),
|
||||||
|
Play(1),
|
||||||
|
SeekForward(2),
|
||||||
|
SeekBack(3),
|
||||||
|
SkipCurrentChapter(4),
|
||||||
|
NextEpisode(5),
|
||||||
|
PlayPauseToggle(6)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
data class PlayerData(
|
||||||
|
val id: Int, // UNIQUE IDENTIFIER, USED FOR SET TIME, HASH OF slug+episodeIndex
|
||||||
|
val titleName: String, // TITLE NAME
|
||||||
|
val episodeName: String?, // EPISODE NAME, NULL IF MOVIE
|
||||||
|
val episodeIndex: Int?, // EPISODE INDEX, NULL IF MOVIE
|
||||||
|
val seasonIndex : Int?, // SEASON INDEX IF IT IS FOUND, EPISODE CAN BE GIVEN BUT SEASON IS NOT GUARANTEED
|
||||||
|
val episodes : Int?, // MAX EPISODE
|
||||||
|
//val seasons : Int?, // SAME AS SEASON INDEX, NOT GUARANTEED, SET TO 1
|
||||||
|
)*/
|
||||||
|
data class PlayerData(
|
||||||
|
val episodeIndex: Int,
|
||||||
|
)
|
||||||
|
|
||||||
|
class PlayerFragment : Fragment() {
|
||||||
|
private val mapper = JsonMapper.builder().addModule(KotlinModule())
|
||||||
|
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()
|
||||||
|
|
||||||
|
private var isFullscreen = false
|
||||||
|
private var isPlayerPlaying = true
|
||||||
|
private lateinit var viewModel: ResultViewModel
|
||||||
|
private lateinit var playerData: PlayerData
|
||||||
|
private var isLoading = true
|
||||||
|
private lateinit var exoPlayer: SimpleExoPlayer
|
||||||
|
|
||||||
|
private fun seekTime(time: Long) {
|
||||||
|
exoPlayer.seekTo(maxOf(minOf(exoPlayer.currentPosition + time, exoPlayer.duration), 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun releasePlayer() {
|
||||||
|
if (this::exoPlayer.isInitialized) {
|
||||||
|
exoPlayer.release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
if (this::exoPlayer.isInitialized) {
|
||||||
|
outState.putInt(STATE_RESUME_WINDOW, exoPlayer.currentWindowIndex)
|
||||||
|
outState.putLong(STATE_RESUME_POSITION, exoPlayer.currentPosition)
|
||||||
|
}
|
||||||
|
outState.putBoolean(STATE_PLAYER_FULLSCREEN, isFullscreen)
|
||||||
|
outState.putBoolean(STATE_PLAYER_PLAYING, isPlayerPlaying)
|
||||||
|
outState.putInt(RESIZE_MODE_KEY, resizeMode)
|
||||||
|
outState.putFloat(PLAYBACK_SPEED, playbackSpeed)
|
||||||
|
savePos()
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun newInstance(data: PlayerData) =
|
||||||
|
PlayerFragment().apply {
|
||||||
|
arguments = Bundle().apply {
|
||||||
|
//println(data)
|
||||||
|
putString("data", mapper.writeValueAsString(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun savePos() {
|
||||||
|
if (this::exoPlayer.isInitialized) {
|
||||||
|
/*if (
|
||||||
|
&& exoPlayer.duration > 0 && exoPlayer.currentPosition > 0
|
||||||
|
) {
|
||||||
|
setViewPosDur(data!!, exoPlayer.currentPosition, exoPlayer.duration)
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val resizeModes = listOf(
|
||||||
|
AspectRatioFrameLayout.RESIZE_MODE_FIT,
|
||||||
|
AspectRatioFrameLayout.RESIZE_MODE_FILL,
|
||||||
|
AspectRatioFrameLayout.RESIZE_MODE_ZOOM,
|
||||||
|
)
|
||||||
|
|
||||||
|
private var resizeMode = 0
|
||||||
|
private var playbackSpeed = 0f
|
||||||
|
private var allEpisodes: HashMap<Int, ArrayList<ExtractorLink>> = HashMap()
|
||||||
|
private var episodes: ArrayList<ResultEpisode> = ArrayList()
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
resizeMode = requireContext().getKey(RESIZE_MODE_KEY, 0)!!
|
||||||
|
playbackSpeed = requireContext().getKey(PLAYBACK_SPEED_KEY, 1f)!!
|
||||||
|
|
||||||
|
observe(viewModel.episodes) { _episodes ->
|
||||||
|
episodes = _episodes
|
||||||
|
if (isLoading) {
|
||||||
|
if (playerData.episodeIndex > 0 && playerData.episodeIndex < episodes.size) {
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// WHAT THE FUCK DID YOU DO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
observe(viewModel.allEpisodes) { _allEpisodes ->
|
||||||
|
allEpisodes = _allEpisodes
|
||||||
|
}
|
||||||
|
|
||||||
|
observe(viewModel.resultResponse) { data ->
|
||||||
|
when (data) {
|
||||||
|
is Resource.Success -> {
|
||||||
|
val d = data.value
|
||||||
|
if (d is LoadResponse) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Resource.Failure -> {
|
||||||
|
//WTF, HOW DID YOU EVEN GET HERE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println(allEpisodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
viewModel = ViewModelProvider(requireActivity()).get(ResultViewModel::class.java)
|
||||||
|
arguments?.getString("data")?.let {
|
||||||
|
playerData = mapper.readValue(it, PlayerData::class.java)
|
||||||
|
}
|
||||||
|
return inflater.inflate(R.layout.fragment_player, container, false)
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
|
@ -24,14 +25,15 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.load.model.GlideUrl
|
import com.bumptech.glide.load.model.GlideUrl
|
||||||
import com.bumptech.glide.request.RequestOptions.bitmapTransform
|
import com.bumptech.glide.request.RequestOptions.bitmapTransform
|
||||||
import com.lagradost.cloudstream3.AnimeLoadResponse
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.LoadResponse
|
|
||||||
import com.lagradost.cloudstream3.R
|
|
||||||
import com.lagradost.cloudstream3.UIHelper.checkWrite
|
import com.lagradost.cloudstream3.UIHelper.checkWrite
|
||||||
import com.lagradost.cloudstream3.UIHelper.fixPaddingStatusbar
|
import com.lagradost.cloudstream3.UIHelper.fixPaddingStatusbar
|
||||||
|
import com.lagradost.cloudstream3.UIHelper.loadResult
|
||||||
import com.lagradost.cloudstream3.UIHelper.requestRW
|
import com.lagradost.cloudstream3.UIHelper.requestRW
|
||||||
import com.lagradost.cloudstream3.mvvm.Resource
|
import com.lagradost.cloudstream3.mvvm.Resource
|
||||||
import com.lagradost.cloudstream3.mvvm.observe
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
|
import com.lagradost.cloudstream3.ui.player.PlayerData
|
||||||
|
import com.lagradost.cloudstream3.ui.player.PlayerFragment
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import jp.wasabeef.glide.transformations.BlurTransformation
|
import jp.wasabeef.glide.transformations.BlurTransformation
|
||||||
import kotlinx.android.synthetic.main.fragment_result.*
|
import kotlinx.android.synthetic.main.fragment_result.*
|
||||||
|
@ -46,18 +48,22 @@ data class ResultEpisode(
|
||||||
val data: Any,
|
val data: Any,
|
||||||
val apiName: String,
|
val apiName: String,
|
||||||
val id: Int,
|
val id: Int,
|
||||||
|
val index: Int,
|
||||||
val watchProgress: Float, // 0-1
|
val watchProgress: Float, // 0-1
|
||||||
)
|
)
|
||||||
|
|
||||||
class ResultFragment : Fragment() {
|
class ResultFragment : Fragment() {
|
||||||
fun newInstance(url: String, slug: String, apiName: String) =
|
companion object {
|
||||||
ResultFragment().apply {
|
fun newInstance(url: String, slug: String, apiName: String) =
|
||||||
arguments = Bundle().apply {
|
ResultFragment().apply {
|
||||||
putString("url", url)
|
arguments = Bundle().apply {
|
||||||
putString("slug", slug)
|
putString("url", url)
|
||||||
putString("apiName", apiName)
|
putString("slug", slug)
|
||||||
|
putString("apiName", apiName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private lateinit var viewModel: ResultViewModel
|
private lateinit var viewModel: ResultViewModel
|
||||||
private var allEpisodes: HashMap<Int, ArrayList<ExtractorLink>> = HashMap()
|
private var allEpisodes: HashMap<Int, ArrayList<ExtractorLink>> = HashMap()
|
||||||
|
@ -68,11 +74,15 @@ class ResultFragment : Fragment() {
|
||||||
savedInstanceState: Bundle?,
|
savedInstanceState: Bundle?,
|
||||||
): View? {
|
): View? {
|
||||||
viewModel =
|
viewModel =
|
||||||
ViewModelProvider(this).get(ResultViewModel::class.java)
|
ViewModelProvider(requireActivity()).get(ResultViewModel::class.java)
|
||||||
|
|
||||||
return inflater.inflate(R.layout.fragment_result, container, false)
|
return inflater.inflate(R.layout.fragment_result, container, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
//requireActivity().viewModelStore.clear() // REMEMBER THE CLEAR
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
@ -100,6 +110,11 @@ class ResultFragment : Fragment() {
|
||||||
allEpisodes = it
|
allEpisodes = it
|
||||||
}
|
}
|
||||||
|
|
||||||
|
observe(viewModel.episodes) { episodes ->
|
||||||
|
(result_episodes.adapter as EpisodeAdapter).cardList = episodes
|
||||||
|
(result_episodes.adapter as EpisodeAdapter).notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
observe(viewModel.resultResponse) { data ->
|
observe(viewModel.resultResponse) { data ->
|
||||||
when (data) {
|
when (data) {
|
||||||
is Resource.Success -> {
|
is Resource.Success -> {
|
||||||
|
@ -130,66 +145,60 @@ class ResultFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun playEpisode(data: ArrayList<ExtractorLink>?) {
|
fun playEpisode(data: ArrayList<ExtractorLink>?, episodeIndex: Int) {
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
if (activity?.checkWrite() != true) {
|
|
||||||
activity?.requestRW()
|
|
||||||
if (activity?.checkWrite() == true) return
|
|
||||||
}
|
|
||||||
|
|
||||||
val outputDir = context!!.cacheDir // context being the Activity pointer
|
|
||||||
val outputFile = File.createTempFile("mirrorlist", ".m3u8", outputDir)
|
|
||||||
var text = "#EXTM3U";
|
|
||||||
for (d in data.sortedBy { -it.quality }) {
|
|
||||||
text += "\n#EXTINF:, ${d.name}\n${d.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.setDataAndTypeAndNormalize(outputFile.toUri(), "video/*")
|
|
||||||
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)
|
|
||||||
/*
|
/*
|
||||||
if (subFile != null) {
|
if (activity?.checkWrite() != true) {
|
||||||
val sfile: Unit = Android.Net.Uri.FromFile(subFile)
|
activity?.requestRW()
|
||||||
vlcIntent.PutExtra("subtitles_location", sfile.Path)
|
if (activity?.checkWrite() == true) return
|
||||||
//vlcIntent.PutExtra("sub_mrl", "file://" sfile.Path);
|
}
|
||||||
//vlcIntent.PutExtra("subtitles_location", "file:///storage/emulated/0/Download/mirrorlist.srt");
|
|
||||||
}*/
|
|
||||||
|
|
||||||
vlcIntent.setComponent(VLC_COMPONENT)
|
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
|
||||||
|
|
||||||
//lastId = episodeId
|
val FROM_START = -1
|
||||||
activity?.startActivityForResult(vlcIntent, REQUEST_CODE)
|
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)
|
||||||
|
*/
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,22 +209,36 @@ class ResultFragment : Fragment() {
|
||||||
result_episodes,
|
result_episodes,
|
||||||
) { episodeClick ->
|
) { episodeClick ->
|
||||||
val id = episodeClick.data.id
|
val id = episodeClick.data.id
|
||||||
when (episodeClick.action) {
|
val index = episodeClick.data.index
|
||||||
ACTION_PLAY_EPISODE -> {
|
val buildInPlayer = true
|
||||||
if (allEpisodes.containsKey(id)) {
|
if (buildInPlayer) {
|
||||||
playEpisode(allEpisodes[id])
|
(requireActivity() as AppCompatActivity).supportFragmentManager.beginTransaction()
|
||||||
} else {
|
.setCustomAnimations(R.anim.enter_anim,
|
||||||
viewModel.loadEpisode(episodeClick.data) { res ->
|
R.anim.exit_anim,
|
||||||
if (res is Resource.Success) {
|
R.anim.pop_enter,
|
||||||
playEpisode(allEpisodes[id])
|
R.anim.pop_exit)
|
||||||
|
.add(R.id.homeRoot, PlayerFragment.newInstance(PlayerData(index)))
|
||||||
|
.commit()
|
||||||
|
} else {
|
||||||
|
when (episodeClick.action) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
ACTION_PLAY_EPISODE -> {
|
||||||
|
if (allEpisodes.containsKey(id)) {
|
||||||
|
playEpisode(allEpisodes[id], index)
|
||||||
|
} else {
|
||||||
|
viewModel.loadEpisode(episodeClick.data) { res ->
|
||||||
|
if (res is Resource.Success) {
|
||||||
|
playEpisode(allEpisodes[id], index)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
ACTION_RELOAD_EPISODE -> viewModel.loadEpisode(episodeClick.data) { res ->
|
||||||
ACTION_RELOAD_EPISODE -> viewModel.loadEpisode(episodeClick.data) { res ->
|
if (res is Resource.Success) {
|
||||||
if (res is Resource.Success) {
|
playEpisode(allEpisodes[id], index)
|
||||||
playEpisode(allEpisodes[id])
|
}
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,47 +247,31 @@ class ResultFragment : Fragment() {
|
||||||
result_episodes.adapter = adapter
|
result_episodes.adapter = adapter
|
||||||
result_episodes.layoutManager = GridLayoutManager(context, 1)
|
result_episodes.layoutManager = GridLayoutManager(context, 1)
|
||||||
|
|
||||||
if (d is AnimeLoadResponse) {
|
if (d.plot != null) {
|
||||||
val preferEnglish = true
|
var syno = d.plot!!
|
||||||
val titleName = (if (preferEnglish) d.engName else d.japName) ?: d.name
|
if (syno.length > MAX_SYNO_LENGH) {
|
||||||
result_title.text = titleName
|
syno = syno.substring(0, MAX_SYNO_LENGH) + "..."
|
||||||
result_toolbar.title = titleName
|
|
||||||
|
|
||||||
if (d.plot != null) {
|
|
||||||
var syno = d.plot
|
|
||||||
if (syno.length > MAX_SYNO_LENGH) {
|
|
||||||
syno = syno.substring(0, MAX_SYNO_LENGH) + "..."
|
|
||||||
}
|
|
||||||
result_descript.setOnClickListener {
|
|
||||||
val builder: AlertDialog.Builder = AlertDialog.Builder(requireContext())
|
|
||||||
builder.setMessage(d.plot).setTitle("Synopsis")
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
result_descript.text = syno
|
|
||||||
} else {
|
|
||||||
result_descript.text = "No Plot found"
|
|
||||||
}
|
}
|
||||||
|
result_descript.setOnClickListener {
|
||||||
result_tags.text = (d.tags ?: ArrayList()).joinToString(separator = " | ")
|
val builder: AlertDialog.Builder = AlertDialog.Builder(requireContext())
|
||||||
val isDub = d.dubEpisodes != null && d.dubEpisodes.size > 0
|
builder.setMessage(d.plot).setTitle("Synopsis")
|
||||||
val dataList = (if (isDub) d.dubEpisodes else d.subEpisodes)
|
.show()
|
||||||
if (dataList != null && apiName != null) {
|
|
||||||
val episodes = ArrayList<ResultEpisode>()
|
|
||||||
for ((index, i) in dataList.withIndex()) {
|
|
||||||
episodes.add(ResultEpisode(
|
|
||||||
null, // TODO ADD NAMES
|
|
||||||
index + 1, //TODO MAKE ABLE TO NOT HAVE SOME EPISODE
|
|
||||||
i,
|
|
||||||
apiName,
|
|
||||||
(slug + index).hashCode(),
|
|
||||||
0f,//(index * 0.1f),//TODO TEST; REMOVE
|
|
||||||
))
|
|
||||||
}
|
|
||||||
(result_episodes.adapter as EpisodeAdapter).cardList = episodes
|
|
||||||
(result_episodes.adapter as EpisodeAdapter).notifyDataSetChanged()
|
|
||||||
}
|
}
|
||||||
|
result_descript.text = syno
|
||||||
} else {
|
} else {
|
||||||
result_title.text = d.name
|
result_descript.text = "No Plot found"
|
||||||
|
}
|
||||||
|
|
||||||
|
when (d) {
|
||||||
|
is AnimeLoadResponse -> {
|
||||||
|
val preferEnglish = true
|
||||||
|
val titleName = (if (preferEnglish) d.engName else d.japName) ?: d.name
|
||||||
|
result_title.text = titleName
|
||||||
|
result_toolbar.title = titleName
|
||||||
|
|
||||||
|
result_tags.text = (d.tags ?: ArrayList()).joinToString(separator = " | ")
|
||||||
|
}
|
||||||
|
else -> result_title.text = d.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,8 @@ import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.lagradost.cloudstream3.APIHolder
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiFromName
|
import com.lagradost.cloudstream3.APIHolder.getApiFromName
|
||||||
import com.lagradost.cloudstream3.MainAPI
|
|
||||||
import com.lagradost.cloudstream3.mvvm.Resource
|
import com.lagradost.cloudstream3.mvvm.Resource
|
||||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
|
@ -14,31 +13,110 @@ import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class ResultViewModel : ViewModel() {
|
class ResultViewModel : ViewModel() {
|
||||||
private val _resultResponse: MutableLiveData<Resource<Any?>> = MutableLiveData()
|
private val _resultResponse: MutableLiveData<Resource<Any?>> = MutableLiveData()
|
||||||
|
private val _episodes: MutableLiveData<ArrayList<ResultEpisode>> = MutableLiveData()
|
||||||
val resultResponse: LiveData<Resource<Any?>> get() = _resultResponse
|
val resultResponse: LiveData<Resource<Any?>> get() = _resultResponse
|
||||||
|
val episodes: LiveData<ArrayList<ResultEpisode>> get() = _episodes
|
||||||
|
private val dubStatus: MutableLiveData<DubStatus> = MutableLiveData()
|
||||||
|
|
||||||
fun load(url: String, apiName: String) = viewModelScope.launch {
|
fun load(url: String, apiName: String) = viewModelScope.launch {
|
||||||
|
_apiName.postValue(apiName)
|
||||||
val data = safeApiCall {
|
val data = safeApiCall {
|
||||||
getApiFromName(apiName).load(url)
|
getApiFromName(apiName).load(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
_resultResponse.postValue(data)
|
_resultResponse.postValue(data)
|
||||||
}
|
|
||||||
|
|
||||||
private val _allEpisodes: MutableLiveData<HashMap<Int, ArrayList<ExtractorLink>>> = MutableLiveData(HashMap())
|
when (data) {
|
||||||
val allEpisodes: LiveData<HashMap<Int, ArrayList<ExtractorLink>>> get() = _allEpisodes
|
is Resource.Success -> {
|
||||||
|
val d = data.value
|
||||||
|
if (d is LoadResponse) {
|
||||||
|
when (d) {
|
||||||
|
is AnimeLoadResponse -> {
|
||||||
|
val isDub = d.dubEpisodes != null && d.dubEpisodes.size > 0
|
||||||
|
dubStatus.postValue(if (isDub) DubStatus.Dubbed else DubStatus.Subbed)
|
||||||
|
|
||||||
|
val dataList = (if (isDub) d.dubEpisodes else d.subEpisodes)
|
||||||
|
|
||||||
|
if (dataList != null) {
|
||||||
|
val episodes = ArrayList<ResultEpisode>()
|
||||||
|
for ((index, i) in dataList.withIndex()) {
|
||||||
|
episodes.add(ResultEpisode(
|
||||||
|
null, // TODO ADD NAMES
|
||||||
|
index + 1, //TODO MAKE ABLE TO NOT HAVE SOME EPISODE
|
||||||
|
i,
|
||||||
|
apiName,
|
||||||
|
(d.url + index).hashCode(),
|
||||||
|
index,
|
||||||
|
0f,//(index * 0.1f),//TODO TEST; REMOVE
|
||||||
|
))
|
||||||
|
}
|
||||||
|
_episodes.postValue(episodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
is TvSeriesLoadResponse -> {
|
||||||
|
val episodes = ArrayList<ResultEpisode>()
|
||||||
|
for ((index, i) in d.episodes.withIndex()) {
|
||||||
|
episodes.add(ResultEpisode(
|
||||||
|
null, // TODO ADD NAMES
|
||||||
|
index + 1, //TODO MAKE ABLE TO NOT HAVE SOME EPISODE
|
||||||
|
i,
|
||||||
|
apiName,
|
||||||
|
(d.url + index).hashCode(),
|
||||||
|
index,
|
||||||
|
0f,//(index * 0.1f),//TODO TEST; REMOVE
|
||||||
|
))
|
||||||
|
}
|
||||||
|
_episodes.postValue(episodes)
|
||||||
|
}
|
||||||
|
is MovieLoadResponse -> {
|
||||||
|
_episodes.postValue(arrayListOf(ResultEpisode(null,
|
||||||
|
0,
|
||||||
|
d.movieUrl,
|
||||||
|
d.apiName,
|
||||||
|
(d.url).hashCode(),
|
||||||
|
0,
|
||||||
|
0f)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
|
||||||
fun loadEpisode(episode: ResultEpisode, callback: (Resource<Boolean>) -> Unit) = viewModelScope.launch {
|
|
||||||
if (_allEpisodes.value?.contains(episode.id) == true) {
|
|
||||||
_allEpisodes.value?.remove(episode.id)
|
|
||||||
}
|
|
||||||
val links = ArrayList<ExtractorLink>()
|
|
||||||
val data = safeApiCall {
|
|
||||||
getApiFromName(episode.apiName).loadLinks(episode.data, true) { //TODO IMPLEMENT CASTING
|
|
||||||
links.add(it)
|
|
||||||
_allEpisodes.value?.set(episode.id, links)
|
|
||||||
// _allEpisodes.value?.get(episode.id)?.add(it)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
callback.invoke(data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val _allEpisodes: MutableLiveData<HashMap<Int, ArrayList<ExtractorLink>>> =
|
||||||
|
MutableLiveData(HashMap()) // LOOKUP BY ID
|
||||||
|
|
||||||
|
val allEpisodes: LiveData<HashMap<Int, ArrayList<ExtractorLink>>> get() = _allEpisodes
|
||||||
|
|
||||||
|
private lateinit var _apiName: MutableLiveData<String>
|
||||||
|
|
||||||
|
fun loadEpisode(episode: ResultEpisode, callback: (Resource<Boolean>) -> Unit) {
|
||||||
|
loadEpisode(episode.id, episode.data, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadEpisode(id: Int, data: Any, callback: (Resource<Boolean>) -> Unit) =
|
||||||
|
viewModelScope.launch {
|
||||||
|
if (_allEpisodes.value?.contains(id) == true) {
|
||||||
|
_allEpisodes.value?.remove(id)
|
||||||
|
}
|
||||||
|
val links = ArrayList<ExtractorLink>()
|
||||||
|
val data = safeApiCall {
|
||||||
|
getApiFromName(_apiName.value).loadLinks(data, true) { //TODO IMPLEMENT CASTING
|
||||||
|
links.add(it)
|
||||||
|
_allEpisodes.value?.set(id, links)
|
||||||
|
// _allEpisodes.value?.get(episode.id)?.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback.invoke(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadIndex(index: Int): ResultEpisode? {
|
||||||
|
return episodes.value?.get(index)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
package com.lagradost.cloudstream3.ui.search
|
package com.lagradost.cloudstream3.ui.search
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
@ -9,7 +8,6 @@ import android.widget.FrameLayout
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.load.model.GlideUrl
|
import com.bumptech.glide.load.model.GlideUrl
|
||||||
|
@ -19,7 +17,6 @@ import com.lagradost.cloudstream3.UIHelper.getGridIsCompact
|
||||||
import com.lagradost.cloudstream3.UIHelper.loadResult
|
import com.lagradost.cloudstream3.UIHelper.loadResult
|
||||||
import com.lagradost.cloudstream3.UIHelper.toPx
|
import com.lagradost.cloudstream3.UIHelper.toPx
|
||||||
import com.lagradost.cloudstream3.ui.AutofitRecyclerView
|
import com.lagradost.cloudstream3.ui.AutofitRecyclerView
|
||||||
import kotlinx.android.synthetic.main.search_result_compact.view.*
|
|
||||||
import kotlinx.android.synthetic.main.search_result_compact.view.backgroundCard
|
import kotlinx.android.synthetic.main.search_result_compact.view.backgroundCard
|
||||||
import kotlinx.android.synthetic.main.search_result_compact.view.imageText
|
import kotlinx.android.synthetic.main.search_result_compact.view.imageText
|
||||||
import kotlinx.android.synthetic.main.search_result_compact.view.imageView
|
import kotlinx.android.synthetic.main.search_result_compact.view.imageView
|
||||||
|
@ -117,10 +114,10 @@ class SearchAdapter(
|
||||||
is AnimeSearchResponse -> {
|
is AnimeSearchResponse -> {
|
||||||
if (card.dubStatus?.size == 1) {
|
if (card.dubStatus?.size == 1) {
|
||||||
//search_result_lang?.visibility = View.VISIBLE
|
//search_result_lang?.visibility = View.VISIBLE
|
||||||
if (card.dubStatus.contains(DubStatus.HasDub)) {
|
if (card.dubStatus.contains(DubStatus.Dubbed)) {
|
||||||
text_is_dub?.visibility = View.VISIBLE
|
text_is_dub?.visibility = View.VISIBLE
|
||||||
//search_result_lang?.setColorFilter(ContextCompat.getColor(activity, R.color.dubColor))
|
//search_result_lang?.setColorFilter(ContextCompat.getColor(activity, R.color.dubColor))
|
||||||
} else if (card.dubStatus.contains(DubStatus.HasSub)) {
|
} else if (card.dubStatus.contains(DubStatus.Subbed)) {
|
||||||
//search_result_lang?.setColorFilter(ContextCompat.getColor(activity, R.color.subColor))
|
//search_result_lang?.setColorFilter(ContextCompat.getColor(activity, R.color.subColor))
|
||||||
text_is_sub?.visibility = View.VISIBLE
|
text_is_sub?.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,8 +126,5 @@ class SearchFragment : Fragment() {
|
||||||
search_exit_icon.alpha = 1f
|
search_exit_icon.alpha = 1f
|
||||||
search_loading_bar.alpha = 0f
|
search_loading_bar.alpha = 0f
|
||||||
}
|
}
|
||||||
|
|
||||||
main_search.onActionViewExpanded()
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
package com.lagradost.cloudstream3.utils
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||||
|
import com.fasterxml.jackson.databind.json.JsonMapper
|
||||||
|
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||||
|
|
||||||
|
const val PREFERENCES_NAME: String = "rebuild_preference"
|
||||||
|
|
||||||
|
object DataStore {
|
||||||
|
val mapper: JsonMapper = JsonMapper.builder().addModule(KotlinModule())
|
||||||
|
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()
|
||||||
|
|
||||||
|
private fun getPreferences(context: Context): SharedPreferences {
|
||||||
|
return context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.getSharedPrefs(): SharedPreferences {
|
||||||
|
return getPreferences(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFolderName(folder: String, path: String): String {
|
||||||
|
return "${folder}/${path}"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.getKeys(folder: String): List<String> {
|
||||||
|
return this.getSharedPrefs().all.keys.filter { it.startsWith(folder) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.removeKey(folder: String, path: String) {
|
||||||
|
removeKey(getFolderName(folder, path))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.containsKey(folder: String, path: String): Boolean {
|
||||||
|
return containsKey(getFolderName(folder, path))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.containsKey(path: String): Boolean {
|
||||||
|
val prefs = getSharedPrefs()
|
||||||
|
return prefs.contains(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.removeKey(path: String) {
|
||||||
|
val prefs = getSharedPrefs()
|
||||||
|
if (prefs.contains(path)) {
|
||||||
|
val editor: SharedPreferences.Editor = prefs.edit()
|
||||||
|
editor.remove(path)
|
||||||
|
editor.apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.removeKeys(folder: String): Int {
|
||||||
|
val keys = getKeys(folder)
|
||||||
|
keys.forEach { value ->
|
||||||
|
removeKey(value)
|
||||||
|
}
|
||||||
|
return keys.size
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> Context.setKey(path: String, value: T) {
|
||||||
|
val editor: SharedPreferences.Editor = getSharedPrefs().edit()
|
||||||
|
editor.putString(path, mapper.writeValueAsString(value))
|
||||||
|
editor.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> Context.setKey(folder: String, path: String, value: T) {
|
||||||
|
setKey(getFolderName(folder, path), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : Any> String.toKotlinObject(): T {
|
||||||
|
return mapper.readValue(this, T::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET KEY GIVEN PATH AND DEFAULT VALUE, NULL IF ERROR
|
||||||
|
inline fun <reified T : Any> Context.getKey(path: String, defVal: T?): T? {
|
||||||
|
try {
|
||||||
|
val json: String = getSharedPrefs().getString(path, null) ?: return defVal
|
||||||
|
return json.toKotlinObject()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : Any> Context.getKey(path: String): T? {
|
||||||
|
return getKey(path, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : Any> Context.getKey(folder: String, path: String): T? {
|
||||||
|
return getKey(getFolderName(folder, path), null)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : Any> Context.getKey(folder: String, path: String, defVal: T?): T? {
|
||||||
|
return getKey(getFolderName(folder, path), defVal)
|
||||||
|
}
|
||||||
|
}
|
10
app/src/main/res/drawable/video_tap_button.xml
Normal file
10
app/src/main/res/drawable/video_tap_button.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="@color/video_ripple">
|
||||||
|
<item android:id="@android:id/mask">
|
||||||
|
<shape android:shape="oval">
|
||||||
|
<solid android:color="@color/video_ripple"/>
|
||||||
|
</shape>
|
||||||
|
<color android:color="@color/video_ripple"/>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
76
app/src/main/res/layout/fragment_player.xml
Normal file
76
app/src/main/res/layout/fragment_player.xml
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:keepScreenOn="true"
|
||||||
|
app:backgroundTint="@android:color/black"
|
||||||
|
android:background="@android:color/black"
|
||||||
|
android:screenOrientation="userLandscape"
|
||||||
|
app:surface_type="texture_view"
|
||||||
|
>
|
||||||
|
<!-- app:controller_layout_id="@layout/player_custom_layout"-->
|
||||||
|
|
||||||
|
<com.google.android.exoplayer2.ui.PlayerView
|
||||||
|
android:id="@+id/player_view"
|
||||||
|
app:show_timeout="0"
|
||||||
|
app:hide_on_touch="false"
|
||||||
|
app:auto_show="true"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:fastforward_increment="10000"
|
||||||
|
app:rewind_increment="10000"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
/>
|
||||||
|
<FrameLayout
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:id="@+id/loading_overlay"
|
||||||
|
android:background="@android:color/black"
|
||||||
|
android:backgroundTint="@android:color/black"
|
||||||
|
>
|
||||||
|
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:id="@+id/main_load"
|
||||||
|
>
|
||||||
|
</ProgressBar>
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:src="@drawable/ic_baseline_arrow_back_24"
|
||||||
|
android:contentDescription="@string/go_back">
|
||||||
|
</ImageView>
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/video_go_back_holder"
|
||||||
|
android:layout_width="65dp"
|
||||||
|
android:layout_height="65dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:focusable="true"
|
||||||
|
android:clickable="true"
|
||||||
|
android:background="@drawable/video_tap_button"
|
||||||
|
android:contentDescription="@string/go_back">
|
||||||
|
</ImageView>
|
||||||
|
</FrameLayout>
|
||||||
|
</FrameLayout>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -7,6 +7,7 @@
|
||||||
<color name="colorOngoing">#F53B66</color> <!--FF8181-->
|
<color name="colorOngoing">#F53B66</color> <!--FF8181-->
|
||||||
<color name="colorPrimaryDark">#3700B3</color>
|
<color name="colorPrimaryDark">#3700B3</color>
|
||||||
<color name="colorAccent">#3b65f5</color> <!-- 818fff-->
|
<color name="colorAccent">#3b65f5</color> <!-- 818fff-->
|
||||||
|
<color name="video_ripple">#80FFFFFF</color>
|
||||||
|
|
||||||
<color name="darkBackground">#2B2C30</color> <!--0f0f10 0E0E10 303135 2B2C30-->
|
<color name="darkBackground">#2B2C30</color> <!--0f0f10 0E0E10 303135 2B2C30-->
|
||||||
<color name="bitDarkerGrayBackground">#1C1C20</color> <!--191a1f 19181E 202125 1C1C20-->
|
<color name="bitDarkerGrayBackground">#1C1C20</color> <!--191a1f 19181E 202125 1C1C20-->
|
||||||
|
|
|
@ -12,4 +12,5 @@
|
||||||
<string name="shadow_descript">Shadow</string>
|
<string name="shadow_descript">Shadow</string>
|
||||||
<string name="episode_more_options_descript">More Options</string>
|
<string name="episode_more_options_descript">More Options</string>
|
||||||
<string name="episode_play_descript">Play Episode</string>
|
<string name="episode_play_descript">Play Episode</string>
|
||||||
|
<string name="go_back">Go back</string>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in a new issue