AquaStream/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPrevie...

604 lines
24 KiB
Kotlin
Raw Normal View History

2022-12-28 11:51:55 +00:00
package com.lagradost.cloudstream3.ui.home
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
2022-12-28 14:25:22 +00:00
import android.widget.FrameLayout
2022-12-28 11:51:55 +00:00
import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.lifecycle.findViewTreeLifecycleOwner
2022-12-28 11:51:55 +00:00
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
2022-12-28 11:51:55 +00:00
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipDrawable
2023-08-02 19:00:04 +00:00
import com.google.android.material.chip.ChipGroup
2022-12-28 11:51:55 +00:00
import com.lagradost.cloudstream3.APIHolder.getId
import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity
2023-08-01 02:03:43 +00:00
import com.lagradost.cloudstream3.CommonActivity.activity
2023-01-21 19:05:37 +00:00
import com.lagradost.cloudstream3.HomePageList
2022-12-28 11:51:55 +00:00
import com.lagradost.cloudstream3.LoadResponse
2023-08-01 02:03:43 +00:00
import com.lagradost.cloudstream3.MainActivity
2022-12-28 11:51:55 +00:00
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.databinding.FragmentHomeHeadBinding
import com.lagradost.cloudstream3.databinding.FragmentHomeHeadTvBinding
2022-12-28 11:51:55 +00:00
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.debugException
import com.lagradost.cloudstream3.mvvm.observe
2022-12-28 11:51:55 +00:00
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.selectHomepage
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
2022-12-28 11:51:55 +00:00
import com.lagradost.cloudstream3.ui.result.ResultViewModel2
import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST
2023-07-28 02:18:28 +00:00
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
2022-12-28 11:51:55 +00:00
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_SHOW_METADATA
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showOptionSelectStringRes
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
2023-08-02 03:30:50 +00:00
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbarMargin
2022-12-29 02:08:08 +00:00
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbarView
2022-12-28 11:51:55 +00:00
import com.lagradost.cloudstream3.utils.UIHelper.setImage
class HomeParentItemAdapterPreview(
items: MutableList<HomeViewModel.ExpandableHomepageList>,
private val viewModel: HomeViewModel,
) : ParentItemAdapter(items, clickCallback = {
viewModel.click(it)
}, moreInfoClickCallback = {
viewModel.popup(it)
}, expandCallback = {
viewModel.expand(it)
}) {
2022-12-28 11:51:55 +00:00
val headItems = 1
companion object {
2022-12-28 14:25:22 +00:00
private const val VIEW_TYPE_HEADER = 2
private const val VIEW_TYPE_ITEM = 1
2022-12-28 11:51:55 +00:00
}
override fun getItemViewType(position: Int) = when (position) {
0 -> VIEW_TYPE_HEADER
else -> VIEW_TYPE_ITEM
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is HeaderViewHolder -> {}
else -> super.onBindViewHolder(holder, position - headItems)
2022-12-28 11:51:55 +00:00
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
VIEW_TYPE_HEADER -> {
val inflater = LayoutInflater.from(parent.context)
val binding = if (isTvSettings()) FragmentHomeHeadTvBinding.inflate(
inflater,
2022-12-28 11:51:55 +00:00
parent,
false
) else FragmentHomeHeadBinding.inflate(inflater, parent, false)
HeaderViewHolder(
binding,
viewModel,
)
2022-12-28 11:51:55 +00:00
}
2022-12-28 11:51:55 +00:00
VIEW_TYPE_ITEM -> super.onCreateViewHolder(parent, viewType)
else -> error("Unhandled viewType=$viewType")
}
}
override fun getItemCount(): Int {
return super.getItemCount() + headItems
}
override fun getItemId(position: Int): Long {
if (position == 0) return 0//previewData.hashCode().toLong()
2022-12-28 11:51:55 +00:00
return super.getItemId(position - headItems)
}
override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) {
when (holder) {
is HeaderViewHolder -> {
holder.onViewDetachedFromWindow()
}
2022-12-28 11:51:55 +00:00
else -> super.onViewDetachedFromWindow(holder)
}
}
override fun onViewAttachedToWindow(holder: RecyclerView.ViewHolder) {
when (holder) {
is HeaderViewHolder -> {
holder.onViewAttachedToWindow()
}
2022-12-28 11:51:55 +00:00
else -> super.onViewAttachedToWindow(holder)
}
}
class HeaderViewHolder
constructor(
val binding: ViewBinding,
val viewModel: HomeViewModel,
) : RecyclerView.ViewHolder(binding.root) {
private var previewAdapter: HomeScrollAdapter = HomeScrollAdapter()
private var resumeAdapter: HomeChildItemAdapter = HomeChildItemAdapter(
ArrayList(),
nextFocusUp = itemView.nextFocusUpId,
nextFocusDown = itemView.nextFocusDownId
) { callback ->
if (callback.action != SEARCH_ACTION_SHOW_METADATA) {
viewModel.click(callback)
return@HomeChildItemAdapter
}
callback.view.context?.getActivity()?.showOptionSelectStringRes(
callback.view,
callback.card.posterUrl,
listOf(
R.string.action_open_watching,
R.string.action_remove_watching
),
listOf(
R.string.action_open_play,
R.string.action_open_watching,
R.string.action_remove_watching
)
) { (isTv, actionId) ->
when (actionId + if (isTv) 0 else 1) {
// play
0 -> {
viewModel.click(
SearchClickCallback(
START_ACTION_RESUME_LATEST,
callback.view,
-1,
callback.card
)
)
}
//info
1 -> {
viewModel.click(
SearchClickCallback(
SEARCH_ACTION_LOAD,
callback.view,
-1,
callback.card
)
)
}
// remove
2 -> {
val card = callback.card
if (card is DataStoreHelper.ResumeWatchingResult) {
DataStoreHelper.removeLastWatched(card.parentId)
viewModel.reloadStored()
}
}
}
}
}
private var bookmarkAdapter: HomeChildItemAdapter = HomeChildItemAdapter(
ArrayList(),
nextFocusUp = itemView.nextFocusUpId,
nextFocusDown = itemView.nextFocusDownId
) { callback ->
if (callback.action != SEARCH_ACTION_SHOW_METADATA) {
viewModel.click(callback)
return@HomeChildItemAdapter
}
callback.view.context?.getActivity()?.showOptionSelectStringRes(
callback.view,
callback.card.posterUrl,
listOf(
R.string.action_open_watching,
R.string.action_remove_from_bookmarks,
),
listOf(
R.string.action_open_play,
R.string.action_open_watching,
R.string.action_remove_from_bookmarks
)
) { (isTv, actionId) ->
when (actionId + if (isTv) 0 else 1) { // play
0 -> {
viewModel.click(
SearchClickCallback(
START_ACTION_RESUME_LATEST,
callback.view,
-1,
callback.card
)
)
}
1 -> { // info
viewModel.click(
SearchClickCallback(
SEARCH_ACTION_LOAD,
callback.view,
-1,
callback.card
)
)
}
2 -> { // remove
DataStoreHelper.setResultWatchState(
callback.card.id,
WatchType.NONE.internalId
)
viewModel.reloadStored()
}
}
}
}
private val previewViewpager: ViewPager2 =
itemView.findViewById(R.id.home_preview_viewpager)
private val previewHeader: FrameLayout = itemView.findViewById(R.id.home_preview)
private var resumeHolder: View = itemView.findViewById(R.id.home_watch_holder)
private var resumeRecyclerView: RecyclerView =
itemView.findViewById(R.id.home_watch_child_recyclerview)
private var bookmarkHolder: View = itemView.findViewById(R.id.home_bookmarked_holder)
private var bookmarkRecyclerView: RecyclerView =
itemView.findViewById(R.id.home_bookmarked_child_recyclerview)
2023-08-02 03:30:50 +00:00
private var homeAccount: View? =
itemView.findViewById(R.id.home_switch_account)
private var topPadding : View? = itemView.findViewById(R.id.home_padding)
private val homeNonePadding: View = itemView.findViewById(R.id.home_none_padding)
2022-12-28 11:51:55 +00:00
private val previewCallback: ViewPager2.OnPageChangeCallback =
object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
previewAdapter.apply {
2022-12-28 11:51:55 +00:00
if (position >= itemCount - 1 && hasMoreItems) {
hasMoreItems = false // don't make two requests
viewModel.loadMoreHomeScrollResponses()
2022-12-28 11:51:55 +00:00
}
}
val item = previewAdapter.getItem(position) ?: return
onSelect(item, position)
}
}
fun onSelect(item: LoadResponse, position: Int) {
(binding as? FragmentHomeHeadTvBinding)?.apply {
homePreviewDescription.isGone =
item.plot.isNullOrBlank()
homePreviewDescription.text =
item.plot ?: ""
homePreviewText.text = item.name
homePreviewTags.apply {
removeAllViews()
item.tags?.forEach { tag ->
val chip = Chip(context)
val chipDrawable =
ChipDrawable.createFromAttributes(
context,
null,
0,
R.style.ChipFilledSemiTransparent
2022-12-28 11:51:55 +00:00
)
chip.setChipDrawable(chipDrawable)
chip.text = tag
chip.isChecked = false
chip.isCheckable = false
chip.isFocusable = false
chip.isClickable = false
addView(chip)
}
}
homePreviewTags.isGone =
item.tags.isNullOrEmpty()
homePreviewPlayBtt.setOnClickListener { view ->
viewModel.click(
LoadClickCallback(
START_ACTION_RESUME_LATEST,
view,
position,
item
)
)
}
2022-12-28 11:51:55 +00:00
homePreviewInfoBtt.setOnClickListener { view ->
viewModel.click(
LoadClickCallback(0, view, position, item)
)
}
2023-01-09 01:15:06 +00:00
}
(binding as? FragmentHomeHeadBinding)?.apply {
homePreviewImage.setImage(item.posterUrl, item.posterHeaders)
homePreviewPlay.setOnClickListener { view ->
viewModel.click(
LoadClickCallback(
START_ACTION_RESUME_LATEST,
view,
position,
item
)
)
}
2023-01-09 01:15:06 +00:00
homePreviewInfo.setOnClickListener { view ->
viewModel.click(
LoadClickCallback(0, view, position, item)
)
}
2023-01-09 01:15:06 +00:00
// very ugly code, but I don't care
val id = item.getId()
val watchType =
DataStoreHelper.getResultWatchState(id)
homePreviewBookmark.setText(watchType.stringRes)
homePreviewBookmark.setCompoundDrawablesWithIntrinsicBounds(
null,
ContextCompat.getDrawable(
homePreviewBookmark.context,
watchType.iconRes
),
null,
null
)
2023-01-09 01:15:06 +00:00
homePreviewBookmark.setOnClickListener { fab ->
fab.context.getActivity()?.showBottomDialog(
WatchType.values()
.map { fab.context.getString(it.stringRes) }
.toList(),
DataStoreHelper.getResultWatchState(id).ordinal,
fab.context.getString(R.string.action_add_to_bookmarks),
showApply = false,
{}) {
val newValue = WatchType.values()[it]
homePreviewBookmark.setCompoundDrawablesWithIntrinsicBounds(
null,
ContextCompat.getDrawable(
homePreviewBookmark.context,
newValue.iconRes
),
null,
null
)
homePreviewBookmark.setText(newValue.stringRes)
2023-01-09 01:15:06 +00:00
ResultViewModel2.updateWatchStatus(
item,
newValue
)
}
2022-12-28 11:51:55 +00:00
}
}
}
2022-12-28 11:51:55 +00:00
fun onViewDetachedFromWindow() {
previewViewpager.unregisterOnPageChangeCallback(previewCallback)
2022-12-28 11:51:55 +00:00
}
fun onViewAttachedToWindow() {
previewViewpager.registerOnPageChangeCallback(previewCallback)
binding.root.findViewTreeLifecycleOwner()?.apply {
observe(viewModel.preview) {
updatePreview(it)
}
if (binding is FragmentHomeHeadTvBinding) {
observe(viewModel.apiName) { name ->
binding.homePreviewChangeApi.text = name
binding.homePreviewChangeApi2.text = name
}
}
observe(viewModel.resumeWatching) {
updateResume(it)
}
observe(viewModel.bookmarks) {
updateBookmarks(it)
}
2023-07-18 20:18:14 +00:00
observe(viewModel.availableWatchStatusTypes) { (checked, visible) ->
for ((chip, watch) in toggleList) {
chip.apply {
isVisible = visible.contains(watch)
isChecked = checked.contains(watch)
}
}
2023-08-02 19:00:04 +00:00
toggleListHolder?.isGone = visible.isEmpty()
}
} ?: debugException { "Expected findViewTreeLifecycleOwner" }
2022-12-28 11:51:55 +00:00
}
private val toggleList = listOf<Pair<Chip, WatchType>>(
Pair(itemView.findViewById(R.id.home_type_watching_btt), WatchType.WATCHING),
Pair(itemView.findViewById(R.id.home_type_completed_btt), WatchType.COMPLETED),
Pair(itemView.findViewById(R.id.home_type_dropped_btt), WatchType.DROPPED),
Pair(itemView.findViewById(R.id.home_type_on_hold_btt), WatchType.ONHOLD),
Pair(itemView.findViewById(R.id.home_plan_to_watch_btt), WatchType.PLANTOWATCH),
2022-12-28 11:51:55 +00:00
)
2023-08-02 19:00:04 +00:00
private val toggleListHolder : ChipGroup? = itemView.findViewById(R.id.home_type_holder)
2022-12-28 11:51:55 +00:00
init {
previewViewpager.setPageTransformer(HomeScrollTransformer())
previewViewpager.adapter = previewAdapter
resumeRecyclerView.adapter = resumeAdapter
bookmarkRecyclerView.adapter = bookmarkAdapter
resumeRecyclerView.setLinearListLayout(nextLeft = R.id.nav_rail_view, nextRight = FOCUS_SELF)
bookmarkRecyclerView.setLinearListLayout(nextLeft = R.id.nav_rail_view, nextRight = FOCUS_SELF)
2023-07-28 02:18:28 +00:00
2023-08-02 03:30:50 +00:00
fixPaddingStatusbarMargin(topPadding)
for ((chip, watch) in toggleList) {
chip.isChecked = false
chip.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
viewModel.loadStoredData(setOf(watch))
}
// Else if all are unchecked -> Do not load data
else if (toggleList.all { !it.first.isChecked }) {
viewModel.loadStoredData(emptySet())
}
}
}
2023-08-02 03:30:50 +00:00
homeAccount?.setOnClickListener { v ->
DataStoreHelper.showWhoIsWatching(v?.context ?: return@setOnClickListener)
}
(binding as? FragmentHomeHeadTvBinding)?.apply {
homePreviewChangeApi.setOnClickListener { view ->
view.context.selectHomepage(viewModel.repo?.name) { api ->
viewModel.loadAndCancel(api, forceReload = true, fromUI = true)
}
}
homePreviewChangeApi2.setOnClickListener { view ->
view.context.selectHomepage(viewModel.repo?.name) { api ->
viewModel.loadAndCancel(api, forceReload = true, fromUI = true)
}
}
2022-12-28 14:25:22 +00:00
// This makes the hidden next buttons only available when on the info button
// Otherwise you might be able to go to the next item without being at the info button
homePreviewInfoBtt.setOnFocusChangeListener { _, hasFocus ->
homePreviewHiddenNextFocus.isFocusable = hasFocus
}
2022-12-28 11:51:55 +00:00
homePreviewPlayBtt.setOnFocusChangeListener { _, hasFocus ->
homePreviewHiddenPrevFocus.isFocusable = hasFocus
2022-12-28 11:51:55 +00:00
}
homePreviewHiddenNextFocus.setOnFocusChangeListener { _, hasFocus ->
2023-08-01 02:03:43 +00:00
if (!hasFocus) return@setOnFocusChangeListener
previewViewpager.setCurrentItem(previewViewpager.currentItem + 1, true)
homePreviewInfoBtt.requestFocus()
}
homePreviewHiddenPrevFocus.setOnFocusChangeListener { _, hasFocus ->
2023-08-01 02:03:43 +00:00
if (!hasFocus) return@setOnFocusChangeListener
if (previewViewpager.currentItem <= 0) {
(activity as? MainActivity)?.binding?.navRailView?.requestFocus()
} else {
previewViewpager.setCurrentItem(previewViewpager.currentItem - 1, true)
binding.homePreviewPlayBtt.requestFocus()
2022-12-28 11:51:55 +00:00
}
}
}
(binding as? FragmentHomeHeadBinding)?.apply {
homeSearch.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
viewModel.queryTextSubmit(query)
return true
2022-12-28 11:51:55 +00:00
}
override fun onQueryTextChange(newText: String): Boolean {
viewModel.queryTextChange(newText)
return true
2022-12-28 11:51:55 +00:00
}
})
2022-12-28 11:51:55 +00:00
}
2022-12-28 14:25:22 +00:00
}
private fun updatePreview(preview: Resource<Pair<Boolean, List<LoadResponse>>>) {
if (binding is FragmentHomeHeadTvBinding) {
binding.homePreviewChangeApi2.isGone = preview is Resource.Success
}
2022-12-29 02:08:08 +00:00
if (preview is Resource.Success) {
homeNonePadding.apply {
2022-12-29 02:08:08 +00:00
val params = layoutParams
params.height = 0
layoutParams = params
}
} else {
fixPaddingStatusbarView(homeNonePadding)
2022-12-29 02:08:08 +00:00
}
2022-12-28 11:51:55 +00:00
when (preview) {
is Resource.Success -> {
if (!previewAdapter.setItems(
2022-12-28 11:51:55 +00:00
preview.value.second,
preview.value.first
)
) {
2022-12-29 02:08:08 +00:00
// this might seam weird and useless, however this prevents a very weird andrid bug were the viewpager is not rendered properly
// I have no idea why that happens, but this is my ducktape solution
previewViewpager.setCurrentItem(0, false)
previewViewpager.beginFakeDrag()
previewViewpager.fakeDragBy(1f)
previewViewpager.endFakeDrag()
2022-12-29 02:08:08 +00:00
previewCallback.onPageSelected(0)
previewHeader.isVisible = true
2022-12-28 11:51:55 +00:00
}
}
2022-12-28 11:51:55 +00:00
else -> {
previewAdapter.setItems(listOf(), false)
previewViewpager.setCurrentItem(0, false)
previewHeader.isVisible = false
2022-12-28 11:51:55 +00:00
}
}
}
private fun updateResume(resumeWatching: List<SearchResponse>) {
resumeHolder.isVisible = resumeWatching.isNotEmpty()
resumeAdapter.updateList(resumeWatching)
2023-01-21 19:05:37 +00:00
if (binding is FragmentHomeHeadBinding) {
binding.homeBookmarkParentItemTitle.setOnClickListener {
viewModel.popup(
2023-01-21 19:05:37 +00:00
HomeViewModel.ExpandableHomepageList(
HomePageList(
binding.homeWatchParentItemTitle.text.toString(),
2023-01-21 19:05:37 +00:00
resumeWatching,
false
), 1, false
)
)
}
}
2022-12-28 11:51:55 +00:00
}
private fun updateBookmarks(data: Pair<Boolean, List<SearchResponse>>) {
val (visible, list) = data
bookmarkHolder.isVisible = visible
bookmarkAdapter.updateList(list)
if (binding is FragmentHomeHeadBinding) {
binding.homeBookmarkParentItemTitle.setOnClickListener {
val items = toggleList.map { it.first }.filter { it.isChecked }
2023-01-21 19:05:37 +00:00
if (items.isEmpty()) return@setOnClickListener // we don't want to show an empty dialog
val textSum = items
.mapNotNull { it.text }.joinToString()
viewModel.popup(
2023-01-21 19:05:37 +00:00
HomeViewModel.ExpandableHomepageList(
HomePageList(
textSum,
list,
2023-01-21 19:05:37 +00:00
false
), 1, false
)
)
}
}
2022-12-28 11:51:55 +00:00
}
}
}