diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt index 347a8d1a..dd96cc96 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt @@ -42,6 +42,7 @@ import com.lagradost.cloudstream3.ui.AutofitRecyclerView import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST +import com.lagradost.cloudstream3.ui.result.setLinearListLayout import com.lagradost.cloudstream3.ui.search.* import com.lagradost.cloudstream3.ui.search.SearchHelper.handleSearchClickCallback import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings @@ -519,6 +520,16 @@ class HomeFragment : Fragment() { } } + home_main_poster_recyclerview?.adapter = + HomeChildItemAdapter( + mutableListOf(), + R.layout.home_result_big_grid, + nextFocusUp = home_main_poster_recyclerview.nextFocusUpId, + nextFocusDown = home_main_poster_recyclerview.nextFocusDownId + ) { callback -> + homeHandleSearch(callback) + } + home_main_poster_recyclerview.setLinearListLayout() observe(homeViewModel.randomItems) { items -> if (items.isNullOrEmpty()) { toggleMainVisibility(false) @@ -531,15 +542,7 @@ class HomeFragment : Fragment() { } val randomSize = items.size - home_main_poster_recyclerview?.adapter = - HomeChildItemAdapter( - items.toMutableList(), - R.layout.home_result_big_grid, - nextFocusUp = home_main_poster_recyclerview.nextFocusUpId, - nextFocusDown = home_main_poster_recyclerview.nextFocusDownId - ) { callback -> - homeHandleSearch(callback) - } + tempAdapter?.updateList(items) if (context?.isTvSettings() == false) { home_main_poster_recyclerview?.post { (home_main_poster_recyclerview?.layoutManager as CenterZoomLayoutManager?)?.let { manager -> @@ -813,6 +816,8 @@ class HomeFragment : Fragment() { homeHandleSearch(callback) } } + home_watch_child_recyclerview.setLinearListLayout() + home_bookmarked_child_recyclerview.setLinearListLayout() home_watch_child_recyclerview?.adapter = HomeChildItemAdapter( ArrayList(), @@ -901,6 +906,7 @@ class HomeFragment : Fragment() { }, { name -> homeViewModel.expand(name) }) + home_master_recycler.setLinearListLayout() home_master_recycler?.setMaxViewPoolSize(0, Int.MAX_VALUE) home_master_recycler.layoutManager = object : LinearLayoutManager(context) { override fun supportsPredictiveItemAnimations(): Boolean { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt index 4f113eba..3fc1da24 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt @@ -10,6 +10,8 @@ import androidx.recyclerview.widget.ListUpdateCallback import androidx.recyclerview.widget.RecyclerView import com.lagradost.cloudstream3.HomePageList import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.ui.result.LinearListLayout +import com.lagradost.cloudstream3.ui.result.setLinearListLayout import com.lagradost.cloudstream3.ui.search.SearchClickCallback import com.lagradost.cloudstream3.ui.search.SearchFragment.Companion.filterSearchResponse import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings @@ -153,6 +155,7 @@ class ParentItemAdapter( ).apply { isHorizontal = info.isHorizontalImages } + recyclerView.setLinearListLayout() } } @@ -167,7 +170,7 @@ class ParentItemAdapter( isHorizontal = info.isHorizontalImages hasNext = expand.hasNext } - + recyclerView.setLinearListLayout() title.text = info.name recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { 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 index 9acae56b..075e6cf5 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.FrameLayout import android.widget.ImageView import android.widget.TextView import androidx.annotation.LayoutRes @@ -75,9 +76,12 @@ class EpisodeAdapter( } override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) { - if(holder.itemView.hasFocus()) { + if (holder.itemView.hasFocus()) { holder.itemView.clearFocus() } + //(holder.itemView as? FrameLayout?)?.descendantFocusability = + // ViewGroup.FOCUS_BLOCK_DESCENDANTS + if (holder is DownloadButtonViewHolder) { holder.downloadButton.dispose() } @@ -87,11 +91,20 @@ class EpisodeAdapter( if (holder is DownloadButtonViewHolder) { holder.downloadButton.dispose() mBoundViewHolders.remove(holder) + //(holder.itemView as? FrameLayout?)?.descendantFocusability = + // ViewGroup.FOCUS_BLOCK_DESCENDANTS } } override fun onViewAttachedToWindow(holder: RecyclerView.ViewHolder) { if (holder is DownloadButtonViewHolder) { + //println("onViewAttachedToWindow = ${holder.absoluteAdapterPosition}") + //holder.itemView.post { + // if (holder.itemView.isAttachedToWindow) + // (holder.itemView as? FrameLayout?)?.descendantFocusability = + // ViewGroup.FOCUS_AFTER_DESCENDANTS + //} + holder.reattachDownloadButton() } } @@ -145,7 +158,6 @@ class EpisodeAdapter( ) : RecyclerView.ViewHolder(itemView), DownloadButtonViewHolder { override var downloadButton = EasyDownloadButton() - var episodeDownloadBar: ContentLoadingProgressBar? = null var episodeDownloadImage: ImageView? = null var localCard: ResultEpisode? = null @@ -217,17 +229,17 @@ class EpisodeAdapter( } } - parentView.setOnClickListener { + itemView.setOnClickListener { clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card)) } if (isTrueTv) { - parentView.isFocusable = true - parentView.isFocusableInTouchMode = true - parentView.touchscreenBlocksFocus = false + itemView.isFocusable = true + itemView.isFocusableInTouchMode = true + //itemView.touchscreenBlocksFocus = false } - parentView.setOnLongClickListener { + itemView.setOnLongClickListener { clickCallback.invoke(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card)) return@setOnLongClickListener true @@ -242,6 +254,9 @@ class EpisodeAdapter( downloadButton.dispose() val card = localCard if (hasDownloadSupport && card != null) { + if (episodeDownloadBar == null || + episodeDownloadImage == null + ) return val downloadInfo = VideoDownloadManager.getDownloadFileInfoAndUpdateSettings( itemView.context, card.id diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/LinearListLayout.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/LinearListLayout.kt new file mode 100644 index 00000000..59a46264 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/LinearListLayout.kt @@ -0,0 +1,129 @@ +package com.lagradost.cloudstream3.ui.result + +import android.content.Context +import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.lagradost.cloudstream3.mvvm.logError + +fun RecyclerView?.setLinearListLayout(isHorizontal: Boolean = true) { + if(this == null) return + this.layoutManager = + this.context?.let { LinearListLayout(it).apply { if (isHorizontal) setHorizontal() else setVertical() } } + ?: this.layoutManager +} + +class LinearListLayout(context: Context?) : + LinearLayoutManager(context) { + + fun setHorizontal() { + orientation = HORIZONTAL + } + + fun setVertical() { + orientation = VERTICAL + } + + private fun getCorrectParent(focused: View): View? { + var current: View? = focused + val last: ArrayList = arrayListOf(focused) + while (current != null && current !is RecyclerView) { + current = (current.parent as? View?)?.also { last.add(it) } + } + return last.getOrNull(last.count() - 2) + } + + private fun getPosition(view: View?): Int? { + return (view?.layoutParams as? RecyclerView.LayoutParams?)?.absoluteAdapterPosition + } + + private fun getViewFromPos(pos: Int): View? { + for (i in 0 until childCount) { + val child = getChildAt(i) + if ((child?.layoutParams as? RecyclerView.LayoutParams?)?.absoluteAdapterPosition == pos) { + return child + } + } + return null + //return recyclerView.children.firstOrNull { child -> (child.layoutParams as? RecyclerView.LayoutParams?)?.absoluteAdapterPosition == pos) } + } + + /* + private fun scrollTo(position: Int) { + val linearSmoothScroller = LinearSmoothScroller(recyclerView.context) + linearSmoothScroller.targetPosition = position + startSmoothScroll(linearSmoothScroller) + }*/ + + override fun onInterceptFocusSearch(focused: View, direction: Int): View? { + val dir = if (orientation == HORIZONTAL) { + if (direction == View.FOCUS_DOWN || direction == View.FOCUS_UP) return null + if (direction == View.FOCUS_RIGHT) 1 else -1 + } else { + if (direction == View.FOCUS_RIGHT || direction == View.FOCUS_LEFT) return null + if (direction == View.FOCUS_DOWN) 1 else -1 + } + + return try { + getPosition(getCorrectParent(focused))?.let { position -> + val lookfor = dir + position + //clamp(dir + position, 0, recyclerView.adapter?.itemCount ?: return null) + getViewFromPos(lookfor) ?: run { + scrollToPosition(lookfor) + null + } + } + } catch (e: Exception) { + logError(e) + null + } + } + + /*override fun onRequestChildFocus( + parent: RecyclerView, + state: RecyclerView.State, + child: View, + focused: View? + ): Boolean { + return super.onRequestChildFocus(parent, state, child, focused) + getPosition(getCorrectParent(focused ?: return true))?.let { + val startView = findFirstVisibleChildClosestToStart(true,true) + val endView = findFirstVisibleChildClosestToEnd(true,true) + val start = getPosition(startView) + val end = getPosition(endView) + fill(parent,LayoutState()) + + val helper = mOrientationHelper ?: return false + val laidOutArea: Int = abs( + helper.getDecoratedEnd(startView) + - helper.getDecoratedStart(endView) + ) + val itemRange: Int = abs( + (start + - end) + ) + 1 + + val avgSizePerRow = laidOutArea.toFloat() / itemRange + + return Math.round( + itemsBefore * avgSizePerRow + ((orientation.getStartAfterPadding() + - orientation.getDecoratedStart(startChild))) + ) + recyclerView.scrollToPosition(it) + } + return true*/ + + //return super.onRequestChildFocus(parent, state, child, focused) + /* if (focused == null || focused == child) { + return super.onRequestChildFocus(parent, state, child, focused) + } + + try { + val pos = getPosition(getCorrectParent(focused) ?: return true) + scrollToPosition(pos) + } catch (e: Exception) { + logError(e) + } + return true +}*/ +} \ No newline at end of file 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 eb23d3ea..5cb8f410 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 @@ -471,31 +471,6 @@ open class ResultFragment : ResultTrailerPlayer() { syncModel.addFromUrl(url) val api = getApiFromName(apiName) - if (media_route_button != null) { - val chromecastSupport = api.hasChromecastSupport - media_route_button?.alpha = if (chromecastSupport) 1f else 0.3f - if (!chromecastSupport) { - media_route_button?.setOnClickListener { - showToast(activity, R.string.no_chromecast_support_toast, Toast.LENGTH_LONG) - } - } - activity?.let { act -> - if (act.isCastApiAvailable()) { - try { - CastButtonFactory.setUpMediaRouteButton(act, media_route_button) - val castContext = CastContext.getSharedInstance(act.applicationContext) - media_route_button?.isGone = - castContext.castState == CastState.NO_DEVICES_AVAILABLE - // this shit leaks for some reason - //castContext.addCastStateListener { state -> - // media_route_button?.isGone = state == CastState.NO_DEVICES_AVAILABLE - //} - } catch (e: Exception) { - logError(e) - } - } - } - } result_episodes?.adapter = EpisodeAdapter( diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt index 750556ad..7f4e0ee0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt @@ -5,23 +5,26 @@ import android.graphics.Rect import android.os.Bundle import android.view.View import android.view.ViewGroup +import android.widget.Toast import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.core.widget.NestedScrollView import com.discord.panels.OverlappingPanelsLayout import com.discord.panels.PanelsChildGestureRegionObserver +import com.google.android.gms.cast.framework.CastButtonFactory +import com.google.android.gms.cast.framework.CastContext +import com.google.android.gms.cast.framework.CastState import com.google.android.material.bottomsheet.BottomSheetDialog +import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.APIHolder.updateHasTrailers -import com.lagradost.cloudstream3.DubStatus -import com.lagradost.cloudstream3.LoadResponse -import com.lagradost.cloudstream3.R -import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.mvvm.Some +import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.player.CSPlayerEvent import com.lagradost.cloudstream3.ui.search.SearchAdapter import com.lagradost.cloudstream3.ui.search.SearchHelper +import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable import com.lagradost.cloudstream3.utils.AppUtils.openBrowser import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog @@ -125,6 +128,8 @@ class ResultFragmentPhone : ResultFragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + val apiName = arguments?.getString(API_NAME_BUNDLE) ?: return + super.onViewCreated(view, savedInstanceState) player_open_source?.setOnClickListener { @@ -192,6 +197,37 @@ class ResultFragmentPhone : ResultFragment() { } //result_poster_blur_holder?.translationY = -scrollY.toFloat() }) + val api = APIHolder.getApiFromName(apiName) + + if (media_route_button != null) { + val chromecastSupport = api.hasChromecastSupport + media_route_button?.alpha = if (chromecastSupport) 1f else 0.3f + if (!chromecastSupport) { + media_route_button?.setOnClickListener { + CommonActivity.showToast( + activity, + R.string.no_chromecast_support_toast, + Toast.LENGTH_LONG + ) + } + } + activity?.let { act -> + if (act.isCastApiAvailable()) { + try { + CastButtonFactory.setUpMediaRouteButton(act, media_route_button) + val castContext = CastContext.getSharedInstance(act.applicationContext) + media_route_button?.isGone = + castContext.castState == CastState.NO_DEVICES_AVAILABLE + // this shit leaks for some reason + //castContext.addCastStateListener { state -> + // media_route_button?.isGone = state == CastState.NO_DEVICES_AVAILABLE + //} + } catch (e: Exception) { + logError(e) + } + } + } + } observe(viewModel.episodesCountText) { count -> result_episodes_text.setText(count) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt index 895f0724..0e3ee53e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt @@ -3,6 +3,7 @@ package com.lagradost.cloudstream3.ui.result import android.app.Dialog import android.os.Bundle import android.view.View +import android.widget.LinearLayout import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView @@ -15,10 +16,17 @@ import com.lagradost.cloudstream3.mvvm.Some import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.ui.search.SearchAdapter import com.lagradost.cloudstream3.ui.search.SearchHelper +import com.lagradost.cloudstream3.utils.AppUtils.setMaxViewPoolSize import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage +import kotlinx.android.synthetic.main.fragment_home.* +import kotlinx.android.synthetic.main.fragment_result.* import kotlinx.android.synthetic.main.fragment_result_tv.* +import kotlinx.android.synthetic.main.fragment_result_tv.result_episodes +import kotlinx.android.synthetic.main.fragment_result_tv.result_episodes_text +import kotlinx.android.synthetic.main.fragment_result_tv.result_play_movie +import kotlinx.android.synthetic.main.fragment_result_tv.result_root class ResultFragmentTv : ResultFragment() { override val resultLayout = R.layout.fragment_result_tv @@ -95,13 +103,21 @@ class ResultFragmentTv : ResultFragment() { result_recommendations_filter_selection?.isVisible = false } } + var loadingDialog: Dialog? = null var popupDialog: Dialog? = null override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + + result_episodes?.layoutManager = + //LinearListLayout(result_episodes ?: return, result_episodes?.context).apply { + LinearListLayout(result_episodes?.context).apply { + setHorizontal() + } (result_episodes?.adapter as EpisodeAdapter?)?.apply { layout = R.layout.result_episode_both_tv } + //result_episodes?.setMaxViewPoolSize(0, Int.MAX_VALUE) result_season_selection.setAdapter() result_range_selection.setAdapter() diff --git a/app/src/main/res/layout/fragment_result.xml b/app/src/main/res/layout/fragment_result.xml index 35e78c7f..47f82f46 100644 --- a/app/src/main/res/layout/fragment_result.xml +++ b/app/src/main/res/layout/fragment_result.xml @@ -917,7 +917,7 @@ app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" android:descendantFocusability="afterDescendants" android:paddingBottom="100dp" - tools:listitem="@layout/result_episode" /> + tools:listitem="@layout/result_episode_both_tv" /> diff --git a/app/src/main/res/layout/result_episode.xml b/app/src/main/res/layout/result_episode.xml index 39521594..127cbd34 100644 --- a/app/src/main/res/layout/result_episode.xml +++ b/app/src/main/res/layout/result_episode.xml @@ -11,7 +11,6 @@ app:cardCornerRadius="@dimen/rounded_image_radius" app:cardBackgroundColor="@color/transparent" app:cardElevation="0dp" - android:foreground="@drawable/outline_drawable" android:layout_marginBottom="5dp">