homepage somewhat working

This commit is contained in:
LagradOst 2021-07-29 02:19:42 +02:00
parent f270f9f551
commit 61323b5c56
15 changed files with 545 additions and 51 deletions

View file

@ -47,6 +47,13 @@ object APIHolder {
return apis[defProvider] return apis[defProvider]
} }
fun getApiFromNameNull(apiName: String?): MainAPI? {
for (api in apis) {
if (apiName == api.name)
return api
}
return null
}
fun LoadResponse.getId(): Int { fun LoadResponse.getId(): Int {
return url.replace(getApiFromName(apiName).mainUrl, "").hashCode() return url.replace(getApiFromName(apiName).mainUrl, "").hashCode()
@ -70,14 +77,19 @@ abstract class MainAPI {
/**If link is stored in the "data" string, so links can be instantly loaded*/ /**If link is stored in the "data" string, so links can be instantly loaded*/
open val instantLinkLoading = false open val instantLinkLoading = false
open val hasQuickSearch = false
/**Set false if links require referer or for some reason cant be played on a chromecast*/ /**Set false if links require referer or for some reason cant be played on a chromecast*/
open val hasChromecastSupport = true open val hasChromecastSupport = true
/**If all links are m3u8 then set this to false*/ /**If all links are m3u8 then set this to false*/
open val hasDownloadSupport = true open val hasDownloadSupport = true
open val hasMainPage = false
open val hasQuickSearch = false
open fun getMainPage() : HomePageResponse? {
return null
}
open fun search(query: String): ArrayList<SearchResponse>? { open fun search(query: String): ArrayList<SearchResponse>? {
return null return null
} }
@ -161,6 +173,15 @@ fun TvType.isMovieType(): Boolean {
data class SubtitleFile(val lang: String, val url: String) data class SubtitleFile(val lang: String, val url: String)
class HomePageResponse(
val items: List<HomePageList>
)
class HomePageList(
val name: String,
val list: List<SearchResponse>
)
interface SearchResponse { interface SearchResponse {
val name: String val name: String
val url: String // PUBLIC URL FOR OPEN IN APP val url: String // PUBLIC URL FOR OPEN IN APP

View file

@ -9,6 +9,7 @@ import java.net.URLEncoder
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
const val SHIRO_TIMEOUT_TIME = 60.0
class ShiroProvider : MainAPI() { class ShiroProvider : MainAPI() {
companion object { companion object {
@ -55,6 +56,9 @@ class ShiroProvider : MainAPI() {
override val hasQuickSearch: Boolean override val hasQuickSearch: Boolean
get() = true get() = true
override val hasMainPage: Boolean
get() = true
data class ShiroSearchResponseShow( data class ShiroSearchResponseShow(
@JsonProperty("image") val image: String, @JsonProperty("image") val image: String,
@JsonProperty("_id") val _id: String, @JsonProperty("_id") val _id: String,
@ -134,6 +138,46 @@ class ShiroProvider : MainAPI() {
@JsonProperty("status") val status: String, @JsonProperty("status") val status: String,
) )
data class ShiroHomePageData(
@JsonProperty("trending_animes") val trending_animes: List<AnimePageData>,
@JsonProperty("ongoing_animes") val ongoing_animes: List<AnimePageData>,
@JsonProperty("latest_animes") val latest_animes: List<AnimePageData>,
@JsonProperty("latest_episodes") val latest_episodes: List<ShiroEpisodes>,
)
data class ShiroHomePage(
@JsonProperty("status") val status: String,
@JsonProperty("data") val data: ShiroHomePageData,
@JsonProperty("random") var random: AnimePage?,
)
private fun toHomePageList(list: List<AnimePageData>, name: String): HomePageList {
return HomePageList(name, list.map { data ->
val type = getType(data.type)
val isDubbed =
data.language == "dubbed"
val set: EnumSet<DubStatus> =
EnumSet.of(if (isDubbed) DubStatus.Dubbed else DubStatus.Subbed)
val episodeCount = data.episodeCount?.toIntOrNull()
return@map AnimeSearchResponse(
data.name.replace("Dubbed", ""), // i.english ?: i.canonicalTitle,
"$mainUrl/anime/${data.slug}",
data.slug,
this.name,
type,
"https://cdn.shiro.is/${data.image}",
data.year?.toIntOrNull(),
data.canonicalTitle,
set,
if (isDubbed) episodeCount else null,
if (!isDubbed) episodeCount else null,
)
}.toList())
}
private fun turnSearchIntoResponse(data: ShiroSearchResponseShow): AnimeSearchResponse { private fun turnSearchIntoResponse(data: ShiroSearchResponseShow): AnimeSearchResponse {
val type = getType(data.type) val type = getType(data.type)
val isDubbed = val isDubbed =
@ -141,12 +185,9 @@ class ShiroProvider : MainAPI() {
data.language == "dubbed" data.language == "dubbed"
else else
data.slug.contains("dubbed") data.slug.contains("dubbed")
val set: EnumSet<DubStatus> = EnumSet.noneOf(DubStatus::class.java) val set: EnumSet<DubStatus> =
EnumSet.of(if (isDubbed) DubStatus.Dubbed else DubStatus.Subbed)
if (isDubbed)
set.add(DubStatus.Dubbed)
else
set.add(DubStatus.Subbed)
val episodeCount = data.episodeCount?.toIntOrNull() val episodeCount = data.episodeCount?.toIntOrNull()
return AnimeSearchResponse( return AnimeSearchResponse(
@ -164,7 +205,26 @@ class ShiroProvider : MainAPI() {
) )
} }
override fun quickSearch(query: String): ArrayList<SearchResponse> { override fun getMainPage(): HomePageResponse? {
if (!autoLoadToken()) return null
val url = "https://tapi.shiro.is/latest?token=$token"
val response = khttp.get(url, timeout = SHIRO_TIMEOUT_TIME)
val res = response.text.let { mapper.readValue<ShiroHomePage>(it) }
val d = res.data
return HomePageResponse(
listOf(
toHomePageList(d.trending_animes, "Trending"),
toHomePageList(d.ongoing_animes, "Ongoing"),
toHomePageList(d.latest_animes, "Latest")
)
)
}
override fun quickSearch(query: String): ArrayList<SearchResponse>? {
if (!autoLoadToken()) return null
val returnValue: ArrayList<SearchResponse> = ArrayList() val returnValue: ArrayList<SearchResponse> = ArrayList()
val response = khttp.get( val response = khttp.get(
@ -214,8 +274,8 @@ class ShiroProvider : MainAPI() {
val episodes = val episodes =
ArrayList<AnimeEpisode>( ArrayList<AnimeEpisode>(
data.episodes?.distinctBy { it.episode_number }?.sortedBy { it.episode_number } data.episodes?.distinctBy { it.episode_number }?.sortedBy { it.episode_number }
?.map { AnimeEpisode(it.videos[0].video_id) } ?.map { AnimeEpisode(it.videos[0].video_id) }
?: ArrayList<AnimeEpisode>()) ?: ArrayList<AnimeEpisode>())
val status = when (data.status) { val status = when (data.status) {
"current" -> ShowStatus.Ongoing "current" -> ShowStatus.Ongoing
"finished" -> ShowStatus.Completed "finished" -> ShowStatus.Completed

View file

@ -5,6 +5,7 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import com.bumptech.glide.load.HttpException import com.bumptech.glide.load.HttpException
import com.lagradost.cloudstream3.ui.ErrorLoadingException
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.net.SocketTimeoutException import java.net.SocketTimeoutException
@ -67,6 +68,9 @@ suspend fun <T> safeApiCall(
is UnknownHostException -> { is UnknownHostException -> {
Resource.Failure(true, null, null, "Cannot connect to server, try again later.") Resource.Failure(true, null, null, "Cannot connect to server, try again later.")
} }
is ErrorLoadingException -> {
Resource.Failure(true, null, null, "Error loading, try again later.")
}
else -> { else -> {
val stackTraceMsg = throwable.localizedMessage + "\n\n" + throwable.stackTrace.joinToString( val stackTraceMsg = throwable.localizedMessage + "\n\n" + throwable.stackTrace.joinToString(
separator = "\n" separator = "\n"

View file

@ -0,0 +1,47 @@
package com.lagradost.cloudstream3.ui
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.ExtractorLink
class ErrorLoadingException(message: String) : Exception(message)
class APIRepository(val api: MainAPI) {
val name : String get() = api.name
val mainUrl : String get() = api.mainUrl
suspend fun load(url: String): Resource<LoadResponse> {
return safeApiCall {
api.load(url) ?: throw ErrorLoadingException("Error Loading")
}
}
suspend fun search(query: String): Resource<ArrayList<SearchResponse>> {
return safeApiCall {
api.search(query) ?: throw ErrorLoadingException("Error Loading")
}
}
suspend fun quickSearch(query: String): Resource<ArrayList<SearchResponse>> {
return safeApiCall {
api.quickSearch(query) ?: throw ErrorLoadingException("Error Loading")
}
}
suspend fun getMainPage(): Resource<HomePageResponse> {
return safeApiCall {
api.getMainPage() ?: throw ErrorLoadingException("Error Loading")
}
}
fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
return normalSafeApiCall { api.loadLinks(data, isCasting, subtitleCallback, callback) } ?: false
}
}

View file

@ -0,0 +1,105 @@
package com.lagradost.cloudstream3.ui.home
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.cardview.widget.CardView
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl
import com.lagradost.cloudstream3.*
import kotlinx.android.synthetic.main.home_result_grid.view.*
class HomeChildItemAdapter(
var cardList: List<Any>,
private val clickCallback: (SearchResponse) -> Unit
) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val layout = R.layout.home_result_grid
return CardViewHolder(
LayoutInflater.from(parent.context).inflate(layout, parent, false), clickCallback
)
}
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, private val clickCallback: (SearchResponse) -> Unit) :
RecyclerView.ViewHolder(itemView) {
val cardView: ImageView = itemView.imageView
private val cardText: TextView = itemView.imageText
private val textType: TextView? = itemView.text_type
// val search_result_lang: ImageView? = itemView.search_result_lang
private val textIsDub: View? = itemView.text_is_dub
private val textIsSub: View? = itemView.text_is_sub
//val cardTextExtra: TextView? = itemView.imageTextExtra
//val imageTextProvider: TextView? = itemView.imageTextProvider
private val bg: CardView = itemView.backgroundCard
fun bind(card: Any) {
if (card is SearchResponse) { // GENERIC
textType?.text = when (card.type) {
TvType.Anime -> "Anime"
TvType.Movie -> "Movie"
TvType.ONA -> "ONA"
TvType.TvSeries -> "TV"
}
// search_result_lang?.visibility = View.GONE
textIsDub?.visibility = View.GONE
textIsSub?.visibility = View.GONE
cardText.text = card.name
//imageTextProvider.text = card.apiName
if (!card.posterUrl.isNullOrEmpty()) {
val glideUrl =
GlideUrl(card.posterUrl)
Glide.with(cardView.context)
.load(glideUrl)
.into(cardView)
}
bg.setOnClickListener {
clickCallback.invoke(card)
// (activity as AppCompatActivity).loadResult(card.url, card.slug, card.apiName)
}
when (card) {
is AnimeSearchResponse -> {
if (card.dubStatus?.size == 1) {
//search_result_lang?.visibility = View.VISIBLE
if (card.dubStatus.contains(DubStatus.Dubbed)) {
textIsDub?.visibility = View.VISIBLE
//search_result_lang?.setColorFilter(ContextCompat.getColor(activity, R.color.dubColor))
} else if (card.dubStatus.contains(DubStatus.Subbed)) {
//search_result_lang?.setColorFilter(ContextCompat.getColor(activity, R.color.subColor))
textIsSub?.visibility = View.VISIBLE
}
}
}
}
}
}
}
}

View file

@ -4,14 +4,20 @@ import android.os.Bundle
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.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.setKey
import com.lagradost.cloudstream3.utils.HOMEPAGE_API
import kotlinx.android.synthetic.main.fragment_child_downloads.*
import kotlinx.android.synthetic.main.fragment_home.*
class HomeFragment : Fragment() { class HomeFragment : Fragment() {
private lateinit var homeViewModel: HomeViewModel private lateinit var homeViewModel: HomeViewModel
override fun onCreateView( override fun onCreateView(
@ -21,11 +27,40 @@ class HomeFragment : Fragment() {
): View? { ): View? {
homeViewModel = homeViewModel =
ViewModelProvider(this).get(HomeViewModel::class.java) ViewModelProvider(this).get(HomeViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_home, container, false)
val textView: TextView = root.findViewById(R.id.text_home) return inflater.inflate(R.layout.fragment_home, container, false)
homeViewModel.text.observe(viewLifecycleOwner, Observer { }
textView.text = it
}) override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
return root super.onViewCreated(view, savedInstanceState)
observe(homeViewModel.apiName) {
context?.setKey(HOMEPAGE_API, it)
}
observe(homeViewModel.page) {
when (it) {
is Resource.Success -> {
val d = it.value
(home_master_recycler?.adapter as ParentItemAdapter?)?.itemList = d.items
home_master_recycler?.adapter?.notifyDataSetChanged()
}
is Resource.Failure -> {
}
is Resource.Loading -> {
}
}
}
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> = ParentItemAdapter(listOf()) {
}
home_master_recycler.adapter = adapter
home_master_recycler.layoutManager = GridLayoutManager(context, 1)
homeViewModel.load(context?.getKey(HOMEPAGE_API))
} }
} }

View file

@ -0,0 +1,51 @@
package com.lagradost.cloudstream3.ui.home
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.HomePageList
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.SearchResponse
import kotlinx.android.synthetic.main.fragment_home.*
import kotlinx.android.synthetic.main.homepage_parent.view.*
class ParentItemAdapter(
var itemList: List<HomePageList>,
private val clickCallback: (SearchResponse) -> Unit
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, i: Int): ParentViewHolder {
val layout = R.layout.homepage_parent
return ParentViewHolder(
LayoutInflater.from(parent.context).inflate(layout, parent, false), clickCallback
)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is ParentViewHolder -> {
holder.bind(itemList[position])
}
}
}
override fun getItemCount(): Int {
return itemList.size
}
class ParentViewHolder
constructor(itemView: View, private val clickCallback: (SearchResponse) -> Unit) :
RecyclerView.ViewHolder(itemView) {
val title: TextView = itemView.home_parent_item_title
val recyclerView: RecyclerView = itemView.home_child_recyclerview
fun bind(info: HomePageList) {
title.text = info.name
recyclerView.adapter = HomeChildItemAdapter(info.list, clickCallback)
recyclerView.layoutManager = GridLayoutManager(itemView.context, 1)
(recyclerView.adapter as HomeChildItemAdapter).notifyDataSetChanged()
}
}
}

View file

@ -3,11 +3,35 @@ package com.lagradost.cloudstream3.ui.home
import androidx.lifecycle.LiveData 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 com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.HomePageResponse
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.ui.APIRepository
import kotlinx.coroutines.launch
class HomeViewModel : ViewModel() { class HomeViewModel : ViewModel() {
var repo: APIRepository? = null
private val _text = MutableLiveData<String>().apply { private val _apiName = MutableLiveData<String>()
value = "This is home Fragment" val apiName: LiveData<String> = _apiName
private val _page = MutableLiveData<Resource<HomePageResponse>>()
val page: LiveData<Resource<HomePageResponse>> = _page
private fun autoloadRepo(): APIRepository {
return APIRepository(apis.first { it.hasMainPage })
}
fun load(preferredApiName: String?) = viewModelScope.launch {
val api = getApiFromNameNull(preferredApiName)
repo = if (api?.hasMainPage == true) {
APIRepository(api)
} else {
autoloadRepo()
}
_page.postValue(Resource.Loading())
_page.postValue(repo?.getMainPage())
} }
val text: LiveData<String> = _text
} }

View file

@ -7,6 +7,7 @@ import com.lagradost.cloudstream3.APIHolder.getApiFromName
import com.lagradost.cloudstream3.APIHolder.getId import com.lagradost.cloudstream3.APIHolder.getId
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.ui.APIRepository
import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultSeason import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultSeason
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState
@ -20,6 +21,8 @@ const val EPISODE_RANGE_SIZE = 50
const val EPISODE_RANGE_OVERLOAD = 60 const val EPISODE_RANGE_OVERLOAD = 60
class ResultViewModel : ViewModel() { class ResultViewModel : ViewModel() {
var repo : APIRepository? = null
private val _resultResponse: MutableLiveData<Resource<Any?>> = MutableLiveData() private val _resultResponse: MutableLiveData<Resource<Any?>> = MutableLiveData()
private val _episodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData() private val _episodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData()
private val _publicEpisodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData() private val _publicEpisodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData()
@ -137,19 +140,14 @@ class ResultViewModel : ViewModel() {
updateEpisodes(context, null, copy, selectedSeason.value) updateEpisodes(context, null, copy, selectedSeason.value)
} }
// THIS SHOULD AT LEAST CLEAN IT UP, SO APIS CAN SWITCH DOMAIN
private fun getId(url: String, api: MainAPI): Int {
return url.replace(api.mainUrl, "").hashCode()
}
fun load(context: Context, url: String, apiName: String) = viewModelScope.launch { fun load(context: Context, url: String, apiName: String) = viewModelScope.launch {
_resultResponse.postValue(Resource.Loading(url)) _resultResponse.postValue(Resource.Loading(url))
_apiName.postValue(apiName) _apiName.postValue(apiName)
val api = getApiFromName(apiName) val api = getApiFromName(apiName)
val data = safeApiCall { repo = APIRepository(api)
api.load(url)
} val data = repo?.load(url)
_resultResponse.postValue(data) _resultResponse.postValue(data)
@ -278,7 +276,7 @@ class ResultViewModel : ViewModel() {
val links = ArrayList<ExtractorLink>() val links = ArrayList<ExtractorLink>()
val subs = ArrayList<SubtitleFile>() val subs = ArrayList<SubtitleFile>()
return safeApiCall { return safeApiCall {
getApiFromName(_apiName.value).loadLinks(data, isCasting, { subtitleFile -> repo?.loadLinks(data, isCasting, { subtitleFile ->
if (!subs.any { it.url == subtitleFile.url }) { if (!subs.any { it.url == subtitleFile.url }) {
subs.add(subtitleFile) subs.add(subtitleFile)
_allEpisodesSubs.value?.set(id, subs) _allEpisodesSubs.value?.set(id, subs)

View file

@ -7,12 +7,14 @@ import androidx.lifecycle.viewModelScope
import com.lagradost.cloudstream3.APIHolder.allApi import com.lagradost.cloudstream3.APIHolder.allApi
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.ui.APIRepository
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class SearchViewModel : ViewModel() { class SearchViewModel : ViewModel() {
private val _searchResponse: MutableLiveData<Resource<ArrayList<Any>>> = MutableLiveData() private val _searchResponse: MutableLiveData<Resource<ArrayList<Any>>> = MutableLiveData()
val searchResponse: LiveData<Resource<ArrayList<Any>>> get() = _searchResponse val searchResponse: LiveData<Resource<ArrayList<Any>>> get() = _searchResponse
var searchCounter = 0 var searchCounter = 0
private val repo = APIRepository(allApi)
private fun clearSearch() { private fun clearSearch() {
_searchResponse.postValue(Resource.Success(ArrayList())) _searchResponse.postValue(Resource.Success(ArrayList()))
@ -26,9 +28,8 @@ class SearchViewModel : ViewModel() {
} }
val localSearchCounter = searchCounter val localSearchCounter = searchCounter
_searchResponse.postValue(Resource.Loading()) _searchResponse.postValue(Resource.Loading())
val data = safeApiCall { val data = repo.search(query)
allApi.search(query)
}
if(localSearchCounter != searchCounter) return@launch if(localSearchCounter != searchCounter) return@launch
_searchResponse.postValue(data as Resource<ArrayList<Any>>?) _searchResponse.postValue(data as Resource<ArrayList<Any>>?)
} }
@ -41,9 +42,7 @@ class SearchViewModel : ViewModel() {
} }
val localSearchCounter = searchCounter val localSearchCounter = searchCounter
_searchResponse.postValue(Resource.Loading()) _searchResponse.postValue(Resource.Loading())
val data = safeApiCall { val data = repo.quickSearch(query)
allApi.quickSearch(query)
}
if(localSearchCounter != searchCounter) return@launch if(localSearchCounter != searchCounter) return@launch
_searchResponse.postValue(data as Resource<ArrayList<Any>>?) _searchResponse.postValue(data as Resource<ArrayList<Any>>?)

View file

@ -9,6 +9,7 @@ import com.fasterxml.jackson.module.kotlin.KotlinModule
const val DOWNLOAD_HEADER_CACHE = "download_header_cache" const val DOWNLOAD_HEADER_CACHE = "download_header_cache"
const val DOWNLOAD_EPISODE_CACHE = "download_episode_cache" const val DOWNLOAD_EPISODE_CACHE = "download_episode_cache"
const val VIDEO_PLAYER_BRIGHTNESS = "video_player_alpha" const val VIDEO_PLAYER_BRIGHTNESS = "video_player_alpha"
const val HOMEPAGE_API = "home_api_used"
const val PREFERENCES_NAME: String = "rebuild_preference" const val PREFERENCES_NAME: String = "rebuild_preference"

View file

@ -7,17 +7,10 @@
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".ui.home.HomeFragment"> tools:context=".ui.home.HomeFragment">
<TextView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/text_home" android:id="@+id/home_master_recycler"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_marginStart="8dp" tools:listitem="@layout/homepage_parent"
android:layout_marginTop="8dp" />
android:layout_marginEnd="8dp"
android:textAlignment="center"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,125 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- android:layout_width="114dp"
android:layout_height="180dp"-->
<androidx.cardview.widget.CardView
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:layout_margin="2dp"
android:layout_width="114dp"
android:layout_height="180dp"
android:layout_marginBottom="2dp"
android:elevation="10dp"
app:cardCornerRadius="@dimen/roundedImageRadius"
android:id="@+id/backgroundCard"
app:cardBackgroundColor="@color/darkBackground"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<ImageView
android:duplicateParentState="true"
android:id="@+id/imageView"
tools:src="@drawable/example_poster"
android:scaleType="centerCrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/search_poster_descript"/>
<ImageView
android:focusable="false"
android:clickable="false"
android:layout_width="match_parent"
android:layout_height="50dp"
android:src="@drawable/title_shadow"
android:layout_gravity="bottom" android:contentDescription="@string/shadow_descript">
</ImageView>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_gravity="bottom"
android:paddingBottom="5dp"
android:paddingTop="5dp"
android:textColor="@color/textColor"
android:id="@+id/imageText"
android:textStyle="bold"
android:maxLines="2"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:ellipsize="end"
/>
<TextView
android:text="Movie"
android:visibility="gone"
android:id="@+id/text_type"
android:textColor="@color/textColor"
android:paddingRight="10dp"
android:paddingLeft="10dp"
android:paddingTop="4dp"
android:layout_marginBottom="5dp"
android:layout_gravity="start"
android:paddingBottom="8dp"
android:minWidth="50dp"
android:gravity="center"
android:background="@drawable/type_bg_color"
android:layout_width="wrap_content" android:layout_height="wrap_content">
</TextView>
<!--<View
android:id="@+id/search_result_lang"
android:layout_gravity="bottom"
android:layout_width="match_parent"
android:layout_height="4dp"
android:alpha="0.9">
</View>-->
<!--<ImageView
android:src="@drawable/ic_baseline_bookmark_24"
android:id="@+id/search_result_lang"
android:layout_gravity="right"
android:layout_marginTop="-5dp"
android:layout_marginRight="-6.5dp"
android:layout_width="30dp"
android:layout_height="30dp">
</ImageView>-->
<LinearLayout
android:orientation="vertical"
android:layout_gravity="end"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--
<ImageView android:id="@+id/text_is_dub" android:tint="@color/colorPrimary"
android:src="@drawable/ic_baseline_subtitles_24" android:layout_width="wrap_content"
android:layout_height="20dp">
</ImageView>-->
<TextView
android:text="@string/app_dubbed_text"
android:id="@+id/text_is_dub"
android:textColor="@color/textColor"
android:paddingRight="10dp"
android:paddingLeft="10dp"
android:paddingTop="4dp"
android:layout_marginBottom="5dp"
android:layout_gravity="end"
android:paddingBottom="4dp"
android:minWidth="50dp"
android:gravity="center"
android:background="@drawable/dub_bg_color"
android:layout_width="wrap_content" android:layout_height="wrap_content">
</TextView>
<TextView
android:id="@+id/text_is_sub"
android:text="@string/app_subbed_text"
android:layout_gravity="end"
android:textColor="@color/textColor"
android:paddingRight="10dp"
android:paddingLeft="10dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:minWidth="50dp"
android:gravity="center"
android:background="@drawable/sub_bg_color"
android:layout_width="wrap_content" android:layout_height="wrap_content"
>
</TextView>
</LinearLayout>
</androidx.cardview.widget.CardView>

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:background="@color/colorPrimary"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/home_parent_item_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="12sp"
android:textSize="18sp"
tools:text="Trending"
/>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="230dp"
android:layout_marginBottom="20dp"
>
<androidx.recyclerview.widget.RecyclerView
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:id="@+id/home_child_recyclerview"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:listitem="@layout/home_result_grid"
/>
</RelativeLayout>
</LinearLayout>

View file

@ -9,10 +9,8 @@
android:focusable="true" android:focusable="true"
android:clickable="true" android:clickable="true"
android:id="@+id/search_result_root" android:id="@+id/search_result_root"
> >
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:foreground="?android:attr/selectableItemBackgroundBorderless" android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:layout_margin="2dp" android:layout_margin="2dp"
android:layout_width="match_parent" android:layout_width="match_parent"