diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index 23704740..fd485067 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -26,5 +26,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 6538269f..31b89e46 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -34,6 +34,9 @@ android {
}
repositories {
+ maven {
+ url 'https://github.com/psiegman/mvn-repo/raw/master/releases'
+ }
maven { url 'https://jitpack.io' }
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
index 27fe3b4d..ebec8317 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
@@ -23,6 +23,14 @@ object APIHolder {
ShiroProvider()
)
+ fun getApiFromName(apiName: String): MainAPI {
+ for (api in apis) {
+ if (apiName == api.name)
+ return api
+ }
+ return apis[defProvider]
+ }
+
fun Activity.getApiSettings(): HashSet {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
@@ -39,11 +47,11 @@ abstract class MainAPI {
return null
}
- open fun load(url: String): Any? { //LoadResponse
+ open fun load(slug: String): Any? { //LoadResponse
return null
}
- open fun loadLinks(url: String, id: Int): Boolean {
+ open fun loadLinks(data: Any, id: Int): Boolean {
return false
}
}
@@ -51,8 +59,7 @@ abstract class MainAPI {
fun MainAPI.fixUrl(url: String): String {
if (url.startsWith('/')) {
return mainUrl + url
- }
- else if(!url.startsWith("http") && !url.startsWith("//")) {
+ } else if (!url.startsWith("http") && !url.startsWith("//")) {
return "$mainUrl/$url"
}
return url
@@ -90,7 +97,8 @@ enum class TvType {
interface SearchResponse {
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 type: TvType
val posterUrl: String?
@@ -100,6 +108,7 @@ interface SearchResponse {
data class AnimeSearchResponse(
override val name: String,
override val url: String,
+ override val slug: String,
override val apiName: String,
override val type: TvType,
@@ -115,6 +124,7 @@ data class AnimeSearchResponse(
data class MovieSearchResponse(
override val name: String,
override val url: String,
+ override val slug: String,
override val apiName: String,
override val type: TvType,
@@ -125,6 +135,7 @@ data class MovieSearchResponse(
data class TvSeriesSearchResponse(
override val name: String,
override val url: String,
+ override val slug: String,
override val apiName: String,
override val type: TvType,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
index 6ea47ddd..41078eff 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
@@ -8,6 +8,26 @@ import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupWithNavController
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?) {
super.onCreate(savedInstanceState)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/UIHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/UIHelper.kt
index 32e0dc96..940d3152 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/UIHelper.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/UIHelper.kt
@@ -3,7 +3,9 @@ package com.lagradost.cloudstream3
import android.app.Activity
import android.content.res.Resources
import android.view.View
+import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager
+import com.lagradost.cloudstream3.ui.result.ResultFragment
object UIHelper {
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 Float.toDp: Float get() = (this / Resources.getSystem().displayMetrics.density)
- fun Activity.loadResult(url: String, apiName: String) {
- /*this.runOnUiThread {
+ fun AppCompatActivity.loadResult(url: String, slug: String, apiName: String) {
+ this.runOnUiThread {
this.supportFragmentManager.beginTransaction()
.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()
- }*/
+ }
}
- private fun Activity.getStatusBarHeight(): Int {
+ fun Activity.getStatusBarHeight(): Int {
var result = 0
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
if (resourceId > 0) {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/ShiroProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/ShiroProvider.kt
index 5b35d5f7..8a0b5b91 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/ShiroProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/ShiroProvider.kt
@@ -152,6 +152,7 @@ class ShiroProvider : MainAPI() {
returnValue.add(AnimeSearchResponse(
i.english ?: i.canonicalTitle,
"$mainUrl/${i.slug}",
+ i.slug,
this.name,
type,
"https://cdn.shiro.is/${i.image}",
@@ -165,9 +166,9 @@ class ShiroProvider : MainAPI() {
return returnValue
}
- override fun load(url: String): Any? {
+ override fun load(slug: String): Any? {
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 mapped = response.let { mapper.readValue(it.text) }
val data = mapped.data
@@ -183,7 +184,7 @@ class ShiroProvider : MainAPI() {
data.english,
data.japanese,
data.canonicalTitle ?: data.name.replace("Dubbed", ""),
- url,
+ "$mainUrl/${slug}",
this.name,
getType(data.type ?: ""),
"https://cdn.shiro.is/${data.image}",
@@ -197,6 +198,5 @@ class ShiroProvider : MainAPI() {
null,
null,
)
-
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
new file mode 100644
index 00000000..0b542369
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
@@ -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,
+ resView: RecyclerView,
+) :
+ RecyclerView.Adapter() {
+ 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)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt
index 7e6d5e3d..d7832f9c 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt
@@ -1,21 +1,63 @@
package com.lagradost.cloudstream3.ui.result
+import android.annotation.SuppressLint
import android.os.Bundle
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
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.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.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() {
+ 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
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
- savedInstanceState: Bundle?
+ savedInstanceState: Bundle?,
): View? {
viewModel =
ViewModelProvider(this).get(ResultViewModel::class.java)
@@ -23,7 +65,121 @@ class ResultFragment : Fragment() {
return inflater.inflate(R.layout.fragment_result, container, false)
}
+ @SuppressLint("SetTextI18n")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
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? = 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()
+ 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)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt
index 8bd77038..d142eb95 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt
@@ -1,7 +1,25 @@
package com.lagradost.cloudstream3.ui.result
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
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() {
+ private val _resultResponse: MutableLiveData> = MutableLiveData()
+ val resultResponse: LiveData> get() = _resultResponse
+ fun load(url: String, apiName:String) = viewModelScope.launch {
+ val data = safeApiCall {
+ getApiFromName(apiName).load(url)
+ }
+
+ _resultResponse.postValue(data)
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt
index 657fe6dc..a68f4c4f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt
@@ -8,6 +8,7 @@ import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl
@@ -105,7 +106,7 @@ class SearchAdapter(
}
bg.setOnClickListener {
- activity.loadResult(card.url, card.apiName)
+ (activity as AppCompatActivity).loadResult(card.url, card.slug, card.apiName)
}
when (card) {
diff --git a/app/src/main/res/drawable/background_shadow.xml b/app/src/main/res/drawable/background_shadow.xml
new file mode 100644
index 00000000..c916ec06
--- /dev/null
+++ b/app/src/main/res/drawable/background_shadow.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/example_poster.jpg b/app/src/main/res/drawable/example_poster.jpg
new file mode 100644
index 00000000..6d6e04ee
Binary files /dev/null and b/app/src/main/res/drawable/example_poster.jpg differ
diff --git a/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml b/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml
new file mode 100644
index 00000000..31e7df2e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_baseline_more_vert_24.xml b/app/src/main/res/drawable/ic_baseline_more_vert_24.xml
new file mode 100644
index 00000000..6a7f2748
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_more_vert_24.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_baseline_play_arrow_24.xml b/app/src/main/res/drawable/ic_baseline_play_arrow_24.xml
new file mode 100644
index 00000000..0870be8f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_play_arrow_24.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/layout/fragment_result.xml b/app/src/main/res/layout/fragment_result.xml
index 912a5d04..f9e2a612 100644
--- a/app/src/main/res/layout/fragment_result.xml
+++ b/app/src/main/res/layout/fragment_result.xml
@@ -1,6 +1,138 @@
-
+
+
+
+
+
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/result_episode.xml b/app/src/main/res/layout/result_episode.xml
new file mode 100644
index 00000000..f3a3c0b9
--- /dev/null
+++ b/app/src/main/res/layout/result_episode.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index f1c90c46..f712e22f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -10,4 +10,6 @@
Poster
No Data
Shadow
+ More Options
+ Play Episode
\ No newline at end of file