diff --git a/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelContentsFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelContentsFragment.kt index 0939fbde..b99e211e 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelContentsFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelContentsFragment.kt @@ -25,6 +25,7 @@ import com.futo.platformplayer.api.media.structures.IReplacerPager import com.futo.platformplayer.api.media.structures.MultiPager import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.constructs.Event2 +import com.futo.platformplayer.constructs.Event3 import com.futo.platformplayer.constructs.TaskHandler import com.futo.platformplayer.dp import com.futo.platformplayer.engine.exceptions.PluginException @@ -61,7 +62,7 @@ class ChannelContentsFragment(private val subType: String? = null) : Fragment(), private var _query: String? = null private var _searchView: SearchView? = null - val onContentClicked = Event2(); + val onContentClicked = Event3, ArrayList>?>(); val onContentUrlClicked = Event2(); val onUrlClicked = Event1(); val onChannelClicked = Event1(); @@ -211,7 +212,10 @@ class ChannelContentsFragment(private val subType: String? = null) : Fragment(), _adapterResults = PreviewContentListAdapter(view.context, FeedStyle.THUMBNAIL, _results, null, Settings.instance.channel.progressBar, viewsToPrepend = arrayListOf(searchView)).apply { this.onContentUrlClicked.subscribe(this@ChannelContentsFragment.onContentUrlClicked::emit); this.onUrlClicked.subscribe(this@ChannelContentsFragment.onUrlClicked::emit); - this.onContentClicked.subscribe(this@ChannelContentsFragment.onContentClicked::emit); + this.onContentClicked.subscribe { content, num -> + val results = ArrayList(_results) + this@ChannelContentsFragment.onContentClicked.emit(content, num, Pair(_pager!!, results)) + } this.onChannelClicked.subscribe(this@ChannelContentsFragment.onChannelClicked::emit); this.onAddToClicked.subscribe(this@ChannelContentsFragment.onAddToClicked::emit); this.onAddToQueueClicked.subscribe(this@ChannelContentsFragment.onAddToQueueClicked::emit); diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/bottombar/MenuBottomBarFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/bottombar/MenuBottomBarFragment.kt index c52bcebb..d43a67f6 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/bottombar/MenuBottomBarFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/bottombar/MenuBottomBarFragment.kt @@ -392,7 +392,7 @@ class MenuBottomBarFragment : MainActivityFragment() { ButtonDefinition(2, R.drawable.ic_creators, R.drawable.ic_creators_filled, R.string.creators, canToggle = false, { it.currentMain is CreatorsFragment }, { it.navigate(withHistory = false) }), ButtonDefinition(3, R.drawable.ic_sources, R.drawable.ic_sources_filled, R.string.sources, canToggle = false, { it.currentMain is SourcesFragment }, { it.navigate(withHistory = false) }), ButtonDefinition(4, R.drawable.ic_playlist, R.drawable.ic_playlist_filled, R.string.playlists, canToggle = false, { it.currentMain is PlaylistsFragment }, { it.navigate(withHistory = false) }), - ButtonDefinition(5, R.drawable.ic_smart_display, R.drawable.ic_smart_display_filled, R.string.shorts, canToggle = true, { it.currentMain is ShortsFragment }, { it.navigate(withHistory = false) }), + ButtonDefinition(5, R.drawable.ic_smart_display, R.drawable.ic_smart_display_filled, R.string.shorts, canToggle = true, { it.currentMain is ShortsFragment && !(it.currentMain as ShortsFragment).isChannelShortsMode }, { it.navigate(withHistory = false) }), ButtonDefinition(6, R.drawable.ic_history, R.drawable.ic_history, R.string.history, canToggle = false, { it.currentMain is HistoryFragment }, { it.navigate(withHistory = false) }), ButtonDefinition(7, R.drawable.ic_download, R.drawable.ic_download, R.string.downloads, canToggle = false, { it.currentMain is DownloadsFragment }, { it.navigate(withHistory = false) }), ButtonDefinition(8, R.drawable.ic_chat, R.drawable.ic_chat_filled, R.string.comments, canToggle = true, { it.currentMain is CommentsFragment }, { it.navigate(withHistory = false) }), diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ChannelFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ChannelFragment.kt index 63b60c1f..72dd3a58 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ChannelFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ChannelFragment.kt @@ -211,6 +211,14 @@ class ChannelFragment : MainFragment() { } } } + adapter.onShortClicked.subscribe { v, _, pagerPair -> + when (v) { + is IPlatformVideo -> { + StatePlayer.instance.clearQueue() + fragment.navigate(Triple(v, pagerPair!!.first, pagerPair.second)) + } + } + } adapter.onAddToClicked.subscribe { content -> _overlayContainer.let { if (content is IPlatformVideo) _slideUpOverlay = diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ShortView.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ShortView.kt index 4491f305..9ec164b7 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ShortView.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ShortView.kt @@ -160,10 +160,10 @@ class ShortView : FrameLayout { null val onResetTriggered = Event0() - val onPlayingToggled = Event1() - val onLikesLoaded = Event3() - val onLikeDislikeUpdated = Event1() - val onVideoUpdated = Event1() + private val onPlayingToggled = Event1() + private val onLikesLoaded = Event3() + private val onLikeDislikeUpdated = Event1() + private val onVideoUpdated = Event1() private val bottomSheet: CommentsModalBottomSheet = CommentsModalBottomSheet() @@ -447,8 +447,7 @@ class ShortView : FrameLayout { overlay.visibility = VISIBLE overlay.animate().alpha(1f).scaleX(1f).scaleY(1f).setDuration(400) - .setInterpolator(OvershootInterpolator(1.2f)) - .start() + .setInterpolator(OvershootInterpolator(1.2f)).start() overlay.postDelayed({ hidePlayPauseIcon() @@ -783,11 +782,11 @@ class ShortView : FrameLayout { mainFragment.lifecycleScope.launch(Dispatchers.IO) { try { - Logger.i(CommentsModalBottomSheet.Companion.TAG, "Started backfill") + Logger.i(CommentsModalBottomSheet.TAG, "Started backfill") args.processHandle.fullyBackfillServersAnnounceExceptions() - Logger.i(CommentsModalBottomSheet.Companion.TAG, "Finished backfill") + Logger.i(CommentsModalBottomSheet.TAG, "Finished backfill") } catch (e: Throwable) { - Logger.e(CommentsModalBottomSheet.Companion.TAG, "Failed to backfill servers", e) + Logger.e(CommentsModalBottomSheet.TAG, "Failed to backfill servers", e) } } @@ -832,13 +831,13 @@ class ShortView : FrameLayout { Logger.w(TAG, "exception", e) UIDialogs.showDialog( context, R.drawable.ic_security, "Authentication", e.message, null, 0, UIDialogs.Action("Cancel", {}), UIDialogs.Action("Login", { - val id = e.config.let { if (it is SourcePluginConfig) it.id else null } - val didLogin = - if (id == null) false else StatePlugins.instance.loginPlugin(context, id) { - loadVideo(url) - } - if (!didLogin) UIDialogs.showDialogOk(context, R.drawable.ic_error_pred, "Failed to login") - }, UIDialogs.ActionStyle.PRIMARY) + val id = e.config.let { if (it is SourcePluginConfig) it.id else null } + val didLogin = + if (id == null) false else StatePlugins.instance.loginPlugin(context, id) { + loadVideo(url) + } + if (!didLogin) UIDialogs.showDialogOk(context, R.drawable.ic_error_pred, "Failed to login") + }, UIDialogs.ActionStyle.PRIMARY) ) }.exception { Logger.w(TAG, "exception", it) @@ -924,7 +923,7 @@ class ShortView : FrameLayout { const val TAG = "VideoDetailView" } - class CommentsModalBottomSheet() : BottomSheetDialogFragment() { + class CommentsModalBottomSheet : BottomSheetDialogFragment() { var mainFragment: MainFragment? = null private lateinit var containerContent: FrameLayout @@ -968,7 +967,7 @@ class ShortView : FrameLayout { private lateinit var behavior: BottomSheetBehavior private val _taskLoadPolycentricProfile = - TaskHandler(StateApp.instance.scopeGetter, { ApiMethods.getPolycentricProfileByClaim(ApiMethods.SERVER, ApiMethods.FUTO_TRUST_ROOT, it.claimFieldType.toLong(), it.claimType.toLong(), it.value!!) }).success { it -> setPolycentricProfile(it, animate = true) } + TaskHandler(StateApp.instance.scopeGetter, { ApiMethods.getPolycentricProfileByClaim(ApiMethods.SERVER, ApiMethods.FUTO_TRUST_ROOT, it.claimFieldType.toLong(), it.claimType.toLong(), it.value!!) }).success { setPolycentricProfile(it, animate = true) } .exception { Logger.w(TAG, "Failed to load claims.", it) } @@ -1024,7 +1023,7 @@ class ShortView : FrameLayout { val id = c.author.id.value Logger.i(TAG, "onAuthorClick: $id") - if (id != null && id.startsWith("polycentric://") == true) { + if (id != null && id.startsWith("polycentric://")) { val navUrl = "https://harbor.social/" + id.substring("polycentric://".length) mainFragment!!.startActivity(Intent(Intent.ACTION_VIEW, navUrl.toUri())) } @@ -1229,15 +1228,21 @@ class ShortView : FrameLayout { buttonPlatform.setTextColor(resources.getColor(if (index == 1) R.color.white else R.color.gray_ac, null)) buttonPolycentric.setTextColor(resources.getColor(if (index == 0) R.color.white else R.color.gray_ac, null)) - if (index == null) { - addCommentView.visibility = GONE - commentsList.clear() - } else if (index == 0) { - addCommentView.visibility = VISIBLE - fetchPolycentricComments() - } else if (index == 1) { - addCommentView.visibility = GONE - fetchComments() + when (index) { + null -> { + addCommentView.visibility = GONE + commentsList.clear() + } + + 0 -> { + addCommentView.visibility = VISIBLE + fetchPolycentricComments() + } + + 1 -> { + addCommentView.visibility = GONE + fetchComments() + } } } @@ -1252,7 +1257,7 @@ class ShortView : FrameLayout { Logger.i(TAG, "fetchPolycentricComments") val video = video val idValue = video.id.value - if (video.url.isEmpty() != false) { + if (video.url.isEmpty()) { Logger.w(TAG, "Failed to fetch polycentric comments because url was null") commentsList.clear() return diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ShortsFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ShortsFragment.kt index a6cdc882..ba0a3d0c 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ShortsFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ShortsFragment.kt @@ -31,8 +31,19 @@ class ShortsFragment : MainFragment() { private var loadPagerTask: TaskHandler>? = null private var nextPageTask: TaskHandler>? = null - private var shortsPager: IPager? = null - private val videos: MutableList = mutableListOf() + private var mainShortsPager: IPager? = null + private val mainShorts: MutableList = mutableListOf() + + // the pager to call next on + private var currentShortsPager: IPager? = null + + // the shorts array bound to the ViewPager2 adapter + private val currentShorts: MutableList = mutableListOf() + + private var channelShortsPager: IPager? = null + private val channelShorts: MutableList = mutableListOf() + val isChannelShortsMode: Boolean + get() = channelShortsPager != null private var viewPager: ViewPager2? = null private lateinit var overlayLoading: FrameLayout @@ -44,6 +55,47 @@ class ShortsFragment : MainFragment() { loadPager() } + // we just completely reset the data structure so we want to tell the adapter that + @SuppressLint("NotifyDataSetChanged") + override fun onShown(parameter: Any?, isBack: Boolean) { + super.onShown(parameter, isBack) + + if (parameter is Triple<*, *, *>) { + setLoading(false) + channelShorts.clear() + @Suppress("UNCHECKED_CAST") // TODO replace with a strongly typed parameter + channelShorts.addAll(parameter.third as ArrayList) + @Suppress("UNCHECKED_CAST") // TODO replace with a strongly typed parameter + channelShortsPager = parameter.second as IPager + + currentShorts.clear() + currentShorts.addAll(channelShorts) + currentShortsPager = channelShortsPager + + viewPager?.adapter?.notifyDataSetChanged() + + viewPager?.post { + viewPager?.currentItem = channelShorts.indexOfFirst { + return@indexOfFirst (parameter.first as IPlatformVideo).id == it.id + } + } + } else if (isChannelShortsMode) { + channelShortsPager = null + channelShorts.clear() + currentShorts.clear() + + if (loadPagerTask == null) { + currentShorts.addAll(mainShorts) + currentShortsPager = mainShortsPager + } else { + setLoading(true) + } + + viewPager?.adapter?.notifyDataSetChanged() + viewPager?.currentItem = 0 + } + } + override fun onCreateMainView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { @@ -62,8 +114,8 @@ class ShortsFragment : MainFragment() { Logger.i(TAG, "Creating adapter") val customViewAdapter = - CustomViewAdapter(videos, layoutInflater, this@ShortsFragment, overlayQualityContainer) { - if (!shortsPager!!.hasMorePages()) { + CustomViewAdapter(currentShorts, layoutInflater, this@ShortsFragment, overlayQualityContainer) { + if (!currentShortsPager!!.hasMorePages()) { return@CustomViewAdapter } nextPage() @@ -81,19 +133,16 @@ class ShortsFragment : MainFragment() { this.customViewAdapter = customViewAdapter - if (loadPagerTask == null && videos.isEmpty()) { + if (loadPagerTask == null && currentShorts.isEmpty()) { loadPager() loadPagerTask!!.success { - applyData() + setLoading(false) } } else { - applyData() + setLoading(false) } - } - private fun applyData() { - val viewPager = viewPager!! viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { fun play(adapter: CustomViewAdapter, position: Int) { val recycler = (viewPager.getChildAt(0) as RecyclerView) @@ -126,17 +175,13 @@ class ShortsFragment : MainFragment() { super.onPageScrollStateChanged(state) if (state == ViewPager2.SCROLL_STATE_IDLE) { val adapter = (viewPager.adapter as CustomViewAdapter) - val position = adapter.newPosition - if (position == null) { - return - } + val position = adapter.newPosition ?: return adapter.newPosition = null play(adapter, position) } } }) - setLoading(false) } private fun nextPage() { @@ -144,12 +189,17 @@ class ShortsFragment : MainFragment() { val nextPageTask = TaskHandler>(StateApp.instance.scopeGetter, { - shortsPager!!.nextPage() + currentShortsPager!!.nextPage() - return@TaskHandler shortsPager!!.getResults() + return@TaskHandler currentShortsPager!!.getResults() }).success { newVideos -> val prevCount = customViewAdapter!!.itemCount - videos.addAll(newVideos) + currentShorts.addAll(newVideos) + if (isChannelShortsMode) { + channelShorts.addAll(newVideos) + } else { + mainShorts.addAll(newVideos) + } customViewAdapter!!.notifyItemRangeInserted(prevCount, newVideos.size) nextPageTask = null } @@ -170,13 +220,19 @@ class ShortsFragment : MainFragment() { return@TaskHandler pager }).success { pager -> - videos.clear() - videos.addAll(pager.getResults()) - shortsPager = pager + mainShorts.clear() + mainShorts.addAll(pager.getResults()) + mainShortsPager = pager - // if the view pager exists go back to the beginning - viewPager?.adapter?.notifyDataSetChanged() - viewPager?.currentItem = 0 + if (!isChannelShortsMode) { + currentShorts.clear() + currentShorts.addAll(mainShorts) + currentShortsPager = pager + + // if the view pager exists go back to the beginning + viewPager?.adapter?.notifyDataSetChanged() + viewPager?.currentItem = 0 + } loadPagerTask = null }.exception { err -> diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/ChannelViewPagerAdapter.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/ChannelViewPagerAdapter.kt index 3bd06903..c13a2df0 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/ChannelViewPagerAdapter.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/ChannelViewPagerAdapter.kt @@ -9,8 +9,10 @@ import com.futo.platformplayer.api.media.models.ResultCapabilities import com.futo.platformplayer.api.media.models.channels.IPlatformChannel import com.futo.platformplayer.api.media.models.contents.ContentType import com.futo.platformplayer.api.media.models.contents.IPlatformContent +import com.futo.platformplayer.api.media.structures.IPager import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.constructs.Event2 +import com.futo.platformplayer.constructs.Event3 import com.futo.platformplayer.fragment.channel.tab.ChannelAboutFragment import com.futo.platformplayer.fragment.channel.tab.ChannelContentsFragment import com.futo.platformplayer.fragment.channel.tab.ChannelListFragment @@ -38,6 +40,7 @@ class ChannelViewPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifec val onContentUrlClicked = Event2() val onUrlClicked = Event1() val onContentClicked = Event2() + val onShortClicked = Event3, ArrayList>?>() val onChannelClicked = Event1() val onAddToClicked = Event1() val onAddToQueueClicked = Event1() @@ -81,7 +84,9 @@ class ChannelViewPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifec when (_tabs[position]) { ChannelTab.VIDEOS -> { fragment = ChannelContentsFragment.newInstance().apply { - onContentClicked.subscribe(this@ChannelViewPagerAdapter.onContentClicked::emit) + onContentClicked.subscribe { video, num, _ -> + this@ChannelViewPagerAdapter.onContentClicked.emit(video, num) + } onContentUrlClicked.subscribe(this@ChannelViewPagerAdapter.onContentUrlClicked::emit) onUrlClicked.subscribe(this@ChannelViewPagerAdapter.onUrlClicked::emit) onChannelClicked.subscribe(this@ChannelViewPagerAdapter.onChannelClicked::emit) @@ -94,7 +99,7 @@ class ChannelViewPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifec ChannelTab.SHORTS -> { fragment = ChannelContentsFragment.newInstance(ResultCapabilities.TYPE_SHORTS).apply { - onContentClicked.subscribe(this@ChannelViewPagerAdapter.onContentClicked::emit) + onContentClicked.subscribe(this@ChannelViewPagerAdapter.onShortClicked::emit) onContentUrlClicked.subscribe(this@ChannelViewPagerAdapter.onContentUrlClicked::emit) onUrlClicked.subscribe(this@ChannelViewPagerAdapter.onUrlClicked::emit) onChannelClicked.subscribe(this@ChannelViewPagerAdapter.onChannelClicked::emit)