forked from recloudstream/cloudstream
UI
This commit is contained in:
parent
0fa16e3bbc
commit
719b60db26
18 changed files with 530 additions and 21 deletions
|
@ -26,5 +26,10 @@
|
||||||
<option name="name" value="maven" />
|
<option name="name" value="maven" />
|
||||||
<option name="url" value="https://jitpack.io" />
|
<option name="url" value="https://jitpack.io" />
|
||||||
</remote-repository>
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="maven" />
|
||||||
|
<option name="name" value="maven" />
|
||||||
|
<option name="url" value="https://github.com/psiegman/mvn-repo/raw/master/releases" />
|
||||||
|
</remote-repository>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
|
@ -34,6 +34,9 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
maven {
|
||||||
|
url 'https://github.com/psiegman/mvn-repo/raw/master/releases'
|
||||||
|
}
|
||||||
maven { url 'https://jitpack.io' }
|
maven { url 'https://jitpack.io' }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,14 @@ object APIHolder {
|
||||||
ShiroProvider()
|
ShiroProvider()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun getApiFromName(apiName: String): MainAPI {
|
||||||
|
for (api in apis) {
|
||||||
|
if (apiName == api.name)
|
||||||
|
return api
|
||||||
|
}
|
||||||
|
return apis[defProvider]
|
||||||
|
}
|
||||||
|
|
||||||
fun Activity.getApiSettings(): HashSet<String> {
|
fun Activity.getApiSettings(): HashSet<String> {
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
|
||||||
|
@ -39,11 +47,11 @@ abstract class MainAPI {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun load(url: String): Any? { //LoadResponse
|
open fun load(slug: String): Any? { //LoadResponse
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun loadLinks(url: String, id: Int): Boolean {
|
open fun loadLinks(data: Any, id: Int): Boolean {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,8 +59,7 @@ abstract class MainAPI {
|
||||||
fun MainAPI.fixUrl(url: String): String {
|
fun MainAPI.fixUrl(url: String): String {
|
||||||
if (url.startsWith('/')) {
|
if (url.startsWith('/')) {
|
||||||
return mainUrl + url
|
return mainUrl + url
|
||||||
}
|
} else if (!url.startsWith("http") && !url.startsWith("//")) {
|
||||||
else if(!url.startsWith("http") && !url.startsWith("//")) {
|
|
||||||
return "$mainUrl/$url"
|
return "$mainUrl/$url"
|
||||||
}
|
}
|
||||||
return url
|
return url
|
||||||
|
@ -90,7 +97,8 @@ enum class TvType {
|
||||||
|
|
||||||
interface SearchResponse {
|
interface SearchResponse {
|
||||||
val name: String
|
val name: String
|
||||||
val url: String
|
val url: String // PUBLIC URL FOR OPEN IN APP
|
||||||
|
val slug: String // USED FOR INTERNAL DATA
|
||||||
val apiName: String
|
val apiName: String
|
||||||
val type: TvType
|
val type: TvType
|
||||||
val posterUrl: String?
|
val posterUrl: String?
|
||||||
|
@ -100,6 +108,7 @@ interface SearchResponse {
|
||||||
data class AnimeSearchResponse(
|
data class AnimeSearchResponse(
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override val url: String,
|
override val url: String,
|
||||||
|
override val slug: String,
|
||||||
override val apiName: String,
|
override val apiName: String,
|
||||||
override val type: TvType,
|
override val type: TvType,
|
||||||
|
|
||||||
|
@ -115,6 +124,7 @@ data class AnimeSearchResponse(
|
||||||
data class MovieSearchResponse(
|
data class MovieSearchResponse(
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override val url: String,
|
override val url: String,
|
||||||
|
override val slug: String,
|
||||||
override val apiName: String,
|
override val apiName: String,
|
||||||
override val type: TvType,
|
override val type: TvType,
|
||||||
|
|
||||||
|
@ -125,6 +135,7 @@ data class MovieSearchResponse(
|
||||||
data class TvSeriesSearchResponse(
|
data class TvSeriesSearchResponse(
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override val url: String,
|
override val url: String,
|
||||||
|
override val slug: String,
|
||||||
override val apiName: String,
|
override val apiName: String,
|
||||||
override val type: TvType,
|
override val type: TvType,
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,26 @@ import androidx.navigation.ui.AppBarConfiguration
|
||||||
import androidx.navigation.ui.setupWithNavController
|
import androidx.navigation.ui.setupWithNavController
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
private fun AppCompatActivity.backPressed(): Boolean {
|
||||||
|
val currentFragment = supportFragmentManager.fragments.last {
|
||||||
|
it.isVisible
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentFragment != null && supportFragmentManager.fragments.size > 2) {
|
||||||
|
//MainActivity.showNavbar()
|
||||||
|
supportFragmentManager.beginTransaction()
|
||||||
|
.setCustomAnimations(R.anim.enter_anim, R.anim.exit_anim, R.anim.pop_enter, R.anim.pop_exit)
|
||||||
|
.remove(currentFragment)
|
||||||
|
.commitAllowingStateLoss()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed() {
|
||||||
|
if (backPressed()) return
|
||||||
|
super.onBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
|
@ -3,7 +3,9 @@ package com.lagradost.cloudstream3
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.lagradost.cloudstream3.ui.result.ResultFragment
|
||||||
|
|
||||||
object UIHelper {
|
object UIHelper {
|
||||||
val Int.toPx: Int get() = (this * Resources.getSystem().displayMetrics.density).toInt()
|
val Int.toPx: Int get() = (this * Resources.getSystem().displayMetrics.density).toInt()
|
||||||
|
@ -11,16 +13,16 @@ object UIHelper {
|
||||||
val Int.toDp: Int get() = (this / Resources.getSystem().displayMetrics.density).toInt()
|
val Int.toDp: Int get() = (this / Resources.getSystem().displayMetrics.density).toInt()
|
||||||
val Float.toDp: Float get() = (this / Resources.getSystem().displayMetrics.density)
|
val Float.toDp: Float get() = (this / Resources.getSystem().displayMetrics.density)
|
||||||
|
|
||||||
fun Activity.loadResult(url: String, apiName: String) {
|
fun AppCompatActivity.loadResult(url: String, slug: String, apiName: String) {
|
||||||
/*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, apiName))
|
.add(R.id.homeRoot, ResultFragment().newInstance(url, slug, apiName))
|
||||||
.commit()
|
.commit()
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Activity.getStatusBarHeight(): Int {
|
fun Activity.getStatusBarHeight(): Int {
|
||||||
var result = 0
|
var result = 0
|
||||||
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
|
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
|
||||||
if (resourceId > 0) {
|
if (resourceId > 0) {
|
||||||
|
|
|
@ -152,6 +152,7 @@ class ShiroProvider : MainAPI() {
|
||||||
returnValue.add(AnimeSearchResponse(
|
returnValue.add(AnimeSearchResponse(
|
||||||
i.english ?: i.canonicalTitle,
|
i.english ?: i.canonicalTitle,
|
||||||
"$mainUrl/${i.slug}",
|
"$mainUrl/${i.slug}",
|
||||||
|
i.slug,
|
||||||
this.name,
|
this.name,
|
||||||
type,
|
type,
|
||||||
"https://cdn.shiro.is/${i.image}",
|
"https://cdn.shiro.is/${i.image}",
|
||||||
|
@ -165,9 +166,9 @@ class ShiroProvider : MainAPI() {
|
||||||
return returnValue
|
return returnValue
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun load(url: String): Any? {
|
override fun load(slug: String): Any? {
|
||||||
if (!autoLoadToken()) return null
|
if (!autoLoadToken()) return null
|
||||||
val rurl = "https://tapi.shiro.is/anime/slug/${url}?token=${token}"
|
val rurl = "https://tapi.shiro.is/anime/slug/${slug}?token=${token}"
|
||||||
val response = khttp.get(rurl, timeout = 120.0)
|
val response = khttp.get(rurl, timeout = 120.0)
|
||||||
val mapped = response.let { mapper.readValue<AnimePage>(it.text) }
|
val mapped = response.let { mapper.readValue<AnimePage>(it.text) }
|
||||||
val data = mapped.data
|
val data = mapped.data
|
||||||
|
@ -183,7 +184,7 @@ class ShiroProvider : MainAPI() {
|
||||||
data.english,
|
data.english,
|
||||||
data.japanese,
|
data.japanese,
|
||||||
data.canonicalTitle ?: data.name.replace("Dubbed", ""),
|
data.canonicalTitle ?: data.name.replace("Dubbed", ""),
|
||||||
url,
|
"$mainUrl/${slug}",
|
||||||
this.name,
|
this.name,
|
||||||
getType(data.type ?: ""),
|
getType(data.type ?: ""),
|
||||||
"https://cdn.shiro.is/${data.image}",
|
"https://cdn.shiro.is/${data.image}",
|
||||||
|
@ -197,6 +198,5 @@ class ShiroProvider : MainAPI() {
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package com.lagradost.cloudstream3.ui.result
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.lagradost.cloudstream3.*
|
||||||
|
import com.lagradost.cloudstream3.APIHolder.getApiFromName
|
||||||
|
import kotlinx.android.synthetic.main.result_episode.view.*
|
||||||
|
|
||||||
|
|
||||||
|
class EpisodeAdapter(
|
||||||
|
activity: Activity,
|
||||||
|
animeList: ArrayList<ResultEpisode>,
|
||||||
|
resView: RecyclerView,
|
||||||
|
) :
|
||||||
|
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
var cardList = animeList
|
||||||
|
private var activity: Activity = activity
|
||||||
|
var resView: RecyclerView? = resView
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
|
return CardViewHolder(
|
||||||
|
LayoutInflater.from(parent.context).inflate(R.layout.result_episode, parent, false),
|
||||||
|
activity,
|
||||||
|
resView!!
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
|
when (holder) {
|
||||||
|
is CardViewHolder -> {
|
||||||
|
holder.bind(cardList[position])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return cardList.size
|
||||||
|
}
|
||||||
|
|
||||||
|
class CardViewHolder
|
||||||
|
constructor(itemView: View, _activity: Activity, resView: RecyclerView) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
val activity = _activity
|
||||||
|
val episode_view_procentage: View = itemView.episode_view_procentage
|
||||||
|
val episode_view_procentage_off: View = itemView.episode_view_procentage_off
|
||||||
|
val episode_text: TextView = itemView.episode_text
|
||||||
|
val episode_extra: ImageView = itemView.episode_extra
|
||||||
|
val episode_play: ImageView = itemView.episode_play
|
||||||
|
|
||||||
|
fun bind(card: ResultEpisode) {
|
||||||
|
episode_text.text = card.name ?: "Episode ${card.episode}"
|
||||||
|
|
||||||
|
fun setWidth(v: View, procentage: Float) {
|
||||||
|
val param = LinearLayout.LayoutParams(
|
||||||
|
v.layoutParams.width,
|
||||||
|
v.layoutParams.height,
|
||||||
|
procentage
|
||||||
|
)
|
||||||
|
v.layoutParams = param
|
||||||
|
}
|
||||||
|
setWidth(episode_view_procentage, card.watchProgress)
|
||||||
|
setWidth(episode_view_procentage_off, 1 - card.watchProgress)
|
||||||
|
|
||||||
|
episode_play.setOnClickListener {
|
||||||
|
getApiFromName(card.apiName).loadLinks(card.data, card.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,21 +1,63 @@
|
||||||
package com.lagradost.cloudstream3.ui.result
|
package com.lagradost.cloudstream3.ui.result
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.TextView
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.view.marginBottom
|
||||||
|
import androidx.core.view.marginLeft
|
||||||
|
import androidx.core.view.marginRight
|
||||||
|
import androidx.core.view.marginTop
|
||||||
|
import androidx.core.widget.NestedScrollView
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
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
|
||||||
|
import com.lagradost.cloudstream3.AnimeLoadResponse
|
||||||
|
import com.lagradost.cloudstream3.LoadResponse
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
|
import com.lagradost.cloudstream3.UIHelper.fixPaddingStatusbar
|
||||||
|
import com.lagradost.cloudstream3.UIHelper.getStatusBarHeight
|
||||||
|
import com.lagradost.cloudstream3.mvvm.Resource
|
||||||
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
|
import jp.wasabeef.glide.transformations.BlurTransformation
|
||||||
|
import kotlinx.android.synthetic.main.fragment_result.*
|
||||||
|
import kotlinx.android.synthetic.main.fragment_search.*
|
||||||
|
|
||||||
|
|
||||||
|
const val MAX_SYNO_LENGH = 600
|
||||||
|
|
||||||
|
data class ResultEpisode(
|
||||||
|
val name: String?,
|
||||||
|
val episode: Int,
|
||||||
|
val data: Any,
|
||||||
|
val apiName: String,
|
||||||
|
val id: Int,
|
||||||
|
val watchProgress: Float, // 0-1
|
||||||
|
)
|
||||||
|
|
||||||
class ResultFragment : Fragment() {
|
class ResultFragment : Fragment() {
|
||||||
|
fun newInstance(url: String, slug: String, apiName: String) =
|
||||||
|
ResultFragment().apply {
|
||||||
|
arguments = Bundle().apply {
|
||||||
|
putString("url", url)
|
||||||
|
putString("slug", slug)
|
||||||
|
putString("apiName", apiName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private lateinit var viewModel: ResultViewModel
|
private lateinit var viewModel: ResultViewModel
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?,
|
||||||
): View? {
|
): View? {
|
||||||
viewModel =
|
viewModel =
|
||||||
ViewModelProvider(this).get(ResultViewModel::class.java)
|
ViewModelProvider(this).get(ResultViewModel::class.java)
|
||||||
|
@ -23,7 +65,121 @@ class ResultFragment : Fragment() {
|
||||||
return inflater.inflate(R.layout.fragment_result, container, false)
|
return inflater.inflate(R.layout.fragment_result, container, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
activity?.fixPaddingStatusbar(result_scroll)
|
||||||
|
activity?.fixPaddingStatusbar(result_barstatus)
|
||||||
|
// activity?.fixPaddingStatusbar(result_toolbar)
|
||||||
|
|
||||||
|
val url = arguments?.getString("url")
|
||||||
|
val slug = arguments?.getString("slug")
|
||||||
|
val apiName = arguments?.getString("apiName")
|
||||||
|
|
||||||
|
result_scroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
||||||
|
if(result_poster_blur == null) return@OnScrollChangeListener
|
||||||
|
result_poster_blur.alpha = maxOf(0f, (0.3f - scrollY / 1000f))
|
||||||
|
result_barstatus.alpha = scrollY / 200f
|
||||||
|
result_barstatus.visibility = if(scrollY > 0) View.VISIBLE else View.GONE
|
||||||
|
})
|
||||||
|
|
||||||
|
result_toolbar.setNavigationIcon(R.drawable.ic_baseline_arrow_back_24)
|
||||||
|
result_toolbar.setNavigationOnClickListener {
|
||||||
|
activity?.onBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
observe(viewModel.resultResponse) { data ->
|
||||||
|
when (data) {
|
||||||
|
is Resource.Success -> {
|
||||||
|
val d = data.value
|
||||||
|
if (d is LoadResponse) {
|
||||||
|
result_bookmark_button.text = "Watching"
|
||||||
|
|
||||||
|
if (d.year != null) {
|
||||||
|
result_year.visibility = View.VISIBLE
|
||||||
|
result_year.text = d.year.toString()
|
||||||
|
} else {
|
||||||
|
result_year.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d.posterUrl != null) {
|
||||||
|
val glideUrl =
|
||||||
|
GlideUrl(d.posterUrl)
|
||||||
|
context!!.let {
|
||||||
|
/*
|
||||||
|
Glide.with(it)
|
||||||
|
.load(glideUrl)
|
||||||
|
.into(result_poster)*/
|
||||||
|
|
||||||
|
Glide.with(it)
|
||||||
|
.load(glideUrl)
|
||||||
|
.apply(bitmapTransform(BlurTransformation(10, 3)))
|
||||||
|
.into(result_poster_blur)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder>? = activity?.let {
|
||||||
|
EpisodeAdapter(
|
||||||
|
it,
|
||||||
|
ArrayList(),
|
||||||
|
result_episodes,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
result_episodes.adapter = adapter
|
||||||
|
result_episodes.layoutManager = GridLayoutManager(context, 1)
|
||||||
|
|
||||||
|
if (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
|
||||||
|
|
||||||
|
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_tags.text = (d.tags ?: ArrayList()).joinToString(separator = " | ")
|
||||||
|
val isDub = d.dubEpisodes != null && d.dubEpisodes.size > 0
|
||||||
|
val dataList = (if (isDub) d.dubEpisodes else d.subEpisodes)
|
||||||
|
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(),
|
||||||
|
(index * 0.1f),//TODO TEST; REMOVE
|
||||||
|
))
|
||||||
|
}
|
||||||
|
(result_episodes.adapter as EpisodeAdapter).cardList = episodes
|
||||||
|
(result_episodes.adapter as EpisodeAdapter).notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result_title.text = d.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Resource.Failure -> {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (viewModel.resultResponse.value == null && apiName != null && slug != null)
|
||||||
|
viewModel.load(slug, apiName)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,25 @@
|
||||||
package com.lagradost.cloudstream3.ui.result
|
package com.lagradost.cloudstream3.ui.result
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.lagradost.cloudstream3.APIHolder
|
||||||
|
import com.lagradost.cloudstream3.APIHolder.getApiFromName
|
||||||
|
import com.lagradost.cloudstream3.MainAPI
|
||||||
|
import com.lagradost.cloudstream3.mvvm.Resource
|
||||||
|
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class ResultViewModel : ViewModel() {
|
class ResultViewModel : ViewModel() {
|
||||||
|
private val _resultResponse: MutableLiveData<Resource<Any?>> = MutableLiveData()
|
||||||
|
val resultResponse: LiveData<Resource<Any?>> get() = _resultResponse
|
||||||
|
|
||||||
|
fun load(url: String, apiName:String) = viewModelScope.launch {
|
||||||
|
val data = safeApiCall {
|
||||||
|
getApiFromName(apiName).load(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
_resultResponse.postValue(data)
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -8,6 +8,7 @@ import android.view.ViewGroup
|
||||||
import android.widget.FrameLayout
|
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.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
|
||||||
|
@ -105,7 +106,7 @@ class SearchAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
bg.setOnClickListener {
|
bg.setOnClickListener {
|
||||||
activity.loadResult(card.url, card.apiName)
|
(activity as AppCompatActivity).loadResult(card.url, card.slug, card.apiName)
|
||||||
}
|
}
|
||||||
|
|
||||||
when (card) {
|
when (card) {
|
||||||
|
|
7
app/src/main/res/drawable/background_shadow.xml
Normal file
7
app/src/main/res/drawable/background_shadow.xml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<gradient
|
||||||
|
android:angle="-90"
|
||||||
|
android:startColor="@color/transparent"
|
||||||
|
android:endColor="@color/bitDarkerGrayBackground"/>
|
||||||
|
</shape>
|
BIN
app/src/main/res/drawable/example_poster.jpg
Normal file
BIN
app/src/main/res/drawable/example_poster.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 61 KiB |
5
app/src/main/res/drawable/ic_baseline_arrow_back_24.xml
Normal file
5
app/src/main/res/drawable/ic_baseline_arrow_back_24.xml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<vector android:autoMirrored="true" android:height="24dp"
|
||||||
|
android:tint="#FFFFFF" android:viewportHeight="24"
|
||||||
|
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
|
||||||
|
</vector>
|
5
app/src/main/res/drawable/ic_baseline_more_vert_24.xml
Normal file
5
app/src/main/res/drawable/ic_baseline_more_vert_24.xml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
|
||||||
|
</vector>
|
5
app/src/main/res/drawable/ic_baseline_play_arrow_24.xml
Normal file
5
app/src/main/res/drawable/ic_baseline_play_arrow_24.xml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M8,5v14l11,-7z"/>
|
||||||
|
</vector>
|
|
@ -1,6 +1,138 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout
|
||||||
android:layout_width="match_parent"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_height="match_parent">
|
android:layout_width="match_parent"
|
||||||
|
android:id="@+id/result_root"
|
||||||
|
android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:background="@color/bitDarkerGrayBackground"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
>
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/result_barstatus"
|
||||||
|
android:background="@color/grayBackground"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:alpha="0"
|
||||||
|
tools:alpha="1"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/result_toolbar"
|
||||||
|
android:paddingTop="@dimen/navbarHeight"
|
||||||
|
app:title="Perfect Run"
|
||||||
|
app:layout_scrollFlags="scroll|enterAlways"
|
||||||
|
android:layout_width="match_parent" android:layout_height="wrap_content">
|
||||||
|
</com.google.android.material.appbar.MaterialToolbar>
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
<FrameLayout android:layout_width="match_parent" android:layout_height="200dp">
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:alpha="0.3"
|
||||||
|
tools:src="@drawable/example_poster"
|
||||||
|
android:background="@color/darkBackground"
|
||||||
|
android:id="@+id/result_poster_blur"
|
||||||
|
android:contentDescription=""/>
|
||||||
|
<ImageView android:src="@drawable/background_shadow" android:layout_gravity="bottom"
|
||||||
|
android:layout_width="match_parent" android:layout_height="100dp">
|
||||||
|
|
||||||
|
</ImageView>
|
||||||
|
</FrameLayout>
|
||||||
|
<androidx.core.widget.NestedScrollView android:id="@+id/result_scroll" android:layout_width="match_parent" android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginTop="100dp"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<GridLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content">
|
||||||
|
<LinearLayout android:gravity="center_vertical" android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content">
|
||||||
|
<TextView
|
||||||
|
tools:text="The Perfect Run"
|
||||||
|
android:id="@+id/result_title"
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/textColor" android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
</TextView>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/result_year"
|
||||||
|
tools:text="2021"
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:textColor="@color/textColor" android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
</TextView>
|
||||||
|
</LinearLayout>
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:layout_gravity="end"
|
||||||
|
app:cornerRadius="1000dp"
|
||||||
|
android:id="@+id/result_bookmark_button"
|
||||||
|
tools:text="Bookmark"
|
||||||
|
app:rippleColor="@color/colorPrimary"
|
||||||
|
app:strokeColor="@color/colorPrimary"
|
||||||
|
android:textColor="@color/textColor"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:backgroundTint="@color/colorPrimary"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="45dp">
|
||||||
|
</com.google.android.material.button.MaterialButton>
|
||||||
|
</GridLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/result_tags"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
tools:text="Adventure | Fantasy World | Comedy | Ecchi | Action | Science Fiction | Fantasy"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_width="match_parent" android:layout_height="wrap_content">
|
||||||
|
</TextView>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:id="@+id/result_descript"
|
||||||
|
tools:text="Ryan Quicksave Romano is an eccentric adventurer with a strange power: he can create a save-point in time and redo his life whenever he dies. Arriving in New Rome, the glitzy capital of sin of a rebuilding Europe, he finds the city torn between mega-corporations, sponsored heroes, superpowered criminals, and true monsters. It's a time of chaos, where potions can grant the power to rule the world and dangers lurk everywhere. "
|
||||||
|
android:layout_width="match_parent" android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
</TextView>
|
||||||
|
<TextView
|
||||||
|
android:textColor="@color/textColor"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:id="@+id/result_episodes_text"
|
||||||
|
tools:text="8 Episodes"
|
||||||
|
android:gravity="start"
|
||||||
|
android:layout_width="match_parent" android:layout_height="wrap_content">
|
||||||
|
</TextView>
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
tools:listitem="@layout/result_episode"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/result_episodes"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
<!--
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="150dp"
|
||||||
|
app:cardCornerRadius="@dimen/roundedImageRadius"
|
||||||
|
android:elevation="10dp"
|
||||||
|
>
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:id="@+id/result_poster"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
tools:src="@drawable/example_poster"
|
||||||
|
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||||
|
android:contentDescription="@string/search_poster_descript"/>
|
||||||
|
</androidx.cardview.widget.CardView>-->
|
||||||
|
|
||||||
|
</FrameLayout>
|
63
app/src/main/res/layout/result_episode.xml
Normal file
63
app/src/main/res/layout/result_episode.xml
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
app:cardCornerRadius="@dimen/roundedImageRadius"
|
||||||
|
app:cardBackgroundColor="@color/itemBackground"
|
||||||
|
android:layout_marginBottom="2dp"
|
||||||
|
>
|
||||||
|
<!-- IDK BUT THIS DOES NOT SEAM LIKE A GOOD WAY OF DOING IT -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
<View
|
||||||
|
android:layout_weight="0.5"
|
||||||
|
android:id="@+id/episode_view_procentage"
|
||||||
|
android:alpha="0.2"
|
||||||
|
android:background="@color/colorPrimary"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
android:id="@+id/episode_view_procentage_off"
|
||||||
|
android:layout_weight="0.10"
|
||||||
|
android:alpha="0"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
</View>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<GridLayout android:layout_width="match_parent" android:layout_height="match_parent">
|
||||||
|
<ImageView
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:id="@+id/episode_play"
|
||||||
|
android:background="?selectableItemBackgroundBorderless"
|
||||||
|
android:src="@drawable/ic_baseline_play_arrow_24"
|
||||||
|
android:contentDescription="@string/episode_play_descript"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/episode_text"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
|
android:layout_gravity="center_vertical" android:gravity="center_vertical" tools:text="Episode 1"
|
||||||
|
android:textColor="@color/textColor" android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
</TextView>
|
||||||
|
<ImageView
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:id="@+id/episode_extra"
|
||||||
|
android:background="?selectableItemBackgroundBorderless"
|
||||||
|
android:layout_gravity="center_vertical|end"
|
||||||
|
android:src="@drawable/ic_baseline_more_vert_24"
|
||||||
|
android:contentDescription="@string/episode_more_options_descript"/>
|
||||||
|
</GridLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</androidx.cardview.widget.CardView>
|
|
@ -10,4 +10,6 @@
|
||||||
<string name="search_poster_descript">Poster</string>
|
<string name="search_poster_descript">Poster</string>
|
||||||
<string name="no_data">No Data</string>
|
<string name="no_data">No Data</string>
|
||||||
<string name="shadow_descript">Shadow</string>
|
<string name="shadow_descript">Shadow</string>
|
||||||
|
<string name="episode_more_options_descript">More Options</string>
|
||||||
|
<string name="episode_play_descript">Play Episode</string>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in a new issue