diff --git a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt index 5c628582..0ed802ba 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt @@ -260,10 +260,10 @@ object CommonActivity { KeyEvent.KEYCODE_A, KeyEvent.KEYCODE_MEDIA_SKIP_BACKWARD, KeyEvent.KEYCODE_MEDIA_REWIND -> { PlayerEventType.SeekBack } - KeyEvent.KEYCODE_MEDIA_NEXT, KeyEvent.KEYCODE_BUTTON_R1 -> { + KeyEvent.KEYCODE_MEDIA_NEXT, KeyEvent.KEYCODE_BUTTON_R1, KeyEvent.KEYCODE_N -> { PlayerEventType.NextEpisode } - KeyEvent.KEYCODE_MEDIA_PREVIOUS, KeyEvent.KEYCODE_BUTTON_L1 -> { + KeyEvent.KEYCODE_MEDIA_PREVIOUS, KeyEvent.KEYCODE_BUTTON_L1, KeyEvent.KEYCODE_B -> { PlayerEventType.PrevEpisode } KeyEvent.KEYCODE_MEDIA_PAUSE -> { @@ -294,6 +294,9 @@ object CommonActivity { KeyEvent.KEYCODE_R, KeyEvent.KEYCODE_NUMPAD_0 -> { PlayerEventType.Resize } + KeyEvent.KEYCODE_C, KeyEvent.KEYCODE_NUMPAD_4 -> { + PlayerEventType.SkipOp + } KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, KeyEvent.KEYCODE_P, KeyEvent.KEYCODE_SPACE, KeyEvent.KEYCODE_NUMPAD_ENTER, KeyEvent.KEYCODE_ENTER -> { // space is not captured due to navigation PlayerEventType.PlayPauseToggle } 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 0b3764f0..4f113eba 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 @@ -98,7 +98,7 @@ class ParentItemAdapter( recyclerView?.apply { // this loops every viewHolder in the recycle view and checks the position to see if it is within the update range val missingUpdates = (position until (position + count)).toMutableSet() - for (i in 0 until mAdapter.itemCount) { + for (i in 0 until itemCount) { val viewHolder = getChildViewHolder(getChildAt(i)) val absolutePosition = viewHolder.absoluteAdapterPosition if (absolutePosition >= position && absolutePosition < position + count) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt index 6c6393cb..2ddef9a4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt @@ -1164,6 +1164,9 @@ open class FullScreenPlayer : AbstractPlayerFragment() { openOnlineSubPicker(view.context, null) {} } } + PlayerEventType.SkipOp -> { + skipOp() + } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt index 0d5c1e26..8561a0f4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt @@ -23,6 +23,7 @@ enum class PlayerEventType(val value: Int) { ShowMirrors(12), Resize(13), SearchSubtitlesOnline(14), + SkipOp(15), } enum class CSPlayerEvent(val value: Int) { 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 6d6586c0..d9fab132 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 @@ -57,11 +57,11 @@ const val ACTION_DOWNLOAD_EPISODE_SUBTITLE_MIRROR = 14 data class EpisodeClickEvent(val action: Int, val data: ResultEpisode) class EpisodeAdapter( - private var cardList: MutableList, private val hasDownloadSupport: Boolean, private val clickCallback: (EpisodeClickEvent) -> Unit, private val downloadClickCallback: (DownloadClickEvent) -> Unit, ) : RecyclerView.Adapter() { + private var cardList: MutableList = mutableListOf() private val mBoundViewHolders: HashSet = HashSet() private fun getAllBoundViewHolders(): Set? { @@ -104,6 +104,8 @@ class EpisodeAdapter( diffResult.dispatchUpdatesTo(this) } + var layout = R.layout.result_episode_both + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { /*val layout = if (cardList.filter { it.poster != null }.size >= cardList.size / 2) R.layout.result_episode_large @@ -111,7 +113,7 @@ class EpisodeAdapter( return EpisodeCardViewHolder( LayoutInflater.from(parent.context) - .inflate(R.layout.result_episode_both, parent, false), + .inflate(layout, parent, false), hasDownloadSupport, clickCallback, downloadClickCallback @@ -149,6 +151,8 @@ class EpisodeAdapter( fun bind(card: ResultEpisode) { localCard = card + val isTrueTv = itemView.context?.isTrueTvSettings() == true + val (parentView, otherView) = if (card.poster == null) { itemView.episode_holder to itemView.episode_holder_large } else { @@ -199,20 +203,22 @@ class EpisodeAdapter( } } - episodePoster?.setOnClickListener { - clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card)) - } + if (!isTrueTv) { + episodePoster?.setOnClickListener { + clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card)) + } - episodePoster?.setOnLongClickListener { - clickCallback.invoke(EpisodeClickEvent(ACTION_SHOW_TOAST, card)) - return@setOnLongClickListener true + episodePoster?.setOnLongClickListener { + clickCallback.invoke(EpisodeClickEvent(ACTION_SHOW_TOAST, card)) + return@setOnLongClickListener true + } } parentView.setOnClickListener { clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card)) } - if (parentView.context.isTrueTvSettings()) { + if (isTrueTv) { parentView.isFocusable = true parentView.isFocusableInTouchMode = true parentView.touchscreenBlocksFocus = false 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 111bc010..8b1c5c0a 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 @@ -328,6 +328,98 @@ open class ResultFragment : ResultTrailerPlayer() { viewModel.reloadEpisodes() } + open fun updateMovie(data : ResourceSome>) { + when (data) { + is ResourceSome.Success -> { + data.value.let { (text, ep) -> + result_play_movie.setText(text) + result_play_movie?.setOnClickListener { + viewModel.handleAction( + activity, + EpisodeClickEvent(ACTION_CLICK_DEFAULT, ep) + ) + } + result_play_movie?.setOnLongClickListener { + viewModel.handleAction( + activity, + EpisodeClickEvent(ACTION_SHOW_OPTIONS, ep) + ) + return@setOnLongClickListener true + } + + main { + val file = + ioWork { + context?.let { + VideoDownloadManager.getDownloadFileInfoAndUpdateSettings( + it, + ep.id + ) + } + } + + downloadButton?.dispose() + downloadButton = EasyDownloadButton() + downloadButton?.setUpMoreButton( + file?.fileLength, + file?.totalBytes, + result_movie_progress_downloaded, + result_movie_download_icon, + result_movie_download_text, + result_movie_download_text_precentage, + result_download_movie, + true, + VideoDownloadHelper.DownloadEpisodeCached( + ep.name, + ep.poster, + 0, + null, + ep.id, + ep.id, + null, + null, + System.currentTimeMillis(), + ) + ) { click -> + when(click.action) { + DOWNLOAD_ACTION_DOWNLOAD -> { + viewModel.handleAction( + activity, + EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, ep) + ) + } + else -> handleDownloadClick(activity, click) + } + } + result_movie_progress_downloaded_holder?.isVisible = true + } + } + } + else -> { + result_movie_progress_downloaded_holder?.isVisible = false + result_play_movie?.isVisible = false + } + } + } + + open fun updateEpisodes(episodes : ResourceSome>) { + when (episodes) { + is ResourceSome.None -> { + result_episode_loading?.isVisible = false + result_episodes?.isVisible = false + } + is ResourceSome.Loading -> { + result_episode_loading?.isVisible = true + result_episodes?.isVisible = false + } + is ResourceSome.Success -> { + result_episodes?.isVisible = true + result_episode_loading?.isVisible = false + (result_episodes?.adapter as? EpisodeAdapter?)?.updateList(episodes.value) + } + } + } + @SuppressLint("SetTextI18n") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -401,24 +493,8 @@ open class ResultFragment : ResultTrailerPlayer() { } } - result_scroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY -> - val dy = scrollY - oldScrollY - if (dy > 0) { //check for scroll down - result_bookmark_fab?.shrink() - } else if (dy < -5) { - result_bookmark_fab?.extend() - } - if (!isFullScreenPlayer && player.getIsPlaying()) { - if (scrollY > (player_background?.height ?: scrollY)) { - player.handleEvent(CSPlayerEvent.Pause) - } - } - //result_poster_blur_holder?.translationY = -scrollY.toFloat() - }) - result_episodes.adapter = EpisodeAdapter( - ArrayList(), api.hasDownloadSupport, { episodeClick -> viewModel.handleAction(activity, episodeClick) @@ -456,9 +532,10 @@ open class ResultFragment : ResultTrailerPlayer() { } // This is to band-aid FireTV navigation - result_season_button?.isFocusableInTouchMode = context?.isTvSettings() == true - result_episode_select?.isFocusableInTouchMode = context?.isTvSettings() == true - result_dub_select?.isFocusableInTouchMode = context?.isTvSettings() == true + val isTv = context?.isTvSettings() == true + result_season_button?.isFocusableInTouchMode = isTv + result_episode_select?.isFocusableInTouchMode = isTv + result_dub_select?.isFocusableInTouchMode = isTv context?.let { ctx -> val arrayAdapter = ArrayAdapter(ctx, R.layout.sort_bottom_single_choice) @@ -653,21 +730,7 @@ open class ResultFragment : ResultTrailerPlayer() { } observe(viewModel.episodes) { episodes -> - when (episodes) { - is ResourceSome.None -> { - result_episode_loading?.isVisible = false - result_episodes?.isVisible = false - } - is ResourceSome.Loading -> { - result_episode_loading?.isVisible = true - result_episodes?.isVisible = false - } - is ResourceSome.Success -> { - result_episodes?.isVisible = true - result_episode_loading?.isVisible = false - (result_episodes?.adapter as? EpisodeAdapter?)?.updateList(episodes.value) - } - } + updateEpisodes(episodes) } result_cast_items?.setOnFocusChangeListener { _, hasFocus -> @@ -692,77 +755,7 @@ open class ResultFragment : ResultTrailerPlayer() { } observe(viewModel.movie) { data -> - when (data) { - is ResourceSome.Success -> { - data.value.let { (text, ep) -> - result_play_movie.setText(text) - result_play_movie?.setOnClickListener { - viewModel.handleAction( - activity, - EpisodeClickEvent(ACTION_CLICK_DEFAULT, ep) - ) - } - result_play_movie?.setOnLongClickListener { - viewModel.handleAction( - activity, - EpisodeClickEvent(ACTION_SHOW_OPTIONS, ep) - ) - return@setOnLongClickListener true - } - - main { - val file = - ioWork { - context?.let { - VideoDownloadManager.getDownloadFileInfoAndUpdateSettings( - it, - ep.id - ) - } - } - - downloadButton?.dispose() - downloadButton = EasyDownloadButton() - downloadButton?.setUpMoreButton( - file?.fileLength, - file?.totalBytes, - result_movie_progress_downloaded, - result_movie_download_icon, - result_movie_download_text, - result_movie_download_text_precentage, - result_download_movie, - true, - VideoDownloadHelper.DownloadEpisodeCached( - ep.name, - ep.poster, - 0, - null, - ep.id, - ep.id, - null, - null, - System.currentTimeMillis(), - ) - ) { click -> - when(click.action) { - DOWNLOAD_ACTION_DOWNLOAD -> { - viewModel.handleAction( - activity, - EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, ep) - ) - } - else -> handleDownloadClick(activity, click) - } - } - result_movie_progress_downloaded_holder?.isVisible = true - } - } - } - else -> { - result_movie_progress_downloaded_holder?.isVisible = false - result_play_movie?.isVisible = false - } - } + updateMovie(data) } observe(viewModel.page) { data -> 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 9b179152..2f6f5dbf 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 @@ -7,6 +7,7 @@ import android.view.View import android.view.ViewGroup 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.material.bottomsheet.BottomSheetDialog @@ -18,6 +19,7 @@ import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.mvvm.Some 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.openBrowser @@ -30,6 +32,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIcons import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes import kotlinx.android.synthetic.main.fragment_result.* import kotlinx.android.synthetic.main.fragment_result_swipe.* +import kotlinx.android.synthetic.main.fragment_trailer.* import kotlinx.android.synthetic.main.result_recommendations.* import kotlinx.android.synthetic.main.trailer_custom_layout.* @@ -129,10 +132,10 @@ class ResultFragmentPhone : ResultFragment() { context?.openBrowser(it.url) } } - result_recommendations?.spanCount = 3 result_overlapping_panels?.setStartPanelLockState(OverlappingPanelsLayout.LockState.CLOSE) result_overlapping_panels?.setEndPanelLockState(OverlappingPanelsLayout.LockState.CLOSE) + result_recommendations?.spanCount = 3 result_recommendations?.adapter = SearchAdapter( ArrayList(), @@ -175,6 +178,21 @@ class ResultFragmentPhone : ResultFragment() { }) + result_scroll?.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY -> + val dy = scrollY - oldScrollY + if (dy > 0) { //check for scroll down + result_bookmark_fab?.shrink() + } else if (dy < -5) { + result_bookmark_fab?.extend() + } + if (!isFullScreenPlayer && player.getIsPlaying()) { + if (scrollY > (player_background?.height ?: scrollY)) { + player.handleEvent(CSPlayerEvent.Pause) + } + } + //result_poster_blur_holder?.translationY = -scrollY.toFloat() + }) + observe(viewModel.selectPopup) { popup -> when (popup) { is Some.Success -> { 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 78664761..eae5250b 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 @@ -1,11 +1,129 @@ package com.lagradost.cloudstream3.ui.result +import android.os.Bundle +import android.view.View +import androidx.core.view.children +import androidx.core.view.isGone +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import com.discord.panels.OverlappingPanelsLayout +import com.lagradost.cloudstream3.DubStatus import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.SearchResponse +import com.lagradost.cloudstream3.mvvm.ResourceSome +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.SingleSelectionHelper.showBottomDialog +import kotlinx.android.synthetic.main.fragment_result_swipe.* +import kotlinx.android.synthetic.main.fragment_result_tv.* +import kotlinx.android.synthetic.main.result_recommendations.* class ResultFragmentTv : ResultFragment() { override val resultLayout = R.layout.fragment_result_tv - override fun setRecommendations(rec: List?, validApiName: String?) { + private fun handleSelection(data: Any) { + when (data) { + is EpisodeRange -> { + viewModel.changeRange(data) + } + is Int -> { + viewModel.changeSeason(data) + } + is DubStatus -> { + viewModel.changeDubStatus(data) + } + } + } + + private fun RecyclerView?.select(index: Int) { + (this?.adapter as? SelectAdaptor?)?.select(index, this) + } + + private fun RecyclerView?.update(data: List) { + (this?.adapter as? SelectAdaptor?)?.updateSelectionList(data) + this?.isVisible = data.size > 1 + } + + private fun RecyclerView?.setAdapter() { + this?.adapter = SelectAdaptor { data -> + handleSelection(data) + } + } + + private fun hasNoFocus(): Boolean { + val focus = activity?.currentFocus + if (focus == null || !focus.isVisible) return true + return focus == this.result_root + } + + override fun updateEpisodes(episodes: ResourceSome>) { + super.updateEpisodes(episodes) + if (episodes is ResourceSome.Success && hasNoFocus()) { + result_episodes?.requestFocus() + } + } + + override fun updateMovie(data: ResourceSome>) { + super.updateMovie(data) + if (data is ResourceSome.Success && hasNoFocus()) { + result_play_movie?.requestFocus() + } + } + + override fun setRecommendations(rec: List?, validApiName: String?) { + val isInvalid = rec.isNullOrEmpty() + result_recommendations?.isGone = isInvalid + result_recommendations_btt?.isGone = isInvalid + val matchAgainst = validApiName ?: rec?.firstOrNull()?.apiName + (result_recommendations?.adapter as SearchAdapter?)?.updateList(rec?.filter { it.apiName == matchAgainst } ?: emptyList()) + + rec?.map { it.apiName }?.distinct()?.let { apiNames -> + // very dirty selection + result_recommendations_filter_button?.isVisible = apiNames.size > 1 + result_recommendations_filter_button?.text = matchAgainst + + } ?: run { + result_recommendations_filter_button?.isVisible = false + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + (result_episodes?.adapter as EpisodeAdapter?)?.apply { + layout = R.layout.result_episode_both_tv + } + + result_season_selection.setAdapter() + result_range_selection.setAdapter() + result_dub_selection.setAdapter() + + observe(viewModel.selectedRangeIndex) { selected -> + result_range_selection.select(selected) + } + observe(viewModel.selectedSeasonIndex) { selected -> + result_season_selection.select(selected) + } + observe(viewModel.selectedDubStatusIndex) { selected -> + result_dub_selection.select(selected) + } + observe(viewModel.rangeSelections) { + result_range_selection.update(it) + } + observe(viewModel.dubSubSelections) { + result_dub_selection.update(it) + } + observe(viewModel.seasonSelections) { + result_season_selection.update(it) + } + + result_recommendations?.spanCount = 8 + result_recommendations?.adapter = + SearchAdapter( + ArrayList(), + result_recommendations, + ) { callback -> + SearchHelper.handleSearchClickCallback(activity, callback) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt index a06655af..3d9e83c3 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt @@ -57,6 +57,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.navigate import com.lagradost.cloudstream3.utils.UIHelper.requestRW import kotlinx.coroutines.* import java.io.File +import java.lang.Math.abs import java.util.concurrent.TimeUnit @@ -311,8 +312,8 @@ class ResultViewModel2 : ViewModel() { /** map>> */ private var currentEpisodes: Map> = mapOf() private var currentRanges: Map> = mapOf() - private var currentSeasons: Set = setOf() - private var currentDubStatus: Set = setOf() + private var currentSeasons: List = listOf() + private var currentDubStatus: List = listOf() private var currentMeta: SyncAPI.SyncResult? = null private var currentSync: Map? = null private var currentIndex: EpisodeIndexer? = null @@ -376,6 +377,18 @@ class ResultViewModel2 : ViewModel() { private val _selectedDubStatus: MutableLiveData> = MutableLiveData(Some.None) val selectedDubStatus: LiveData> = _selectedDubStatus + private val _selectedRangeIndex: MutableLiveData = + MutableLiveData(-1) + val selectedRangeIndex: LiveData = _selectedRangeIndex + + private val _selectedSeasonIndex: MutableLiveData = + MutableLiveData(-1) + val selectedSeasonIndex: LiveData = _selectedSeasonIndex + + private val _selectedDubStatusIndex: MutableLiveData = MutableLiveData(-1) + val selectedDubStatusIndex: LiveData = _selectedDubStatusIndex + + private val _loadedLinks: MutableLiveData> = MutableLiveData(Some.None) val loadedLinks: LiveData> = _loadedLinks @@ -1414,6 +1427,16 @@ class ResultViewModel2 : ViewModel() { val episodes = currentEpisodes[indexer] val ranges = currentRanges[indexer] + + if (ranges?.contains(range) != true) { + // if the current ranges does not include the range then select the range with the closest matching start episode + // this usually happends when dub has less episodes then sub -> the range does not exist + ranges?.minByOrNull { abs(it.startEpisode - range.startEpisode) }?.let { r -> + postEpisodeRange(indexer, r) + return + } + } + val size = episodes?.size val isMovie = currentResponse?.isMovie() == true currentIndex = indexer @@ -1435,6 +1458,10 @@ class ResultViewModel2 : ViewModel() { ) ) + _selectedSeasonIndex.postValue( + currentSeasons.indexOf(indexer.season) + ) + _selectedSeason.postValue( some( if (isMovie || currentSeasons.size <= 1) null else @@ -1449,6 +1476,10 @@ class ResultViewModel2 : ViewModel() { ) ) + _selectedRangeIndex.postValue( + ranges?.indexOf(range) ?: -1 + ) + _selectedRange.postValue( some( if (isMovie) null else if ((currentRanges[indexer]?.size ?: 0) > 1) { @@ -1458,6 +1489,11 @@ class ResultViewModel2 : ViewModel() { } ) ) + + _selectedDubStatusIndex.postValue( + currentDubStatus.indexOf(indexer.dubStatus) + ) + _selectedDubStatus.postValue( some( if (isMovie || currentDubStatus.size <= 1) null else @@ -1487,6 +1523,12 @@ class ResultViewModel2 : ViewModel() { postMovie() } else { val ret = getEpisodes(indexer, range) + /*if (ret.isEmpty()) { + val index = ranges?.indexOf(range) + if(index != null && index > 0) { + + } + }*/ _episodes.postValue(ResourceSome.Success(ret)) } } @@ -1675,8 +1717,8 @@ class ResultViewModel2 : ViewModel() { seasonsSelection += key.season dubSelection += key.dubStatus } - currentDubStatus = dubSelection - currentSeasons = seasonsSelection + currentDubStatus = dubSelection.toList() + currentSeasons = seasonsSelection.toList() _dubSubSelections.postValue(dubSelection.map { txt(it) to it }) if (loadResponse is EpisodeResponse) { _seasonSelections.postValue(seasonsSelection.map { seasonNumber -> diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/SelectAdaptor.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/SelectAdaptor.kt new file mode 100644 index 00000000..f230d4b1 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/SelectAdaptor.kt @@ -0,0 +1,121 @@ +package com.lagradost.cloudstream3.ui.result + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.core.view.isVisible +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.button.MaterialButton +import com.lagradost.cloudstream3.ActorData +import com.lagradost.cloudstream3.ActorRole +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.ui.home.ParentItemAdapter +import com.lagradost.cloudstream3.ui.settings.AccountAdapter +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings +import com.lagradost.cloudstream3.utils.UIHelper.setImage +import kotlinx.android.synthetic.main.cast_item.view.* +import org.schabi.newpipe.extractor.timeago.patterns.it + +typealias SelectData = Pair + +class SelectAdaptor(val callback: (Any) -> Unit) : RecyclerView.Adapter() { + private val selection: MutableList = mutableListOf() + private var selectedIndex: Int = -1 + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return SelectViewHolder( + LayoutInflater.from(parent.context).inflate(R.layout.result_selection, parent, false), + ) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is SelectViewHolder -> { + holder.bind(selection[position], position == selectedIndex, callback) + } + } + } + + override fun getItemCount(): Int { + return selection.size + } + + fun select(newIndex: Int, recyclerView: RecyclerView?) { + if(recyclerView == null) return + if(newIndex == selectedIndex) return + val oldIndex = selectedIndex + selectedIndex = newIndex + recyclerView.apply { + for (i in 0 until itemCount) { + val viewHolder = getChildViewHolder( getChildAt(i) ?: continue) ?: continue + val pos = viewHolder.absoluteAdapterPosition + if (viewHolder is SelectViewHolder) { + if (pos == oldIndex) { + viewHolder.update(false) + } else if (pos == newIndex) { + viewHolder.update(true) + } + } + } + } + } + + fun updateSelectionList(newList: List) { + val diffResult = DiffUtil.calculateDiff( + SelectDataCallback(this.selection, newList) + ) + + selection.clear() + selection.addAll(newList) + + diffResult.dispatchUpdatesTo(this) + } + + + private class SelectViewHolder + constructor( + itemView: View, + ) : + RecyclerView.ViewHolder(itemView) { + private val item: MaterialButton = itemView as MaterialButton + + fun update(isSelected: Boolean) { + item.isSelected = isSelected + } + + fun bind( + data: SelectData, isSelected: Boolean, callback: (Any) -> Unit + ) { + val isTrueTv = itemView.context?.isTrueTvSettings() == true + if (isTrueTv) { + item.isFocusable = true + item.isFocusableInTouchMode = true + } + + item.isSelected = isSelected + item.setText(data.first) + item.setOnClickListener { + callback.invoke(data.second) + } + } + } +} + +class SelectDataCallback( + private val oldList: List, + private val newList: List +) : + DiffUtil.Callback() { + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) = + oldList[oldItemPosition].second == newList[newItemPosition].second + + override fun getOldListSize() = oldList.size + + override fun getNewListSize() = newList.size + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = + oldList[oldItemPosition] == newList[newItemPosition] +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt index 2e773310..125778fc 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt @@ -44,6 +44,7 @@ import com.lagradost.cloudstream3.isMovieType import com.lagradost.cloudstream3.mapper import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.ui.result.ResultFragment +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.utils.AppUtils.loadResult import com.lagradost.cloudstream3.utils.Coroutines.ioSafe @@ -316,12 +317,11 @@ object AppUtils { //private val viewModel: ResultViewModel by activityViewModels() private fun getResultsId(context: Context) : Int { - return R.id.global_to_navigation_results_phone - //return if(context.isTvSettings()) { - // R.id.global_to_navigation_results_tv - //} else { - // R.id.global_to_navigation_results_phone - //} + return if(context.isTrueTvSettings()) { + R.id.global_to_navigation_results_tv + } else { + R.id.global_to_navigation_results_phone + } } fun AppCompatActivity.loadResult( diff --git a/app/src/main/res/color/selectable_black.xml b/app/src/main/res/color/selectable_black.xml index 0d483687..6761eb74 100644 --- a/app/src/main/res/color/selectable_black.xml +++ b/app/src/main/res/color/selectable_black.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/color/selectable_white.xml b/app/src/main/res/color/selectable_white.xml index 25656c29..bc012def 100644 --- a/app/src/main/res/color/selectable_white.xml +++ b/app/src/main/res/color/selectable_white.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/outline_drawable_less.xml b/app/src/main/res/drawable/outline_drawable_less.xml new file mode 100644 index 00000000..0b641074 --- /dev/null +++ b/app/src/main/res/drawable/outline_drawable_less.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/outline_less.xml b/app/src/main/res/drawable/outline_less.xml new file mode 100644 index 00000000..b8dba5b6 --- /dev/null +++ b/app/src/main/res/drawable/outline_less.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_result_tv.xml b/app/src/main/res/layout/fragment_result_tv.xml index 6c28658d..6e740271 100644 --- a/app/src/main/res/layout/fragment_result_tv.xml +++ b/app/src/main/res/layout/fragment_result_tv.xml @@ -6,9 +6,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" style="@style/DarkFragment" - android:background="?attr/primaryBlackBackground" - android:clickable="true" - android:focusable="true"> + android:background="?attr/primaryBlackBackground"> + - - @@ -400,6 +399,10 @@ android:layout_height="wrap_content"> @@ -574,42 +578,46 @@ - + android:layout_height="wrap_content" /> - - - - + android:paddingBottom="10dp" + tools:listitem="@layout/result_selection" + android:orientation="horizontal" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + + app:shimmer_highlight_alpha="0.3"> --> + + - - \ No newline at end of file diff --git a/app/src/main/res/layout/result_episode.xml b/app/src/main/res/layout/result_episode.xml index f2399ecb..39521594 100644 --- a/app/src/main/res/layout/result_episode.xml +++ b/app/src/main/res/layout/result_episode.xml @@ -2,8 +2,6 @@ + + - + \ No newline at end of file diff --git a/app/src/main/res/layout/result_episode_both_tv.xml b/app/src/main/res/layout/result_episode_both_tv.xml new file mode 100644 index 00000000..239bec11 --- /dev/null +++ b/app/src/main/res/layout/result_episode_both_tv.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/result_episode_large.xml b/app/src/main/res/layout/result_episode_large.xml index 8fc917ec..07e10b78 100644 --- a/app/src/main/res/layout/result_episode_large.xml +++ b/app/src/main/res/layout/result_episode_large.xml @@ -3,11 +3,10 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" - android:nextFocusLeft="@id/episode_poster" android:nextFocusRight="@id/result_episode_download" android:id="@+id/episode_holder_large" - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" app:cardCornerRadius="@dimen/rounded_image_radius" app:cardBackgroundColor="?attr/boxItemBackground" @@ -19,7 +18,7 @@ android:foreground="?android:attr/selectableItemBackgroundBorderless" android:padding="10dp" android:orientation="vertical" - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/result_episode_tv.xml b/app/src/main/res/layout/result_episode_tv.xml new file mode 100644 index 00000000..e74254bc --- /dev/null +++ b/app/src/main/res/layout/result_episode_tv.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/result_selection.xml b/app/src/main/res/layout/result_selection.xml index e379201b..fe880dc9 100644 --- a/app/src/main/res/layout/result_selection.xml +++ b/app/src/main/res/layout/result_selection.xml @@ -1,8 +1,7 @@ - \ No newline at end of file + tools:text="Season 1" /> \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 5ac7cbf9..fb9180f3 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -3,6 +3,8 @@ 16dp 16dp 10dp + 4dp + 0dp 2dp 15dp diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 3be2fbed..9c6a5902 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -432,12 +432,12 @@ false textStart 20dp - 4dp + @dimen/rounded_button_radius 15sp 0dp 0dp - @drawable/outline_drawable + @drawable/outline_drawable_less