forked from recloudstream/cloudstream
fixed homepage
This commit is contained in:
parent
1c494f0ce2
commit
f268418190
7 changed files with 316 additions and 227 deletions
|
@ -1,40 +1,39 @@
|
||||||
package com.lagradost.cloudstream3.plugins
|
package com.lagradost.cloudstream3.plugins
|
||||||
|
|
||||||
import android.app.*
|
import android.app.*
|
||||||
import dalvik.system.PathClassLoader
|
import android.content.Context
|
||||||
import com.google.gson.Gson
|
|
||||||
import android.content.res.AssetManager
|
import android.content.res.AssetManager
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.os.Environment
|
|
||||||
import android.widget.Toast
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.os.Environment
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
import com.google.gson.Gson
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
|
import com.lagradost.cloudstream3.APIHolder.removePluginMapping
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
|
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||||
import com.lagradost.cloudstream3.plugins.RepositoryManager.ONLINE_PLUGINS_FOLDER
|
|
||||||
import com.lagradost.cloudstream3.plugins.RepositoryManager.downloadPluginToFile
|
|
||||||
import com.lagradost.cloudstream3.CommonActivity.showToast
|
import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||||
import com.lagradost.cloudstream3.plugins.RepositoryManager.getRepoPlugins
|
|
||||||
import com.lagradost.cloudstream3.ui.settings.extensions.REPOSITORIES_KEY
|
|
||||||
import com.lagradost.cloudstream3.ui.settings.extensions.RepositoryData
|
|
||||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename
|
|
||||||
import com.lagradost.cloudstream3.APIHolder.removePluginMapping
|
|
||||||
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
|
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
|
||||||
import com.lagradost.cloudstream3.mvvm.debugPrint
|
import com.lagradost.cloudstream3.mvvm.debugPrint
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||||
|
import com.lagradost.cloudstream3.plugins.RepositoryManager.ONLINE_PLUGINS_FOLDER
|
||||||
import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES
|
import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
import com.lagradost.cloudstream3.plugins.RepositoryManager.downloadPluginToFile
|
||||||
|
import com.lagradost.cloudstream3.plugins.RepositoryManager.getRepoPlugins
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.extensions.REPOSITORIES_KEY
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.extensions.RepositoryData
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorApi
|
import com.lagradost.cloudstream3.utils.ExtractorApi
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
||||||
|
import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename
|
||||||
import com.lagradost.cloudstream3.utils.extractorApis
|
import com.lagradost.cloudstream3.utils.extractorApis
|
||||||
|
import dalvik.system.PathClassLoader
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
@ -217,7 +216,7 @@ object PluginManager {
|
||||||
* 3. If outdated download and load the plugin
|
* 3. If outdated download and load the plugin
|
||||||
* 4. Else load the plugin normally
|
* 4. Else load the plugin normally
|
||||||
**/
|
**/
|
||||||
fun updateAllOnlinePluginsAndLoadThem(activity: Activity) = ioSafe {
|
fun updateAllOnlinePluginsAndLoadThem(activity: Activity) {
|
||||||
// Load all plugins as fast as possible!
|
// Load all plugins as fast as possible!
|
||||||
loadAllOnlinePlugins(activity)
|
loadAllOnlinePlugins(activity)
|
||||||
|
|
||||||
|
@ -227,7 +226,7 @@ object PluginManager {
|
||||||
val urls = (getKey<Array<RepositoryData>>(REPOSITORIES_KEY)
|
val urls = (getKey<Array<RepositoryData>>(REPOSITORIES_KEY)
|
||||||
?: emptyArray()) + PREBUILT_REPOSITORIES
|
?: emptyArray()) + PREBUILT_REPOSITORIES
|
||||||
|
|
||||||
val onlinePlugins = urls.toList().amap {
|
val onlinePlugins = urls.toList().apmap {
|
||||||
getRepoPlugins(it.url)?.toList() ?: emptyList()
|
getRepoPlugins(it.url)?.toList() ?: emptyList()
|
||||||
}.flatten().distinctBy { it.second.url }
|
}.flatten().distinctBy { it.second.url }
|
||||||
|
|
||||||
|
@ -248,7 +247,7 @@ object PluginManager {
|
||||||
|
|
||||||
val updatedPlugins = mutableListOf<String>()
|
val updatedPlugins = mutableListOf<String>()
|
||||||
|
|
||||||
outdatedPlugins.amap { pluginData ->
|
outdatedPlugins.apmap { pluginData ->
|
||||||
if (pluginData.isDisabled) {
|
if (pluginData.isDisabled) {
|
||||||
//updatedPlugins.add(activity.getString(R.string.single_plugin_disabled, pluginData.onlineData.second.name))
|
//updatedPlugins.add(activity.getString(R.string.single_plugin_disabled, pluginData.onlineData.second.name))
|
||||||
unloadPlugin(pluginData.savedData.filePath)
|
unloadPlugin(pluginData.savedData.filePath)
|
||||||
|
@ -279,9 +278,9 @@ object PluginManager {
|
||||||
/**
|
/**
|
||||||
* Use updateAllOnlinePluginsAndLoadThem
|
* Use updateAllOnlinePluginsAndLoadThem
|
||||||
* */
|
* */
|
||||||
fun loadAllOnlinePlugins(activity: Activity) = ioSafe {
|
fun loadAllOnlinePlugins(activity: Activity) {
|
||||||
// Load all plugins as fast as possible!
|
// Load all plugins as fast as possible!
|
||||||
(getPluginsOnline()).toList().amap { pluginData ->
|
(getPluginsOnline()).toList().apmap { pluginData ->
|
||||||
loadPlugin(
|
loadPlugin(
|
||||||
activity,
|
activity,
|
||||||
File(pluginData.filePath),
|
File(pluginData.filePath),
|
||||||
|
@ -290,7 +289,7 @@ object PluginManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadAllLocalPlugins(activity: Activity) = ioSafe {
|
fun loadAllLocalPlugins(activity: Activity) {
|
||||||
val dir = File(LOCAL_PLUGINS_PATH)
|
val dir = File(LOCAL_PLUGINS_PATH)
|
||||||
removeKey(PLUGINS_KEY_LOCAL)
|
removeKey(PLUGINS_KEY_LOCAL)
|
||||||
|
|
||||||
|
@ -298,7 +297,7 @@ object PluginManager {
|
||||||
val res = dir.mkdirs()
|
val res = dir.mkdirs()
|
||||||
if (!res) {
|
if (!res) {
|
||||||
Log.w(TAG, "Failed to create local directories")
|
Log.w(TAG, "Failed to create local directories")
|
||||||
return@ioSafe
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package com.lagradost.cloudstream3.ui.home
|
package com.lagradost.cloudstream3.ui.home
|
||||||
|
|
||||||
import android.animation.LayoutTransition
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
@ -26,13 +25,13 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.transition.ChangeBounds
|
import androidx.transition.ChangeBounds
|
||||||
import androidx.transition.TransitionManager
|
import androidx.transition.TransitionManager
|
||||||
|
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
import com.google.android.material.chip.ChipGroup
|
import com.google.android.material.chip.ChipGroup
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.APIHolder.allProviders
|
|
||||||
import com.lagradost.cloudstream3.APIHolder.apis
|
import com.lagradost.cloudstream3.APIHolder.apis
|
||||||
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
|
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
||||||
|
@ -554,7 +553,9 @@ class HomeFragment : Fragment() {
|
||||||
is Resource.Success -> {
|
is Resource.Success -> {
|
||||||
home_preview?.isVisible = true
|
home_preview?.isVisible = true
|
||||||
(home_preview_viewpager?.adapter as? HomeScrollAdapter)?.apply {
|
(home_preview_viewpager?.adapter as? HomeScrollAdapter)?.apply {
|
||||||
setItems(preview.value)
|
if (!setItems(preview.value.second, preview.value.first)) {
|
||||||
|
home_preview_viewpager?.setCurrentItem(0, false)
|
||||||
|
}
|
||||||
// home_preview_viewpager?.setCurrentItem(1000, false)
|
// home_preview_viewpager?.setCurrentItem(1000, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -563,6 +564,10 @@ class HomeFragment : Fragment() {
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
(home_preview_viewpager?.adapter as? HomeScrollAdapter)?.setItems(
|
||||||
|
listOf(),
|
||||||
|
false
|
||||||
|
)
|
||||||
home_preview?.isVisible = false
|
home_preview?.isVisible = false
|
||||||
context?.fixPaddingStatusbar(home_watch_holder)
|
context?.fixPaddingStatusbar(home_watch_holder)
|
||||||
}
|
}
|
||||||
|
@ -577,17 +582,25 @@ class HomeFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
home_preview_viewpager?.apply {
|
home_preview_viewpager?.apply {
|
||||||
setPageTransformer(false, HomeScrollTransformer())
|
setPageTransformer(HomeScrollTransformer())
|
||||||
adapter = HomeScrollAdapter { load ->
|
val callback: OnPageChangeCallback = object : OnPageChangeCallback() {
|
||||||
load.apply {
|
override fun onPageSelected(position: Int) {
|
||||||
|
(home_preview_viewpager?.adapter as? HomeScrollAdapter)?.apply {
|
||||||
|
if (position >= itemCount - 1 && hasMoreItems) {
|
||||||
|
hasMoreItems = false // dont make two requests
|
||||||
|
homeViewModel.loadMoreHomeScrollResponses()
|
||||||
|
}
|
||||||
|
|
||||||
|
getItem(position)
|
||||||
|
?.apply {
|
||||||
home_preview_title_holder?.let { parent ->
|
home_preview_title_holder?.let { parent ->
|
||||||
TransitionManager.beginDelayedTransition(parent, ChangeBounds())
|
TransitionManager.beginDelayedTransition(parent, ChangeBounds())
|
||||||
}
|
}
|
||||||
|
|
||||||
home_preview_tags?.text = tags?.joinToString(" • ") ?: ""
|
// home_preview_tags?.text = tags?.joinToString(" • ") ?: ""
|
||||||
home_preview_tags?.isGone = tags.isNullOrEmpty()
|
// home_preview_tags?.isGone = tags.isNullOrEmpty()
|
||||||
home_preview_image?.setImage(posterUrl, posterHeaders)
|
// home_preview_image?.setImage(posterUrl, posterHeaders)
|
||||||
home_preview_title?.text = name
|
// home_preview_title?.text = name
|
||||||
|
|
||||||
home_preview_play?.setOnClickListener {
|
home_preview_play?.setOnClickListener {
|
||||||
activity?.loadResult(url, apiName, START_ACTION_RESUME_LATEST)
|
activity?.loadResult(url, apiName, START_ACTION_RESUME_LATEST)
|
||||||
|
@ -598,7 +611,7 @@ class HomeFragment : Fragment() {
|
||||||
//activity.loadSearchResult(random)
|
//activity.loadSearchResult(random)
|
||||||
}
|
}
|
||||||
// very ugly code, but I dont care
|
// very ugly code, but I dont care
|
||||||
val watchType = DataStoreHelper.getResultWatchState(load.getId())
|
val watchType = DataStoreHelper.getResultWatchState(this.getId())
|
||||||
home_preview_bookmark?.setText(watchType.stringRes)
|
home_preview_bookmark?.setText(watchType.stringRes)
|
||||||
home_preview_bookmark?.setCompoundDrawablesWithIntrinsicBounds(
|
home_preview_bookmark?.setCompoundDrawablesWithIntrinsicBounds(
|
||||||
null,
|
null,
|
||||||
|
@ -608,29 +621,37 @@ class HomeFragment : Fragment() {
|
||||||
)
|
)
|
||||||
home_preview_bookmark?.setOnClickListener { fab ->
|
home_preview_bookmark?.setOnClickListener { fab ->
|
||||||
activity?.showBottomDialog(
|
activity?.showBottomDialog(
|
||||||
WatchType.values().map { fab.context.getString(it.stringRes) }
|
WatchType.values()
|
||||||
|
.map { fab.context.getString(it.stringRes) }
|
||||||
.toList(),
|
.toList(),
|
||||||
DataStoreHelper.getResultWatchState(load.getId()).ordinal,
|
DataStoreHelper.getResultWatchState(this.getId()).ordinal,
|
||||||
fab.context.getString(R.string.action_add_to_bookmarks),
|
fab.context.getString(R.string.action_add_to_bookmarks),
|
||||||
showApply = false,
|
showApply = false,
|
||||||
{}) {
|
{}) {
|
||||||
val newValue = WatchType.values()[it]
|
val newValue = WatchType.values()[it]
|
||||||
home_preview_bookmark?.setCompoundDrawablesWithIntrinsicBounds(
|
home_preview_bookmark?.setCompoundDrawablesWithIntrinsicBounds(
|
||||||
null,
|
null,
|
||||||
getDrawable(home_preview_bookmark.context, newValue.iconRes),
|
getDrawable(
|
||||||
|
home_preview_bookmark.context,
|
||||||
|
newValue.iconRes
|
||||||
|
),
|
||||||
null,
|
null,
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
home_preview_bookmark?.setText(newValue.stringRes)
|
home_preview_bookmark?.setText(newValue.stringRes)
|
||||||
|
|
||||||
updateWatchStatus(load, newValue)
|
updateWatchStatus(this, newValue)
|
||||||
reloadStored()
|
reloadStored()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
registerOnPageChangeCallback(callback)
|
||||||
|
adapter = HomeScrollAdapter()
|
||||||
|
}
|
||||||
|
|
||||||
observe(homeViewModel.apiName) { apiName ->
|
observe(homeViewModel.apiName) { apiName ->
|
||||||
currentApiName = apiName
|
currentApiName = apiName
|
||||||
|
|
|
@ -1,60 +1,89 @@
|
||||||
package com.lagradost.cloudstream3.ui.home
|
package com.lagradost.cloudstream3.ui.home
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import androidx.core.view.isGone
|
||||||
import androidx.viewpager.widget.PagerAdapter
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.lagradost.cloudstream3.LoadResponse
|
import com.lagradost.cloudstream3.LoadResponse
|
||||||
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||||
|
import kotlinx.android.synthetic.main.home_scroll_view.view.*
|
||||||
|
|
||||||
|
|
||||||
class HomeScrollAdapter(private val onPrimaryCallback: (LoadResponse) -> Unit) : PagerAdapter() {
|
class HomeScrollAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
private var items: List<LoadResponse> = listOf()
|
private var items: MutableList<LoadResponse> = mutableListOf()
|
||||||
|
var hasMoreItems: Boolean = false
|
||||||
|
|
||||||
fun setItems(newItems: List<LoadResponse>) {
|
fun getItem(position: Int) : LoadResponse? {
|
||||||
items = newItems
|
return items.getOrNull(position)
|
||||||
|
|
||||||
notifyDataSetChanged()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getCount(): Int {
|
fun setItems(newItems: List<LoadResponse>, hasNext: Boolean): Boolean {
|
||||||
return Int.MAX_VALUE//items.size
|
val isSame = newItems.firstOrNull()?.url == items.firstOrNull()?.url
|
||||||
|
hasMoreItems = hasNext
|
||||||
|
|
||||||
|
val diffResult = DiffUtil.calculateDiff(
|
||||||
|
HomeScrollDiffCallback(this.items, newItems)
|
||||||
|
)
|
||||||
|
|
||||||
|
items.clear()
|
||||||
|
items.addAll(newItems)
|
||||||
|
|
||||||
|
|
||||||
|
diffResult.dispatchUpdatesTo(this)
|
||||||
|
|
||||||
|
return isSame
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemPosition(`object`: Any): Int {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
return POSITION_NONE//super.getItemPosition(`object`)
|
return CardViewHolder(
|
||||||
|
LayoutInflater.from(parent.context).inflate(R.layout.home_scroll_view, parent, false),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getItemAtPosition(idx: Int): LoadResponse {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
return items[idx % items.size]
|
when (holder) {
|
||||||
}
|
is CardViewHolder -> {
|
||||||
|
holder.bind(items[position])
|
||||||
override fun setPrimaryItem(container: ViewGroup, position: Int, `object`: Any) {
|
}
|
||||||
super.setPrimaryItem(container, position, `object`)
|
}
|
||||||
onPrimaryCallback.invoke(getItemAtPosition(position))
|
}
|
||||||
}
|
|
||||||
|
class CardViewHolder
|
||||||
override fun instantiateItem(container: ViewGroup, position: Int): Any {
|
constructor(
|
||||||
val image = ImageView(container.context)
|
itemView: View,
|
||||||
val item = getItemAtPosition(position)
|
) :
|
||||||
image.scaleType = ImageView.ScaleType.CENTER_CROP
|
RecyclerView.ViewHolder(itemView) {
|
||||||
image.setImage(item.posterUrl ?: item.backgroundPosterUrl, item.posterHeaders)
|
|
||||||
|
fun bind(card: LoadResponse) {
|
||||||
// val itemView: View = mLayoutInflater.inflate(R.layout.pager_item, container, false)
|
card.apply {
|
||||||
|
itemView.home_scroll_preview_tags?.text = tags?.joinToString(" • ") ?: ""
|
||||||
// val imageView: ImageView = itemView.findViewById<View>(R.id.imageView) as ImageView
|
itemView.home_scroll_preview_tags?.isGone = tags.isNullOrEmpty()
|
||||||
// imageView.setImageResource(mResources.get(position))
|
itemView.home_scroll_preview?.setImage(posterUrl, posterHeaders)
|
||||||
|
itemView.home_scroll_preview_title?.text = name
|
||||||
container.addView(image)
|
}
|
||||||
|
}
|
||||||
return image
|
}
|
||||||
}
|
|
||||||
|
class HomeScrollDiffCallback(
|
||||||
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
|
private val oldList: List<LoadResponse>,
|
||||||
container.removeView(`object` as View)
|
private val newList: List<LoadResponse>
|
||||||
}
|
) :
|
||||||
|
DiffUtil.Callback() {
|
||||||
override fun isViewFromObject(view: View, `object`: Any): Boolean {
|
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
||||||
return view === `object`
|
oldList[oldItemPosition].url == newList[newItemPosition].url
|
||||||
|
|
||||||
|
override fun getOldListSize() = oldList.size
|
||||||
|
|
||||||
|
override fun getNewListSize() = newList.size
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
||||||
|
oldList[oldItemPosition] == newList[newItemPosition]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return items.size
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,13 +1,23 @@
|
||||||
package com.lagradost.cloudstream3.ui.home
|
package com.lagradost.cloudstream3.ui.home
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.viewpager.widget.ViewPager
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
|
|
||||||
class HomeScrollTransformer : ViewPager.PageTransformer {
|
class HomeScrollTransformer : ViewPager2.PageTransformer {
|
||||||
override fun transformPage(page: View, position: Float) {
|
override fun transformPage(page: View, position: Float) {
|
||||||
|
//page.translationX = -position * page.width / 2.0f
|
||||||
|
|
||||||
|
//val params = RecyclerView.LayoutParams(
|
||||||
|
// RecyclerView.LayoutParams.MATCH_PARENT,
|
||||||
|
// 0
|
||||||
|
//)
|
||||||
|
//page.layoutParams = params
|
||||||
|
//progressBar?.layoutParams = params
|
||||||
|
|
||||||
|
val padding = (-position * page.width / 2).toInt()
|
||||||
page.setPadding(
|
page.setPadding(
|
||||||
maxOf(0, (-position * page.width / 2).toInt()), 0,
|
padding, 0,
|
||||||
maxOf(0, (position * page.width / 2).toInt()), 0
|
-padding, 0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,6 +4,7 @@ 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.*
|
||||||
import com.lagradost.cloudstream3.APIHolder.apis
|
import com.lagradost.cloudstream3.APIHolder.apis
|
||||||
import com.lagradost.cloudstream3.APIHolder.filterHomePageListByFilmQuality
|
import com.lagradost.cloudstream3.APIHolder.filterHomePageListByFilmQuality
|
||||||
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
|
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
|
||||||
|
@ -12,17 +13,12 @@ import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.context
|
import com.lagradost.cloudstream3.AcraApplication.Companion.context
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||||
import com.lagradost.cloudstream3.HomePageList
|
|
||||||
import com.lagradost.cloudstream3.LoadResponse
|
|
||||||
import com.lagradost.cloudstream3.MainAPI
|
|
||||||
import com.lagradost.cloudstream3.SearchResponse
|
|
||||||
import com.lagradost.cloudstream3.mvvm.*
|
import com.lagradost.cloudstream3.mvvm.*
|
||||||
import com.lagradost.cloudstream3.ui.APIRepository
|
import com.lagradost.cloudstream3.ui.APIRepository
|
||||||
import com.lagradost.cloudstream3.ui.APIRepository.Companion.noneApi
|
import com.lagradost.cloudstream3.ui.APIRepository.Companion.noneApi
|
||||||
import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi
|
import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi
|
||||||
import com.lagradost.cloudstream3.ui.WatchType
|
import com.lagradost.cloudstream3.ui.WatchType
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioWork
|
|
||||||
import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE
|
import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllResumeStateIds
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllResumeStateIds
|
||||||
|
@ -35,7 +31,6 @@ import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
|
||||||
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
|
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.async
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
|
@ -49,6 +44,8 @@ class HomeViewModel : ViewModel() {
|
||||||
private val _randomItems = MutableLiveData<List<SearchResponse>?>(null)
|
private val _randomItems = MutableLiveData<List<SearchResponse>?>(null)
|
||||||
val randomItems: LiveData<List<SearchResponse>?> = _randomItems
|
val randomItems: LiveData<List<SearchResponse>?> = _randomItems
|
||||||
|
|
||||||
|
private var currentShuffledList: List<SearchResponse> = listOf()
|
||||||
|
|
||||||
private fun autoloadRepo(): APIRepository {
|
private fun autoloadRepo(): APIRepository {
|
||||||
return APIRepository(apis.first { it.hasMainPage })
|
return APIRepository(apis.first { it.hasMainPage })
|
||||||
}
|
}
|
||||||
|
@ -61,9 +58,12 @@ class HomeViewModel : ViewModel() {
|
||||||
val bookmarks: LiveData<Pair<Boolean, List<SearchResponse>>> = _bookmarks
|
val bookmarks: LiveData<Pair<Boolean, List<SearchResponse>>> = _bookmarks
|
||||||
|
|
||||||
private val _resumeWatching = MutableLiveData<List<SearchResponse>>()
|
private val _resumeWatching = MutableLiveData<List<SearchResponse>>()
|
||||||
private val _preview = MutableLiveData<Resource<List<LoadResponse>>>()
|
private val _preview = MutableLiveData<Resource<Pair<Boolean, List<LoadResponse>>>>()
|
||||||
|
private val previewResponses = mutableListOf<LoadResponse>()
|
||||||
|
private val previewResponsesAdded = mutableSetOf<String>()
|
||||||
|
|
||||||
val resumeWatching: LiveData<List<SearchResponse>> = _resumeWatching
|
val resumeWatching: LiveData<List<SearchResponse>> = _resumeWatching
|
||||||
val preview: LiveData<Resource<List<LoadResponse>>> = _preview
|
val preview: LiveData<Resource<Pair<Boolean, List<LoadResponse>>>> = _preview
|
||||||
|
|
||||||
fun loadResumeWatching() = viewModelScope.launchSafe {
|
fun loadResumeWatching() = viewModelScope.launchSafe {
|
||||||
val resumeWatching = withContext(Dispatchers.IO) {
|
val resumeWatching = withContext(Dispatchers.IO) {
|
||||||
|
@ -212,6 +212,40 @@ class HomeViewModel : ViewModel() {
|
||||||
expandAndReturn(name)
|
expandAndReturn(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns the amount of items added and modifies current
|
||||||
|
private suspend fun updatePreviewResponses(
|
||||||
|
current: MutableList<LoadResponse>,
|
||||||
|
alreadyAdded: MutableSet<String>,
|
||||||
|
shuffledList: List<SearchResponse>,
|
||||||
|
size: Int
|
||||||
|
): Int {
|
||||||
|
var count = 0
|
||||||
|
|
||||||
|
val addItems = arrayListOf<SearchResponse>()
|
||||||
|
for (searchResponse in shuffledList) {
|
||||||
|
if (!alreadyAdded.contains(searchResponse.url)) {
|
||||||
|
addItems.add(searchResponse)
|
||||||
|
previewResponsesAdded.add(searchResponse.url)
|
||||||
|
if (++count >= size) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val add = addItems.amap { searchResponse ->
|
||||||
|
repo?.load(searchResponse.url)
|
||||||
|
}.mapNotNull { if (it != null && it is Resource.Success) it.value else null }
|
||||||
|
current.addAll(add)
|
||||||
|
return add.size
|
||||||
|
}
|
||||||
|
|
||||||
|
private var addJob: Job? = null
|
||||||
|
fun loadMoreHomeScrollResponses() {
|
||||||
|
addJob = ioSafe {
|
||||||
|
updatePreviewResponses(previewResponses, previewResponsesAdded, currentShuffledList, 1)
|
||||||
|
_preview.postValue(Resource.Success((previewResponsesAdded.size < currentShuffledList.size) to previewResponses))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun load(api: MainAPI?) = ioSafe {
|
private fun load(api: MainAPI?) = ioSafe {
|
||||||
repo = if (api != null) {
|
repo = if (api != null) {
|
||||||
|
@ -226,6 +260,7 @@ class HomeViewModel : ViewModel() {
|
||||||
if (repo?.hasMainPage == true) {
|
if (repo?.hasMainPage == true) {
|
||||||
_page.postValue(Resource.Loading())
|
_page.postValue(Resource.Loading())
|
||||||
_preview.postValue(Resource.Loading())
|
_preview.postValue(Resource.Loading())
|
||||||
|
addJob?.cancel()
|
||||||
|
|
||||||
when (val data = repo?.getMainPage(1, null)) {
|
when (val data = repo?.getMainPage(1, null)) {
|
||||||
is Resource.Success -> {
|
is Resource.Success -> {
|
||||||
|
@ -241,64 +276,10 @@ class HomeViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
val items = data.value.mapNotNull { it?.items }.flatten()
|
val items = data.value.mapNotNull { it?.items }.flatten()
|
||||||
val responses = ioWork {
|
|
||||||
items.flatMap { it.list }.shuffled().take(6).map { searchResponse ->
|
|
||||||
async { repo?.load(searchResponse.url) }
|
|
||||||
}.map { it.await() }.mapNotNull { if (it != null && it is Resource.Success) it.value else null } }
|
|
||||||
//.amap { searchResponse ->
|
|
||||||
// repo?.load(searchResponse.url)
|
|
||||||
///}
|
|
||||||
|
|
||||||
//.map { searchResponse ->
|
|
||||||
// async { repo?.load(searchResponse.url) }
|
|
||||||
// }.map { it.await() }
|
|
||||||
|
|
||||||
|
|
||||||
if (responses.isEmpty()) {
|
previewResponses.clear()
|
||||||
_preview.postValue(
|
previewResponsesAdded.clear()
|
||||||
Resource.Failure(
|
|
||||||
false,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"No homepage responses"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
_preview.postValue(Resource.Success(responses))
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
items.randomOrNull()?.list?.randomOrNull()?.url?.let { url ->
|
|
||||||
// backup request in case first fails
|
|
||||||
var first = repo?.load(url)
|
|
||||||
if(first == null ||first is Resource.Failure) {
|
|
||||||
first = repo?.load(items.random().list.random().url)
|
|
||||||
}
|
|
||||||
first?.let {
|
|
||||||
_preview.postValue(it)
|
|
||||||
} ?: run {
|
|
||||||
_preview.postValue(
|
|
||||||
Resource.Failure(
|
|
||||||
false,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"No repo found, this should never happen"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} ?: run {
|
|
||||||
_preview.postValue(
|
|
||||||
Resource.Failure(
|
|
||||||
false,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"No homepage items"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}*/
|
|
||||||
|
|
||||||
_page.postValue(Resource.Success(expandable))
|
|
||||||
|
|
||||||
|
|
||||||
//val home = data.value
|
//val home = data.value
|
||||||
if (items.isNotEmpty()) {
|
if (items.isNotEmpty()) {
|
||||||
|
@ -313,9 +294,30 @@ class HomeViewModel : ViewModel() {
|
||||||
context?.filterSearchResultByFilmQuality(currentList.shuffled())
|
context?.filterSearchResultByFilmQuality(currentList.shuffled())
|
||||||
?: currentList.shuffled()
|
?: currentList.shuffled()
|
||||||
|
|
||||||
|
updatePreviewResponses(
|
||||||
|
previewResponses,
|
||||||
|
previewResponsesAdded,
|
||||||
|
randomItems,
|
||||||
|
3
|
||||||
|
)
|
||||||
|
|
||||||
_randomItems.postValue(randomItems)
|
_randomItems.postValue(randomItems)
|
||||||
|
currentShuffledList = randomItems
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (previewResponses.isEmpty()) {
|
||||||
|
_preview.postValue(
|
||||||
|
Resource.Failure(
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"No homepage responses"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
_preview.postValue(Resource.Success((previewResponsesAdded.size < currentShuffledList.size) to previewResponses))
|
||||||
|
}
|
||||||
|
_page.postValue(Resource.Success(expandable))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
_randomItems.postValue(emptyList())
|
_randomItems.postValue(emptyList())
|
||||||
logError(e)
|
logError(e)
|
||||||
|
|
|
@ -287,39 +287,23 @@
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible">
|
tools:visibility="visible">
|
||||||
|
|
||||||
<androidx.viewpager.widget.ViewPager
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
android:id="@+id/home_preview_viewpager"
|
android:id="@+id/home_preview_viewpager"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
</androidx.viewpager.widget.ViewPager>
|
</androidx.viewpager2.widget.ViewPager2>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:visibility="gone"
|
|
||||||
android:id="@+id/home_preview_image"
|
android:id="@+id/home_preview_image"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:alpha="0.8"
|
android:alpha="0.8"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
|
android:visibility="gone"
|
||||||
tools:src="@drawable/example_poster" />
|
tools:src="@drawable/example_poster" />
|
||||||
|
|
||||||
<View
|
|
||||||
android:id="@+id/title_shadow_top"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="150dp"
|
|
||||||
android:layout_gravity="top"
|
|
||||||
android:alpha="1"
|
|
||||||
android:background="@drawable/background_shadow"
|
|
||||||
android:rotation="180"
|
|
||||||
android:visibility="visible" />
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:id="@+id/title_shadow"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="300dp"
|
|
||||||
android:layout_gravity="bottom"
|
|
||||||
android:background="@drawable/background_shadow" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/home_padding"
|
android:id="@+id/home_padding"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -363,44 +347,14 @@
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|
||||||
android:id="@+id/home_preview_title_holder"
|
android:id="@+id/home_preview_title_holder"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="100dp"
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/home_preview_title"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center"
|
|
||||||
android:paddingBottom="10dp"
|
|
||||||
android:paddingHorizontal="30dp"
|
|
||||||
android:textColor="?attr/white"
|
|
||||||
android:textSize="17sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
tools:text="The Perfect Run" />
|
|
||||||
<!--<TextView
|
|
||||||
android:paddingStart="30dp"
|
|
||||||
android:paddingEnd="30dp"
|
|
||||||
android:id="@+id/home_season_tags"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="14sp"
|
|
||||||
tools:text="5 seasons 50 episodes" />-->
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/home_preview_tags"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center"
|
|
||||||
android:paddingStart="30dp"
|
|
||||||
android:paddingEnd="30dp"
|
|
||||||
android:textColor="?attr/white"
|
|
||||||
android:textSize="14sp"
|
|
||||||
tools:text="Hello • World • Tags" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
74
app/src/main/res/layout/home_scroll_view.xml
Normal file
74
app/src/main/res/layout/home_scroll_view.xml
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:background="?attr/primaryGrayBackground"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/home_scroll_preview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
tools:src="@drawable/example_poster" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/title_shadow_top"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="150dp"
|
||||||
|
android:layout_gravity="top"
|
||||||
|
android:alpha="1"
|
||||||
|
android:background="@drawable/background_shadow"
|
||||||
|
android:rotation="180"
|
||||||
|
android:visibility="visible" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/title_shadow"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="300dp"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:background="@drawable/background_shadow" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal|bottom"
|
||||||
|
android:layout_marginBottom="100dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/home_scroll_preview_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingHorizontal="30dp"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
android:textColor="?attr/white"
|
||||||
|
android:textSize="17sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
tools:text="The Perfect Run" />
|
||||||
|
<!--<TextView
|
||||||
|
android:paddingStart="30dp"
|
||||||
|
android:paddingEnd="30dp"
|
||||||
|
android:id="@+id/home_season_tags"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="14sp"
|
||||||
|
tools:text="5 seasons 50 episodes" />-->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/home_scroll_preview_tags"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingStart="30dp"
|
||||||
|
android:paddingEnd="30dp"
|
||||||
|
android:textColor="?attr/white"
|
||||||
|
android:textSize="14sp"
|
||||||
|
tools:text="Hello • World • Tags" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</FrameLayout>
|
Loading…
Reference in a new issue