From 5edd389e841a9b12b42694702cdd898825a5f0ac Mon Sep 17 00:00:00 2001 From: Kai DeLorenzo Date: Tue, 4 Jun 2024 20:22:42 -0500 Subject: [PATCH] removed hardcoding. fixed bugs. hide CHANNELS and SUPPORT for non polycentric linked channels --- .../channel/tab/ChannelAboutFragment.kt | 2 +- .../channel/tab/ChannelContentsFragment.kt | 2 +- .../channel/tab/ChannelListFragment.kt | 2 +- .../tab/ChannelMonetizationFragment.kt | 2 +- .../channel/tab/ChannelPlaylistsFragment.kt | 154 ++-- .../channel/tab/IChannelTabFragment.kt | 8 +- .../mainactivity/main/ChannelFragment.kt | 658 ++++++++++-------- .../views/adapters/ChannelViewPagerAdapter.kt | 158 +++-- 8 files changed, 505 insertions(+), 481 deletions(-) diff --git a/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelAboutFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelAboutFragment.kt index f7d9d9bf..19f1454b 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelAboutFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelAboutFragment.kt @@ -114,7 +114,7 @@ class ChannelAboutFragment : Fragment, IChannelTabFragment { } - fun setPolycentricProfile(polycentricProfile: PolycentricProfile?) { + override fun setPolycentricProfile(polycentricProfile: PolycentricProfile?) { _lastPolycentricProfile = polycentricProfile; if (polycentricProfile == null) { 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 0647afed..ea8ee4fa 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 @@ -309,7 +309,7 @@ class ChannelContentsFragment : Fragment(), IChannelTabFragment { _adapterResults?.setLoading(loading); } - fun setPolycentricProfile(polycentricProfile: PolycentricProfile?) { + override fun setPolycentricProfile(polycentricProfile: PolycentricProfile?) { val p = _lastPolycentricProfile; if (p != null && polycentricProfile != null && p.system == polycentricProfile.system) { Logger.i(TAG, "setPolycentricProfile skipped because previous was same"); diff --git a/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelListFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelListFragment.kt index 00ec5982..807fbd90 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelListFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelListFragment.kt @@ -124,7 +124,7 @@ class ChannelListFragment : Fragment, IChannelTabFragment { } } - fun setPolycentricProfile(polycentricProfile: PolycentricProfile?) { + override fun setPolycentricProfile(polycentricProfile: PolycentricProfile?) { _taskLoadChannel.cancel(); _lastPolycentricProfile = polycentricProfile; diff --git a/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelMonetizationFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelMonetizationFragment.kt index e2e39ee7..53268d16 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelMonetizationFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelMonetizationFragment.kt @@ -46,7 +46,7 @@ class ChannelMonetizationFragment : Fragment, IChannelTabFragment { _lastChannel = channel; } - fun setPolycentricProfile(polycentricProfile: PolycentricProfile?) { + override fun setPolycentricProfile(polycentricProfile: PolycentricProfile?) { _lastPolycentricProfile = polycentricProfile if (polycentricProfile != null) { _supportView?.setPolycentricProfile(polycentricProfile) diff --git a/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelPlaylistsFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelPlaylistsFragment.kt index a344c7da..dbb58b68 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelPlaylistsFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/ChannelPlaylistsFragment.kt @@ -15,6 +15,7 @@ import com.futo.platformplayer.api.media.models.PlatformAuthorLink 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.models.playlists.IPlatformPlaylist import com.futo.platformplayer.api.media.platforms.js.models.JSPager import com.futo.platformplayer.api.media.structures.IAsyncPager import com.futo.platformplayer.api.media.structures.IPager @@ -27,12 +28,8 @@ import com.futo.platformplayer.constructs.TaskHandler import com.futo.platformplayer.engine.exceptions.PluginException import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException import com.futo.platformplayer.exceptions.ChannelException -import com.futo.platformplayer.fragment.mainactivity.main.FeedView -import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile import com.futo.platformplayer.logging.Logger -import com.futo.platformplayer.states.StateCache import com.futo.platformplayer.states.StatePlatform -import com.futo.platformplayer.states.StateSubscriptions import com.futo.platformplayer.views.FeedStyle import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader @@ -42,15 +39,13 @@ import kotlinx.coroutines.launch class ChannelPlaylistsFragment : Fragment(), IChannelTabFragment { private var _recyclerResults: RecyclerView? = null - private var _llmVideo: LinearLayoutManager? = null + private var _llmPlaylist: LinearLayoutManager? = null private var _loading = false - private var _pagerParent: IPager? = null - private var _pager: IPager? = null - private var _cache: FeedView.ItemCache? = null + private var _pagerParent: IPager? = null + private var _pager: IPager? = null private var _channel: IPlatformChannel? = null private var _results: ArrayList = arrayListOf() private var _adapterResults: InsertedViewAdapterWithLoader? = null - private var _lastPolycentricProfile: PolycentricProfile? = null val onContentClicked = Event2() val onContentUrlClicked = Event2() @@ -61,62 +56,49 @@ class ChannelPlaylistsFragment : Fragment(), IChannelTabFragment { val onAddToWatchLaterClicked = Event1() val onLongPress = Event1() - private fun getPlaylistPager(channel: IPlatformChannel): IPager { + private fun getPlaylistPager(channel: IPlatformChannel): IPager { Logger.i(TAG, "getPlaylistPager") - return StatePlatform.instance.getChannelPlaylists(channel.url) as IPager + return StatePlatform.instance.getChannelPlaylists(channel.url) } - private val _taskLoadVideos = - TaskHandler>({ lifecycleScope }, { + private val _taskLoadPlaylists = + TaskHandler>({ lifecycleScope }, { val livePager = getPlaylistPager(it) - return@TaskHandler if (_channel?.let { channel -> - StateSubscriptions.instance.isSubscribed( - channel - ) - } == true) - StateCache.cachePagerResults(lifecycleScope, livePager) - else livePager + return@TaskHandler livePager }).success { livePager -> setLoading(false) setPager(livePager) - } - .exception { } - .exception { - Logger.w(TAG, "Failed to load initial videos.", it) - UIDialogs.showGeneralRetryErrorDialog( - requireContext(), + }.exception { }.exception { + Logger.w(TAG, "Failed to load initial playlists.", it) + UIDialogs.showGeneralRetryErrorDialog(requireContext(), it.message ?: "", it, { loadNextPage() }) } - private var _nextPageHandler: TaskHandler, List> = - TaskHandler, List>({ lifecycleScope }, { - if (it is IAsyncPager<*>) - it.nextPageAsync() - else - it.nextPage() + private var _nextPageHandler: TaskHandler, List> = + TaskHandler, List>({ lifecycleScope }, { + if (it is IAsyncPager<*>) it.nextPageAsync() + else it.nextPage() processPagerExceptions(it) return@TaskHandler it.getResults() }).success { setLoading(false) val posBefore = _results.size - //val toAdd = it.filter { it is IPlatformVideo }.map { it as IPlatformVideo } _results.addAll(it) - _adapterResults?.let { adapterVideo -> - adapterVideo.notifyItemRangeInserted( - adapterVideo.childToParentPosition( + _adapterResults?.let { adapterResult -> + adapterResult.notifyItemRangeInserted( + adapterResult.childToParentPosition( posBefore ), it.size ) } }.exception { Logger.w(TAG, "Failed to load next page.", it) - UIDialogs.showGeneralRetryErrorDialog( - requireContext(), + UIDialogs.showGeneralRetryErrorDialog(requireContext(), it.message ?: "", it, { loadNextPage() }) @@ -127,10 +109,10 @@ class ChannelPlaylistsFragment : Fragment(), IChannelTabFragment { super.onScrolled(recyclerView, dx, dy) val recyclerResults = _recyclerResults ?: return - val llmVideo = _llmVideo ?: return + val llmPlaylist = _llmPlaylist ?: return val visibleItemCount = recyclerResults.childCount - val firstVisibleItem = llmVideo.findFirstVisibleItemPosition() + val firstVisibleItem = llmPlaylist.findFirstVisibleItemPosition() val visibleThreshold = 15 if (!_loading && firstVisibleItem + visibleItemCount + visibleThreshold >= _results.size) { loadNextPage() @@ -147,7 +129,7 @@ class ChannelPlaylistsFragment : Fragment(), IChannelTabFragment { Logger.i(TAG, "setChannel setChannel=${channel}") - _taskLoadVideos.cancel() + _taskLoadPlaylists.cancel() _channel = channel _results.clear() @@ -157,20 +139,14 @@ class ChannelPlaylistsFragment : Fragment(), IChannelTabFragment { } override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val view = inflater.inflate(R.layout.fragment_channel_videos, container, false) _recyclerResults = view.findViewById(R.id.recycler_videos) _adapterResults = PreviewContentListAdapter( - view.context, - FeedStyle.THUMBNAIL, - _results, - null, - Settings.instance.channel.progressBar + view.context, FeedStyle.THUMBNAIL, _results, null, Settings.instance.channel.progressBar ).apply { this.onContentUrlClicked.subscribe(this@ChannelPlaylistsFragment.onContentUrlClicked::emit) this.onUrlClicked.subscribe(this@ChannelPlaylistsFragment.onUrlClicked::emit) @@ -182,9 +158,9 @@ class ChannelPlaylistsFragment : Fragment(), IChannelTabFragment { this.onLongPress.subscribe(this@ChannelPlaylistsFragment.onLongPress::emit) } - _llmVideo = LinearLayoutManager(view.context) + _llmPlaylist = LinearLayoutManager(view.context) _recyclerResults?.adapter = _adapterResults - _recyclerResults?.layoutManager = _llmVideo + _recyclerResults?.layoutManager = _llmPlaylist _recyclerResults?.addOnScrollListener(_scrollListener) return view @@ -196,31 +172,29 @@ class ChannelPlaylistsFragment : Fragment(), IChannelTabFragment { _recyclerResults = null _pager = null - _taskLoadVideos.cancel() + _taskLoadPlaylists.cancel() _nextPageHandler.cancel() } private fun setPager( - pager: IPager, - cache: FeedView.ItemCache? = null + pager: IPager ) { if (_pagerParent != null && _pagerParent is IRefreshPager<*>) { (_pagerParent as IRefreshPager<*>).onPagerError.remove(this) (_pagerParent as IRefreshPager<*>).onPagerChanged.remove(this) _pagerParent = null } - if (_pager is IReplacerPager<*>) - (_pager as IReplacerPager<*>).onReplaced.remove(this) + if (_pager is IReplacerPager<*>) (_pager as IReplacerPager<*>).onReplaced.remove(this) - val pagerToSet: IPager? + val pagerToSet: IPager? if (pager is IRefreshPager<*>) { _pagerParent = pager - pagerToSet = pager.getCurrentPager() as IPager + pagerToSet = pager.getCurrentPager() as IPager pager.onPagerChanged.subscribe(this) { lifecycleScope.launch(Dispatchers.Main) { try { - loadPagerInternal(it as IPager) + loadPagerInternal(it as IPager) } catch (e: Throwable) { Logger.e(TAG, "loadPagerInternal failed.", e) } @@ -228,33 +202,26 @@ class ChannelPlaylistsFragment : Fragment(), IChannelTabFragment { } pager.onPagerError.subscribe(this) { Logger.e(TAG, "Search pager failed: ${it.message}", it) - if (it is PluginException) - UIDialogs.toast("Plugin [${it.config.name}] failed due to:\n${it.message}") - else - UIDialogs.toast("Plugin failed due to:\n${it.message}") + if (it is PluginException) UIDialogs.toast("Plugin [${it.config.name}] failed due to:\n${it.message}") + else UIDialogs.toast("Plugin failed due to:\n${it.message}") } } else pagerToSet = pager - loadPagerInternal(pagerToSet, cache) + loadPagerInternal(pagerToSet) } private fun loadPagerInternal( - pager: IPager, - cache: FeedView.ItemCache? = null + pager: IPager ) { - _cache = cache - - if (_pager is IReplacerPager<*>) - (_pager as IReplacerPager<*>).onReplaced.remove(this) + if (_pager is IReplacerPager<*>) (_pager as IReplacerPager<*>).onReplaced.remove(this) if (pager is IReplacerPager<*>) { pager.onReplaced.subscribe(this) { oldItem, newItem -> - if (_pager != pager) - return@subscribe + if (_pager != pager) return@subscribe lifecycleScope.launch(Dispatchers.Main) { val toReplaceIndex = _results.indexOfFirst { it == oldItem } if (toReplaceIndex >= 0) { - _results[toReplaceIndex] = newItem as IPlatformContent + _results[toReplaceIndex] = newItem as IPlatformPlaylist _adapterResults?.let { it.notifyItemChanged(it.childToParentPosition(toReplaceIndex)) } @@ -277,11 +244,11 @@ class ChannelPlaylistsFragment : Fragment(), IChannelTabFragment { private fun loadInitial() { val channel: IPlatformChannel = _channel ?: return setLoading(true) - _taskLoadVideos.run(channel) + _taskLoadPlaylists.run(channel) } private fun loadNextPage() { - val pager: IPager = _pager ?: return + val pager: IPager = _pager ?: return if (_pager?.hasMorePages() == true) { setLoading(true) _nextPageHandler.run(pager) @@ -293,32 +260,11 @@ class ChannelPlaylistsFragment : Fragment(), IChannelTabFragment { _adapterResults?.setLoading(loading) } - fun setPolycentricProfile(polycentricProfile: PolycentricProfile?) { - val p = _lastPolycentricProfile - if (p != null && polycentricProfile != null && p.system == polycentricProfile.system) { - Logger.i( - ChannelContentsFragment.TAG, - "setPolycentricProfile skipped because previous was same" - ) - return - } - - _lastPolycentricProfile = polycentricProfile - - if (polycentricProfile != null) { - _taskLoadVideos.cancel() - val itemsRemoved = _results.size - _results.clear() - _adapterResults?.notifyItemRangeRemoved(0, itemsRemoved) - loadInitial() - } - } - private fun processPagerExceptions(pager: IPager<*>) { if (pager is MultiPager<*> && pager.allowFailure) { val ex = pager.getResultExceptions() for (kv in ex) { - val jsVideoPager: JSPager<*>? = when (kv.key) { + val jsPager: JSPager<*>? = when (kv.key) { is MultiPager<*> -> (kv.key as MultiPager<*>).findPager { it is JSPager<*> } as JSPager<*>? is JSPager<*> -> kv.key as JSPager<*> else -> null @@ -329,14 +275,12 @@ class ChannelPlaylistsFragment : Fragment(), IChannelTabFragment { try { val channel = if (kv.value is ChannelException) (kv.value as ChannelException).channelNameOrUrl else null - if (jsVideoPager != null) - UIDialogs.toast( - it, "Plugin ${jsVideoPager.getPluginConfig().name} failed:\n" + - (if (!channel.isNullOrEmpty()) "(${channel}) " else "") + - "${kv.value.message}", false - ) - else - UIDialogs.toast(it, kv.value.message ?: "", false) + if (jsPager != null) UIDialogs.toast( + it, + "Plugin ${jsPager.getPluginConfig().name} failed:\n" + (if (!channel.isNullOrEmpty()) "(${channel}) " else "") + "${kv.value.message}", + false + ) + else UIDialogs.toast(it, kv.value.message ?: "", false) } catch (e: Throwable) { Logger.e(TAG, "Failed to show toast.", e) } diff --git a/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/IChannelTabFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/IChannelTabFragment.kt index e1da77c5..2b615d25 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/IChannelTabFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/channel/tab/IChannelTabFragment.kt @@ -1,7 +1,11 @@ package com.futo.platformplayer.fragment.channel.tab import com.futo.platformplayer.api.media.models.channels.IPlatformChannel +import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile interface IChannelTabFragment { - fun setChannel(channel: IPlatformChannel); -} \ No newline at end of file + fun setChannel(channel: IPlatformChannel) + fun setPolycentricProfile(polycentricProfile: PolycentricProfile?) { + + } +} 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 2edb7e44..f6126c58 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 @@ -15,8 +15,9 @@ import androidx.appcompat.widget.AppCompatImageView import androidx.lifecycle.lifecycleScope import androidx.viewpager2.widget.ViewPager2 import com.bumptech.glide.Glide -import com.futo.platformplayer.* import com.futo.platformplayer.R +import com.futo.platformplayer.UIDialogs +import com.futo.platformplayer.UISlideOverlays import com.futo.platformplayer.api.media.PlatformID import com.futo.platformplayer.api.media.exceptions.NoPlatformClientException import com.futo.platformplayer.api.media.models.PlatformAuthorLink @@ -27,27 +28,32 @@ import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylist import com.futo.platformplayer.api.media.models.post.IPlatformPost import com.futo.platformplayer.api.media.models.video.IPlatformVideo import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo +import com.futo.platformplayer.assume import com.futo.platformplayer.constructs.TaskHandler -import com.futo.platformplayer.fragment.channel.tab.ChannelAboutFragment -import com.futo.platformplayer.fragment.channel.tab.ChannelContentsFragment -import com.futo.platformplayer.fragment.channel.tab.ChannelListFragment -import com.futo.platformplayer.fragment.channel.tab.ChannelMonetizationFragment -import com.futo.platformplayer.fragment.channel.tab.ChannelPlaylistsFragment +import com.futo.platformplayer.dp +import com.futo.platformplayer.fragment.channel.tab.IChannelTabFragment import com.futo.platformplayer.fragment.mainactivity.topbar.NavigationTopBarFragment import com.futo.platformplayer.images.GlideHelper.Companion.crossfade import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.models.SearchType import com.futo.platformplayer.models.Subscription import com.futo.platformplayer.polycentric.PolycentricCache +import com.futo.platformplayer.selectBestImage +import com.futo.platformplayer.selectHighestResolutionImage import com.futo.platformplayer.states.StatePlatform import com.futo.platformplayer.states.StatePlayer import com.futo.platformplayer.states.StatePlaylists import com.futo.platformplayer.states.StateSubscriptions +import com.futo.platformplayer.toHumanNumber +import com.futo.platformplayer.views.adapters.ChannelTab import com.futo.platformplayer.views.adapters.ChannelViewPagerAdapter import com.futo.platformplayer.views.others.CreatorThumbnail import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuOverlay import com.futo.platformplayer.views.subscriptions.SubscribeButton -import com.futo.polycentric.core.* +import com.futo.polycentric.core.OwnedClaim +import com.futo.polycentric.core.PublicKey +import com.futo.polycentric.core.SystemState +import com.futo.polycentric.core.toURLInfoSystemLinkUrl import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayoutMediator import kotlinx.coroutines.Dispatchers @@ -56,476 +62,510 @@ import kotlinx.coroutines.withContext import kotlinx.serialization.Serializable @Serializable -data class PolycentricProfile(val system: PublicKey, val systemState: SystemState, val ownedClaims: List); - -const val PLAYLIST_POSITION = 4 +data class PolycentricProfile( + val system: PublicKey, val systemState: SystemState, val ownedClaims: List +) class ChannelFragment : MainFragment() { - override val isMainView : Boolean = true; - override val hasBottomBar: Boolean = true; - private var _view: ChannelView? = null; + override val isMainView: Boolean = true + override val hasBottomBar: Boolean = true + private var _view: ChannelView? = null override fun onShownWithView(parameter: Any?, isBack: Boolean) { - super.onShownWithView(parameter, isBack); - _view?.onShown(parameter, isBack); + super.onShownWithView(parameter, isBack) + _view?.onShown(parameter, isBack) } - override fun onCreateMainView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - val view = ChannelView(this, inflater); - _view = view; - return view; + override fun onCreateMainView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + val view = ChannelView(this, inflater) + _view = view + return view } override fun onBackPressed(): Boolean { - return _view?.onBackPressed() ?: false; + return _view?.onBackPressed() ?: false } override fun onDestroyMainView() { - super.onDestroyMainView(); + super.onDestroyMainView() - _view?.cleanup(); - _view = null; + _view?.cleanup() + _view = null } fun selectTab(selectedTabIndex: Int) { - _view?.selectTab(selectedTabIndex); + _view?.selectTab(selectedTabIndex) } @SuppressLint("ViewConstructor") - class ChannelView : LinearLayout { - private val _fragment: ChannelFragment; + class ChannelView + (fragment: ChannelFragment, inflater: LayoutInflater) : LinearLayout(inflater.context) { + private val _fragment: ChannelFragment = fragment - private var _textChannel: TextView; - private var _textChannelSub: TextView; - private var _creatorThumbnail: CreatorThumbnail; - private var _imageBanner: AppCompatImageView; + private var _textChannel: TextView + private var _textChannelSub: TextView + private var _creatorThumbnail: CreatorThumbnail + private var _imageBanner: AppCompatImageView - private var _tabs: TabLayout; - private var _viewPager: ViewPager2; - private var _tabLayoutMediator: TabLayoutMediator; - private var _buttonSubscribe: SubscribeButton; - private var _buttonSubscriptionSettings: ImageButton; + private var _tabs: TabLayout + private var _viewPager: ViewPager2 - private var _overlayContainer: FrameLayout; - private var _overlay_loading: LinearLayout; - private var _overlay_loading_spinner: ImageView; + // private var _adapter: ChannelViewPagerAdapter; + private var _tabLayoutMediator: TabLayoutMediator + private var _buttonSubscribe: SubscribeButton + private var _buttonSubscriptionSettings: ImageButton - private var _slideUpOverlay: SlideUpMenuOverlay? = null; + private var _overlayContainer: FrameLayout + private var _overlayLoading: LinearLayout + private var _overlayLoadingSpinner: ImageView - private var _isLoading: Boolean = false; - private var _selectedTabIndex: Int = -1; + private var _slideUpOverlay: SlideUpMenuOverlay? = null + + private var _isLoading: Boolean = false + private var _selectedTabIndex: Int = -1 var channel: IPlatformChannel? = null - private set; - private var _url: String? = null; + private set + private var _url: String? = null - private val _onPageChangeCallback = object : ViewPager2.OnPageChangeCallback() { - override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { - super.onPageScrolled(position, positionOffset, positionOffsetPixels); - //recalculate(position, positionOffset); - } - } + private val _onPageChangeCallback = object : ViewPager2.OnPageChangeCallback() {} - private val _taskLoadPolycentricProfile: TaskHandler; - private val _taskGetChannel: TaskHandler; + private val _taskLoadPolycentricProfile: TaskHandler + private val _taskGetChannel: TaskHandler - constructor(fragment: ChannelFragment, inflater: LayoutInflater) : super(inflater.context) { - _fragment = fragment; - inflater.inflate(R.layout.fragment_channel, this); - - _taskLoadPolycentricProfile = TaskHandler({fragment.lifecycleScope}, { id -> - return@TaskHandler PolycentricCache.instance.getProfileAsync(id); - }) - .success { it -> setPolycentricProfile(it, animate = true) } - .exception { - Logger.w(TAG, "Failed to load polycentric profile.", it); - }; - - _taskGetChannel = TaskHandler({fragment.lifecycleScope}, { url -> StatePlatform.instance.getChannelLive(url) }) - .success { showChannel(it); } + init { + inflater.inflate(R.layout.fragment_channel, this) + _taskLoadPolycentricProfile = + TaskHandler({ fragment.lifecycleScope }, + { id -> + return@TaskHandler PolycentricCache.instance.getProfileAsync(id) + }).success { setPolycentricProfile(it, animate = true) }.exception { + Logger.w(TAG, "Failed to load polycentric profile.", it) + } + _taskGetChannel = TaskHandler({ fragment.lifecycleScope }, + { url -> StatePlatform.instance.getChannelLive(url) }).success { showChannel(it); } .exception { - UIDialogs.showDialog(context, + UIDialogs.showDialog( + context, R.drawable.ic_sources, - context.getString(R.string.no_source_enabled_to_support_this_channel) + "\n(${_url})", null, null, + context.getString(R.string.no_source_enabled_to_support_this_channel) + "\n(${_url})", + null, + null, 0, UIDialogs.Action("Back", { - fragment.close(true); + fragment.close(true) }, UIDialogs.ActionStyle.PRIMARY) - ); + ) + }.exception { + Logger.e(TAG, "Failed to load channel.", it) + UIDialogs.showGeneralRetryErrorDialog( + context, it.message ?: "", it, { loadChannel() }, null, fragment + ) } - .exception { - Logger.e(TAG, "Failed to load channel.", it); - UIDialogs.showGeneralRetryErrorDialog(context, it.message ?: "", it, { loadChannel() }, null, fragment); - } - - val tabs: TabLayout = findViewById(R.id.tabs); - val viewPager: ViewPager2 = findViewById(R.id.view_pager); - _textChannel = findViewById(R.id.text_channel_name); - _textChannelSub = findViewById(R.id.text_metadata); - _creatorThumbnail = findViewById(R.id.creator_thumbnail); - _imageBanner = findViewById(R.id.image_channel_banner); - _buttonSubscribe = findViewById(R.id.button_subscribe); - _buttonSubscriptionSettings = findViewById(R.id.button_sub_settings); - _overlay_loading = findViewById(R.id.channel_loading_overlay); - _overlay_loading_spinner = findViewById(R.id.channel_loader); - _overlayContainer = findViewById(R.id.overlay_container); - + val tabs: TabLayout = findViewById(R.id.tabs) + val viewPager: ViewPager2 = findViewById(R.id.view_pager) + _textChannel = findViewById(R.id.text_channel_name) + _textChannelSub = findViewById(R.id.text_metadata) + _creatorThumbnail = findViewById(R.id.creator_thumbnail) + _imageBanner = findViewById(R.id.image_channel_banner) + _buttonSubscribe = findViewById(R.id.button_subscribe) + _buttonSubscriptionSettings = findViewById(R.id.button_sub_settings) + _overlayLoading = findViewById(R.id.channel_loading_overlay) + _overlayLoadingSpinner = findViewById(R.id.channel_loader) + _overlayContainer = findViewById(R.id.overlay_container) _buttonSubscribe.onSubscribed.subscribe { - UISlideOverlays.showSubscriptionOptionsOverlay(it, _overlayContainer); - _buttonSubscriptionSettings.visibility = if(_buttonSubscribe.isSubscribed) View.VISIBLE else View.GONE; + UISlideOverlays.showSubscriptionOptionsOverlay(it, _overlayContainer) + _buttonSubscriptionSettings.visibility = + if (_buttonSubscribe.isSubscribed) View.VISIBLE else View.GONE } _buttonSubscribe.onUnSubscribed.subscribe { - _buttonSubscriptionSettings.visibility = if(_buttonSubscribe.isSubscribed) View.VISIBLE else View.GONE; + _buttonSubscriptionSettings.visibility = + if (_buttonSubscribe.isSubscribed) View.VISIBLE else View.GONE } - _buttonSubscriptionSettings.setOnClickListener { - val url = channel?.url ?: _url ?: return@setOnClickListener; - val sub = StateSubscriptions.instance.getSubscription(url) ?: return@setOnClickListener; - UISlideOverlays.showSubscriptionOptionsOverlay(sub, _overlayContainer); - }; + val url = channel?.url ?: _url ?: return@setOnClickListener + val sub = + StateSubscriptions.instance.getSubscription(url) ?: return@setOnClickListener + UISlideOverlays.showSubscriptionOptionsOverlay(sub, _overlayContainer) + } //TODO: Determine if this is really the only solution (isSaveEnabled=false) - viewPager.isSaveEnabled = false; - viewPager.registerOnPageChangeCallback(_onPageChangeCallback); - val adapter = ChannelViewPagerAdapter(fragment.childFragmentManager, fragment.lifecycle); + viewPager.isSaveEnabled = false + viewPager.registerOnPageChangeCallback(_onPageChangeCallback) + val adapter = ChannelViewPagerAdapter(fragment.childFragmentManager, fragment.lifecycle) adapter.onChannelClicked.subscribe { c -> fragment.navigate(c) } adapter.onContentClicked.subscribe { v, _ -> - if(v is IPlatformVideo) { - StatePlayer.instance.clearQueue(); - fragment.navigate(v).maximizeVideoDetail(); - } else if (v is IPlatformPlaylist) { - fragment.navigate(v); - } else if (v is IPlatformPost) { - fragment.navigate(v); + when (v) { + is IPlatformVideo -> { + StatePlayer.instance.clearQueue() + fragment.navigate(v).maximizeVideoDetail() + } + + is IPlatformPlaylist -> { + fragment.navigate(v) + } + + is IPlatformPost -> { + fragment.navigate(v) + } } } - adapter.onAddToClicked.subscribe {content -> + adapter.onAddToClicked.subscribe { content -> _overlayContainer.let { - if(content is IPlatformVideo) - _slideUpOverlay = UISlideOverlays.showVideoOptionsOverlay(content, it); + if (content is IPlatformVideo) _slideUpOverlay = + UISlideOverlays.showVideoOptionsOverlay(content, it) } } adapter.onAddToQueueClicked.subscribe { content -> - if(content is IPlatformVideo) { - StatePlayer.instance.addToQueue(content); + if (content is IPlatformVideo) { + StatePlayer.instance.addToQueue(content) } } adapter.onAddToWatchLaterClicked.subscribe { content -> - if(content is IPlatformVideo) { - StatePlaylists.instance.addToWatchLater(SerializedPlatformVideo.fromVideo(content)); - UIDialogs.toast("Added to watch later\n[${content.name}]"); + if (content is IPlatformVideo) { + StatePlaylists.instance.addToWatchLater( + SerializedPlatformVideo.fromVideo( + content + ) + ) + UIDialogs.toast("Added to watch later\n[${content.name}]") } } adapter.onUrlClicked.subscribe { url -> - fragment.navigate(url); + fragment.navigate(url) } adapter.onContentUrlClicked.subscribe { url, contentType -> - when(contentType) { + when (contentType) { ContentType.MEDIA -> { - StatePlayer.instance.clearQueue(); - fragment.navigate(url).maximizeVideoDetail(); - }; - ContentType.URL -> fragment.navigate(url); - else -> {}; + StatePlayer.instance.clearQueue() + fragment.navigate(url).maximizeVideoDetail() + } + + ContentType.URL -> fragment.navigate(url) + else -> {} } } adapter.onLongPress.subscribe { content -> _overlayContainer.let { - if(content is IPlatformVideo) - _slideUpOverlay = UISlideOverlays.showVideoOptionsOverlay(content, it); + if (content is IPlatformVideo) _slideUpOverlay = + UISlideOverlays.showVideoOptionsOverlay(content, it) } } - viewPager.adapter = adapter; - - val tabLayoutMediator = TabLayoutMediator(tabs, viewPager) { tab, position -> - tab.text = when (position) { - 0 -> "VIDEOS" - 1 -> "CHANNELS" - //2 -> "STORE" - 2 -> "SUPPORT" - 3 -> "ABOUT" - PLAYLIST_POSITION -> "PLAYLISTS" - else -> "Unknown $position" - }; - }; - tabLayoutMediator.attach(); - - _tabLayoutMediator = tabLayoutMediator; - _tabs = tabs; - _viewPager = viewPager; + viewPager.adapter = adapter + val tabLayoutMediator = TabLayoutMediator( + tabs, viewPager, (viewPager.adapter as ChannelViewPagerAdapter)::getTabNames + ) + tabLayoutMediator.attach() + _tabLayoutMediator = tabLayoutMediator + _tabs = tabs + _viewPager = viewPager if (_selectedTabIndex != -1) { - selectTab(_selectedTabIndex); + selectTab(_selectedTabIndex) } - - setLoading(true); + setLoading(true) } fun cleanup() { - _taskLoadPolycentricProfile.cancel(); - _taskGetChannel.cancel(); - _tabLayoutMediator.detach(); - _viewPager.unregisterOnPageChangeCallback(_onPageChangeCallback); - hideSlideUpOverlay(); - (_overlay_loading_spinner.drawable as Animatable?)?.stop(); + _taskLoadPolycentricProfile.cancel() + _taskGetChannel.cancel() + _tabLayoutMediator.detach() + _viewPager.unregisterOnPageChangeCallback(_onPageChangeCallback) + hideSlideUpOverlay() + (_overlayLoadingSpinner.drawable as Animatable?)?.stop() } fun onShown(parameter: Any?, isBack: Boolean) { - hideSlideUpOverlay(); - _taskLoadPolycentricProfile.cancel(); - _selectedTabIndex = -1; + hideSlideUpOverlay() + _taskLoadPolycentricProfile.cancel() + _selectedTabIndex = -1 if (!isBack || _url == null) { - _imageBanner.setImageDrawable(null); + _imageBanner.setImageDrawable(null) - if (parameter is String) { - _buttonSubscribe.setSubscribeChannel(parameter); - _buttonSubscriptionSettings.visibility = if(_buttonSubscribe.isSubscribed) View.VISIBLE else View.GONE; - setPolycentricProfileOr(parameter) { - _textChannel.text = ""; - _textChannelSub.text = ""; - _creatorThumbnail.setThumbnail(null, true); - Glide.with(_imageBanner) - .clear(_imageBanner); - }; + when (parameter) { + is String -> { + _buttonSubscribe.setSubscribeChannel(parameter) + _buttonSubscriptionSettings.visibility = + if (_buttonSubscribe.isSubscribed) View.VISIBLE else View.GONE + setPolycentricProfileOr(parameter) { + _textChannel.text = "" + _textChannelSub.text = "" + _creatorThumbnail.setThumbnail(null, true) + Glide.with(_imageBanner).clear(_imageBanner) + } - _url = parameter; - loadChannel(); - } else if (parameter is SerializedChannel) { - showChannel(parameter); - _url = parameter.url; - loadChannel(); - } else if (parameter is IPlatformChannel) - showChannel(parameter); - else if (parameter is PlatformAuthorLink) { - setPolycentricProfileOr(parameter.url) { - _textChannel.text = parameter.name; - _textChannelSub.text = ""; - _creatorThumbnail.setThumbnail(parameter.thumbnail, true); - Glide.with(_imageBanner) - .clear(_imageBanner); + _url = parameter + loadChannel() + } - loadPolycentricProfile(parameter.id, parameter.url) - }; + is SerializedChannel -> { + showChannel(parameter) + _url = parameter.url + loadChannel() + } - _url = parameter.url; - loadChannel(); - } else if (parameter is Subscription) { - setPolycentricProfileOr(parameter.channel.url) { - _textChannel.text = parameter.channel.name; - _textChannelSub.text = ""; - _creatorThumbnail.setThumbnail(parameter.channel.thumbnail, true); - Glide.with(_imageBanner) - .clear(_imageBanner); + is IPlatformChannel -> showChannel(parameter) + is PlatformAuthorLink -> { + setPolycentricProfileOr(parameter.url) { + _textChannel.text = parameter.name + _textChannelSub.text = "" + _creatorThumbnail.setThumbnail(parameter.thumbnail, true) + Glide.with(_imageBanner).clear(_imageBanner) - loadPolycentricProfile(parameter.channel.id, parameter.channel.url) - }; + loadPolycentricProfile(parameter.id, parameter.url) + } - _url = parameter.channel.url; - loadChannel(); + _url = parameter.url + loadChannel() + } + + is Subscription -> { + setPolycentricProfileOr(parameter.channel.url) { + _textChannel.text = parameter.channel.name + _textChannelSub.text = "" + _creatorThumbnail.setThumbnail(parameter.channel.thumbnail, true) + Glide.with(_imageBanner).clear(_imageBanner) + + loadPolycentricProfile(parameter.channel.id, parameter.channel.url) + } + + _url = parameter.channel.url + loadChannel() + } } } else { - loadChannel(); + loadChannel() } } fun selectTab(selectedTabIndex: Int) { - _selectedTabIndex = selectedTabIndex; - _tabs.selectTab(_tabs.getTabAt(selectedTabIndex)); + _selectedTabIndex = selectedTabIndex + _tabs.selectTab(_tabs.getTabAt(selectedTabIndex)) } private fun loadPolycentricProfile(id: PlatformID, url: String) { - val cachedPolycentricProfile = PolycentricCache.instance.getCachedProfile(url, true); + val cachedPolycentricProfile = PolycentricCache.instance.getCachedProfile(url, true) if (cachedPolycentricProfile != null) { setPolycentricProfile(cachedPolycentricProfile, animate = true) if (cachedPolycentricProfile.expired) { - _taskLoadPolycentricProfile.run(id); + _taskLoadPolycentricProfile.run(id) } } else { - _taskLoadPolycentricProfile.run(id); + _taskLoadPolycentricProfile.run(id) } } private fun setLoading(isLoading: Boolean) { if (_isLoading == isLoading) { - return; + return } - _isLoading = isLoading; - if(isLoading){ - _overlay_loading.visibility = View.VISIBLE; - (_overlay_loading_spinner.drawable as Animatable?)?.start(); - } - else { - (_overlay_loading_spinner.drawable as Animatable?)?.stop(); - _overlay_loading.visibility = View.GONE; + _isLoading = isLoading + if (isLoading) { + _overlayLoading.visibility = View.VISIBLE + (_overlayLoadingSpinner.drawable as Animatable?)?.start() + } else { + (_overlayLoadingSpinner.drawable as Animatable?)?.stop() + _overlayLoading.visibility = View.GONE } } fun onBackPressed(): Boolean { if (_slideUpOverlay != null) { - hideSlideUpOverlay(); - return true; + hideSlideUpOverlay() + return true } - return false; + return false } private fun hideSlideUpOverlay() { - _slideUpOverlay?.hide(false); - _slideUpOverlay = null; + _slideUpOverlay?.hide(false) + _slideUpOverlay = null } private fun loadChannel() { - val url = _url; + val url = _url if (url != null) { - setLoading(true); - _taskGetChannel.run(url); + setLoading(true) + _taskGetChannel.run(url) } } private fun showChannel(channel: IPlatformChannel) { - setLoading(false); + setLoading(false) - if (!StatePlatform.instance.getChannelClient(channel.url).capabilities.hasGetChannelPlaylists) { - _tabs.removeTabAt(PLAYLIST_POSITION) - } - - _fragment.topBar?.onShown(channel); + _fragment.topBar?.onShown(channel) val buttons = arrayListOf(Pair(R.drawable.ic_playlist_add) { - UIDialogs.showConfirmationDialog(context, context.getString(R.string.do_you_want_to_convert_channel_channelname_to_a_playlist).replace("{channelName}", channel.name), { - UIDialogs.showDialogProgress(context) { - _fragment.lifecycleScope.launch(Dispatchers.IO) { - try { - StatePlaylists.instance.createPlaylistFromChannel(channel) { page -> - _fragment.lifecycleScope.launch(Dispatchers.Main) { - it.setText("${channel.name}\n" + context.getString(R.string.page) + " $page"); + UIDialogs.showConfirmationDialog(context, + context.getString(R.string.do_you_want_to_convert_channel_channelname_to_a_playlist) + .replace("{channelName}", channel.name), + { + UIDialogs.showDialogProgress(context) { + _fragment.lifecycleScope.launch(Dispatchers.IO) { + try { + StatePlaylists.instance.createPlaylistFromChannel(channel) { page -> + _fragment.lifecycleScope.launch(Dispatchers.Main) { + it.setText("${channel.name}\n" + context.getString(R.string.page) + " $page") + } } - }; - } - catch(ex: Exception) { - Logger.e(TAG, "Error", ex); - UIDialogs.showGeneralErrorDialog(context, context.getString(R.string.failed_to_convert_channel), ex); - } + } catch (ex: Exception) { + Logger.e(TAG, "Error", ex) + UIDialogs.showGeneralErrorDialog( + context, + context.getString(R.string.failed_to_convert_channel), + ex + ) + } - withContext(Dispatchers.Main) { - it.hide(); + withContext(Dispatchers.Main) { + it.hide() + } } - }; - }; - }); - }); + } + }) + }) _fragment.lifecycleScope.launch(Dispatchers.IO) { - val plugin = StatePlatform.instance.getChannelClientOrNull(channel.url); + val plugin = StatePlatform.instance.getChannelClientOrNull(channel.url) withContext(Dispatchers.Main) { if (plugin != null && plugin.capabilities.hasSearchChannelContents) { buttons.add(Pair(R.drawable.ic_search) { - _fragment.navigate(SuggestionsFragmentData("", SearchType.VIDEO, channel.url)); - }); + _fragment.navigate( + SuggestionsFragmentData( + "", SearchType.VIDEO, channel.url + ) + ) + }) - _fragment.topBar?.assume()?.setMenuItems(buttons); + _fragment.topBar?.assume()?.setMenuItems(buttons) } } } - _buttonSubscribe.setSubscribeChannel(channel); - _buttonSubscriptionSettings.visibility = if(_buttonSubscribe.isSubscribed) View.VISIBLE else View.GONE; - _textChannel.text = channel.name; - _textChannelSub.text = if(channel.subscribers > 0) "${channel.subscribers.toHumanNumber()} " + context.getString(R.string.subscribers).lowercase() else ""; + _buttonSubscribe.setSubscribeChannel(channel) + _buttonSubscriptionSettings.visibility = + if (_buttonSubscribe.isSubscribed) View.VISIBLE else View.GONE + _textChannel.text = channel.name + _textChannelSub.text = + if (channel.subscribers > 0) "${channel.subscribers.toHumanNumber()} " + context.getString( + R.string.subscribers + ).lowercase() else "" - //TODO: Find a better way to access the adapter fragments.. - - (_viewPager.adapter as ChannelViewPagerAdapter?)?.let { - it.getFragment().setChannel(channel); - it.getFragment().setChannel(channel); - it.getFragment().setChannel(channel); - it.getFragment().setChannel(channel); - if (StatePlatform.instance.getChannelClient(channel.url).capabilities.hasGetChannelPlaylists) { - Logger.w(TAG, "Supported channel playlists??"); - it.getFragment().setChannel(channel); - } - //TODO: Call on other tabs as needed + val supportsPlaylists = + StatePlatform.instance.getChannelClient(channel.url).capabilities.hasGetChannelPlaylists + if (supportsPlaylists && !(_viewPager.adapter as ChannelViewPagerAdapter).containsItem( + ChannelTab.PLAYLISTS.ordinal.toLong() + ) + ) { + (_viewPager.adapter as ChannelViewPagerAdapter).insert(2, ChannelTab.PLAYLISTS) + } + if (!supportsPlaylists && (_viewPager.adapter as ChannelViewPagerAdapter).containsItem( + ChannelTab.PLAYLISTS.ordinal.toLong() + ) + ) { + (_viewPager.adapter as ChannelViewPagerAdapter).remove(2) } - this.channel = channel; + // sets the channel for each tab + for (fragment in _fragment.childFragmentManager.fragments) { + (fragment as IChannelTabFragment).setChannel(channel) + } + + (_viewPager.adapter as ChannelViewPagerAdapter).channel = channel + + + _viewPager.adapter!!.notifyDataSetChanged() + + this.channel = channel setPolycentricProfileOr(channel.url) { - _textChannel.text = channel.name; - _creatorThumbnail.setThumbnail(channel.thumbnail, true); - Glide.with(_imageBanner) - .load(channel.banner) - .crossfade() - .into(_imageBanner); + _textChannel.text = channel.name + _creatorThumbnail.setThumbnail(channel.thumbnail, true) + Glide.with(_imageBanner).load(channel.banner).crossfade().into(_imageBanner) - _taskLoadPolycentricProfile.run(channel.id); - }; + _taskLoadPolycentricProfile.run(channel.id) + } } private fun setPolycentricProfileOr(url: String, or: () -> Unit) { - setPolycentricProfile(null, animate = false); + setPolycentricProfile(null, animate = false) - val cachedProfile = channel?.let { PolycentricCache.instance.getCachedProfile(url) }; + val cachedProfile = channel?.let { PolycentricCache.instance.getCachedProfile(url) } if (cachedProfile != null) { - setPolycentricProfile(cachedProfile, animate = false); + setPolycentricProfile(cachedProfile, animate = false) } else { - or(); + or() } } - private fun setPolycentricProfile(cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) { - val dp_35 = 35.dp(resources) - val profile = cachedPolycentricProfile?.profile; - val avatar = profile?.systemState?.avatar?.selectBestImage(dp_35 * dp_35) - ?.let { it.toURLInfoSystemLinkUrl(profile.system.toProto(), it.process, profile.systemState.servers.toList()) }; - - if (avatar != null) { - _creatorThumbnail.setThumbnail(avatar, animate); - } else { - _creatorThumbnail.setThumbnail(channel?.thumbnail, animate); - _creatorThumbnail.setHarborAvailable(profile != null, animate, profile?.system?.toProto()); + private fun setPolycentricProfile( + cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean + ) { + val dp35 = 35.dp(resources) + val profile = cachedPolycentricProfile?.profile + val avatar = profile?.systemState?.avatar?.selectBestImage(dp35 * dp35)?.let { + it.toURLInfoSystemLinkUrl( + profile.system.toProto(), it.process, profile.systemState.servers.toList() + ) } - val banner = profile?.systemState?.banner?.selectHighestResolutionImage() - ?.let { it.toURLInfoSystemLinkUrl(profile.system.toProto(), it.process, profile.systemState.servers.toList()) }; + if (avatar != null) { + _creatorThumbnail.setThumbnail(avatar, animate) + } else { + _creatorThumbnail.setThumbnail(channel?.thumbnail, animate) + _creatorThumbnail.setHarborAvailable( + profile != null, animate, profile?.system?.toProto() + ) + } + + val banner = profile?.systemState?.banner?.selectHighestResolutionImage()?.let { + it.toURLInfoSystemLinkUrl( + profile.system.toProto(), it.process, profile.systemState.servers.toList() + ) + } if (banner != null) { - Glide.with(_imageBanner) - .load(banner) - .crossfade() - .into(_imageBanner); + Glide.with(_imageBanner).load(banner).crossfade().into(_imageBanner) } else { - Glide.with(_imageBanner) - .load(channel?.banner) - .crossfade() - .into(_imageBanner); + Glide.with(_imageBanner).load(channel?.banner).crossfade().into(_imageBanner) } if (profile != null) { - _fragment.topBar?.onShown(profile); - _textChannel.text = profile.systemState.username; + _fragment.topBar?.onShown(profile) + _textChannel.text = profile.systemState.username } - (_viewPager.adapter as ChannelViewPagerAdapter?)?.let { - it.getFragment().setPolycentricProfile(profile); - it.getFragment().setPolycentricProfile(profile); - it.getFragment().setPolycentricProfile(profile); - it.getFragment().setPolycentricProfile(profile); - channel?.let { channel -> - if (StatePlatform.instance.getChannelClient(channel.url).capabilities.hasGetChannelPlaylists) { - Logger.w(TAG, "Supported channel playlists??"); - it.getFragment().setPolycentricProfile(profile); - } - } - //TODO: Call on other tabs as needed + // sets the profile for each tab + for (fragment in _fragment.childFragmentManager.fragments) { + (fragment as IChannelTabFragment).setPolycentricProfile(profile) } + + //TODO only add channels and support if its setup on the polycentric profile + if (profile != null && !(_viewPager.adapter as ChannelViewPagerAdapter).containsItem( + ChannelTab.SUPPORT.ordinal.toLong() + ) + ) { + (_viewPager.adapter as ChannelViewPagerAdapter).insert(2, ChannelTab.SUPPORT) + } + if (profile != null && !(_viewPager.adapter as ChannelViewPagerAdapter).containsItem( + ChannelTab.CHANNELS.ordinal.toLong() + ) + ) { + (_viewPager.adapter as ChannelViewPagerAdapter).insert(2, ChannelTab.CHANNELS) + } + (_viewPager.adapter as ChannelViewPagerAdapter).profile = profile + _viewPager.adapter!!.notifyDataSetChanged() } } companion object { - val TAG = "ChannelFragment"; + const val TAG = "ChannelFragment" fun newInstance() = ChannelFragment().apply { } } } 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 e908084b..e0618cdd 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 @@ -5,81 +5,117 @@ import androidx.fragment.app.FragmentManager import androidx.lifecycle.Lifecycle import androidx.viewpager2.adapter.FragmentStateAdapter import com.futo.platformplayer.api.media.models.PlatformAuthorLink +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.constructs.Event1 import com.futo.platformplayer.constructs.Event2 -import com.futo.platformplayer.fragment.channel.tab.* +import com.futo.platformplayer.fragment.channel.tab.ChannelAboutFragment +import com.futo.platformplayer.fragment.channel.tab.ChannelContentsFragment +import com.futo.platformplayer.fragment.channel.tab.ChannelListFragment +import com.futo.platformplayer.fragment.channel.tab.ChannelMonetizationFragment +import com.futo.platformplayer.fragment.channel.tab.ChannelPlaylistsFragment +import com.futo.platformplayer.fragment.channel.tab.IChannelTabFragment +import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile +import com.google.android.material.tabs.TabLayout -class ChannelViewPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) : FragmentStateAdapter(fragmentManager, lifecycle) { - private val _cache: Array = arrayOfNulls(5); - val onContentUrlClicked = Event2(); - val onUrlClicked = Event1(); - val onContentClicked = Event2(); - val onChannelClicked = Event1(); - val onAddToClicked = Event1(); - val onAddToQueueClicked = Event1(); - val onAddToWatchLaterClicked = Event1(); - val onLongPress = Event1(); +enum class ChannelTab { + VIDEOS, CHANNELS, PLAYLISTS, SUPPORT, ABOUT +} + +class ChannelViewPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) : + FragmentStateAdapter(fragmentManager, lifecycle) { + private val _supportedFragments = mutableMapOf( + ChannelTab.VIDEOS.ordinal to ChannelTab.VIDEOS, ChannelTab.ABOUT.ordinal to ChannelTab.ABOUT + ) + private val _tabs = arrayListOf(ChannelTab.VIDEOS, ChannelTab.ABOUT) + + var profile: PolycentricProfile? = null + var channel: IPlatformChannel? = null + + val onContentUrlClicked = Event2() + val onUrlClicked = Event1() + val onContentClicked = Event2() + val onChannelClicked = Event1() + val onAddToClicked = Event1() + val onAddToQueueClicked = Event1() + val onAddToWatchLaterClicked = Event1() + val onLongPress = Event1() + + override fun getItemId(position: Int): Long { + return _tabs[position].ordinal.toLong() + } + + override fun containsItem(itemId: Long): Boolean { + return _supportedFragments.containsKey(itemId.toInt()) + } override fun getItemCount(): Int { - return _cache.size; + return _supportedFragments.size } - inline fun getFragment(): T { - //TODO: I have a feeling this can somehow be synced with createFragment so only 1 mapping exists (without a Map<>) - if(T::class == ChannelContentsFragment::class) - return createFragment(0) as T; - else if(T::class == ChannelListFragment::class) - return createFragment(1) as T; - //else if(T::class == ChannelStoreFragment::class) - // return createFragment(2) as T; - else if(T::class == ChannelMonetizationFragment::class) - return createFragment(2) as T; - else if(T::class == ChannelAboutFragment::class) - return createFragment(3) as T; - else if(T::class == ChannelPlaylistsFragment::class) - return createFragment(4) as T; - else - throw NotImplementedError("Implement other types"); + fun getTabNames(tab: TabLayout.Tab, position: Int) { + tab.text = _tabs[position].name + } + + fun insert(position: Int, tab: ChannelTab) { + _supportedFragments[tab.ordinal] = tab + _tabs.add(position, tab) + notifyItemInserted(position) + } + + fun remove(position: Int) { + _supportedFragments.remove(_tabs[position].ordinal) + _tabs.removeAt(position) + notifyItemRemoved(position) } override fun createFragment(position: Int): Fragment { - val cachedFragment = _cache[position]; - if (cachedFragment != null) { - return cachedFragment; + val fragment: Fragment + when (_tabs[position]) { + ChannelTab.VIDEOS -> { + fragment = ChannelContentsFragment.newInstance().apply { + onContentClicked.subscribe(this@ChannelViewPagerAdapter.onContentClicked::emit) + onContentUrlClicked.subscribe(this@ChannelViewPagerAdapter.onContentUrlClicked::emit) + onUrlClicked.subscribe(this@ChannelViewPagerAdapter.onUrlClicked::emit) + onChannelClicked.subscribe(this@ChannelViewPagerAdapter.onChannelClicked::emit) + onAddToClicked.subscribe(this@ChannelViewPagerAdapter.onAddToClicked::emit) + onAddToQueueClicked.subscribe(this@ChannelViewPagerAdapter.onAddToQueueClicked::emit) + onAddToWatchLaterClicked.subscribe(this@ChannelViewPagerAdapter.onAddToWatchLaterClicked::emit) + onLongPress.subscribe(this@ChannelViewPagerAdapter.onLongPress::emit) + } + } + + ChannelTab.CHANNELS -> { + fragment = ChannelListFragment.newInstance() + .apply { onClickChannel.subscribe(onChannelClicked::emit) } + } + + ChannelTab.PLAYLISTS -> { + fragment = ChannelPlaylistsFragment.newInstance().apply { + onContentClicked.subscribe(this@ChannelViewPagerAdapter.onContentClicked::emit) + onContentUrlClicked.subscribe(this@ChannelViewPagerAdapter.onContentUrlClicked::emit) + onUrlClicked.subscribe(this@ChannelViewPagerAdapter.onUrlClicked::emit) + onChannelClicked.subscribe(this@ChannelViewPagerAdapter.onChannelClicked::emit) + onAddToClicked.subscribe(this@ChannelViewPagerAdapter.onAddToClicked::emit) + onAddToQueueClicked.subscribe(this@ChannelViewPagerAdapter.onAddToQueueClicked::emit) + onAddToWatchLaterClicked.subscribe(this@ChannelViewPagerAdapter.onAddToWatchLaterClicked::emit) + onLongPress.subscribe(this@ChannelViewPagerAdapter.onLongPress::emit) + } + } + + ChannelTab.SUPPORT -> { + fragment = ChannelMonetizationFragment.newInstance() + } + + ChannelTab.ABOUT -> { + fragment = ChannelAboutFragment.newInstance() + } } + channel?.let { (fragment as IChannelTabFragment).setChannel(it) } + profile?.let { (fragment as IChannelTabFragment).setPolycentricProfile(it) } - val fragment = when (position) { - 0 -> ChannelContentsFragment.newInstance().apply { - onContentClicked.subscribe(this@ChannelViewPagerAdapter.onContentClicked::emit); - onContentUrlClicked.subscribe(this@ChannelViewPagerAdapter.onContentUrlClicked::emit); - onUrlClicked.subscribe(this@ChannelViewPagerAdapter.onUrlClicked::emit); - onChannelClicked.subscribe(this@ChannelViewPagerAdapter.onChannelClicked::emit); - onAddToClicked.subscribe(this@ChannelViewPagerAdapter.onAddToClicked::emit); - onAddToQueueClicked.subscribe(this@ChannelViewPagerAdapter.onAddToQueueClicked::emit); - onAddToWatchLaterClicked.subscribe(this@ChannelViewPagerAdapter.onAddToWatchLaterClicked::emit); - onLongPress.subscribe(this@ChannelViewPagerAdapter.onLongPress::emit); - }; - 1 -> ChannelListFragment.newInstance().apply { onClickChannel.subscribe(onChannelClicked::emit) }; - //2 -> ChannelStoreFragment.newInstance(); - 2 -> ChannelMonetizationFragment.newInstance(); - 3 -> ChannelAboutFragment.newInstance(); - 4 -> ChannelPlaylistsFragment.newInstance().apply { - onContentClicked.subscribe(this@ChannelViewPagerAdapter.onContentClicked::emit); - onContentUrlClicked.subscribe(this@ChannelViewPagerAdapter.onContentUrlClicked::emit); - onUrlClicked.subscribe(this@ChannelViewPagerAdapter.onUrlClicked::emit); - onChannelClicked.subscribe(this@ChannelViewPagerAdapter.onChannelClicked::emit); - onAddToClicked.subscribe(this@ChannelViewPagerAdapter.onAddToClicked::emit); - onAddToQueueClicked.subscribe(this@ChannelViewPagerAdapter.onAddToQueueClicked::emit); - onAddToWatchLaterClicked.subscribe(this@ChannelViewPagerAdapter.onAddToWatchLaterClicked::emit); - onLongPress.subscribe(this@ChannelViewPagerAdapter.onLongPress::emit); - }; - else -> throw IllegalStateException("Invalid tab position $position") - }; - - _cache[position]= fragment; - return fragment; + return fragment } } \ No newline at end of file