add back button when in channel shorts

hide refresh button when in channel shorts

prevent main player being open when viewing shorts

show info toast when long pressing refresh button

switch bottom bar button ids back to their original values

Changelog: changed
This commit is contained in:
Kai 2025-07-10 09:30:42 -05:00
commit cac8a8fde4
No known key found for this signature in database
6 changed files with 118 additions and 139 deletions

View file

@ -353,10 +353,6 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
_fragBrowser = BrowserFragment.newInstance(); _fragBrowser = BrowserFragment.newInstance();
_fragShorts.onShownEvent.subscribe {
_fragVideoDetail.closeVideoDetails()
};
//Overlays //Overlays
_fragVideoDetail = VideoDetailFragment.newInstance(); _fragVideoDetail = VideoDetailFragment.newInstance();
//Overlay Init //Overlay Init

View file

@ -392,13 +392,13 @@ 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<CreatorsFragment>(withHistory = false) }), ButtonDefinition(2, R.drawable.ic_creators, R.drawable.ic_creators_filled, R.string.creators, canToggle = false, { it.currentMain is CreatorsFragment }, { it.navigate<CreatorsFragment>(withHistory = false) }),
ButtonDefinition(3, R.drawable.ic_sources, R.drawable.ic_sources_filled, R.string.sources, canToggle = false, { it.currentMain is SourcesFragment }, { it.navigate<SourcesFragment>(withHistory = false) }), ButtonDefinition(3, R.drawable.ic_sources, R.drawable.ic_sources_filled, R.string.sources, canToggle = false, { it.currentMain is SourcesFragment }, { it.navigate<SourcesFragment>(withHistory = false) }),
ButtonDefinition(4, R.drawable.ic_playlist, R.drawable.ic_playlist_filled, R.string.playlists, canToggle = false, { it.currentMain is PlaylistsFragment }, { it.navigate<PlaylistsFragment>(withHistory = false) }), ButtonDefinition(4, R.drawable.ic_playlist, R.drawable.ic_playlist_filled, R.string.playlists, canToggle = false, { it.currentMain is PlaylistsFragment }, { it.navigate<PlaylistsFragment>(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<ShortsFragment>(withHistory = false) }), ButtonDefinition(11, 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<ShortsFragment>(withHistory = false) }),
ButtonDefinition(6, R.drawable.ic_history, R.drawable.ic_history, R.string.history, canToggle = false, { it.currentMain is HistoryFragment }, { it.navigate<HistoryFragment>(withHistory = false) }), ButtonDefinition(5, R.drawable.ic_history, R.drawable.ic_history, R.string.history, canToggle = false, { it.currentMain is HistoryFragment }, { it.navigate<HistoryFragment>(withHistory = false) }),
ButtonDefinition(7, R.drawable.ic_download, R.drawable.ic_download, R.string.downloads, canToggle = false, { it.currentMain is DownloadsFragment }, { it.navigate<DownloadsFragment>(withHistory = false) }), ButtonDefinition(6, R.drawable.ic_download, R.drawable.ic_download, R.string.downloads, canToggle = false, { it.currentMain is DownloadsFragment }, { it.navigate<DownloadsFragment>(withHistory = false) }),
ButtonDefinition(8, R.drawable.ic_chat, R.drawable.ic_chat_filled, R.string.comments, canToggle = true, { it.currentMain is CommentsFragment }, { it.navigate<CommentsFragment>(withHistory = false) }), ButtonDefinition(8, R.drawable.ic_chat, R.drawable.ic_chat_filled, R.string.comments, canToggle = true, { it.currentMain is CommentsFragment }, { it.navigate<CommentsFragment>(withHistory = false) }),
ButtonDefinition(9, R.drawable.ic_subscriptions, R.drawable.ic_subscriptions_filled, R.string.subscription_group_menu, canToggle = true, { it.currentMain is SubscriptionGroupListFragment }, { it.navigate<SubscriptionGroupListFragment>(withHistory = false) }), ButtonDefinition(9, R.drawable.ic_subscriptions, R.drawable.ic_subscriptions_filled, R.string.subscription_group_menu, canToggle = true, { it.currentMain is SubscriptionGroupListFragment }, { it.navigate<SubscriptionGroupListFragment>(withHistory = false) }),
ButtonDefinition(10, R.drawable.ic_help_square, R.drawable.ic_help_square_fill, R.string.tutorials, canToggle = true, { it.currentMain is TutorialFragment }, { it.navigate<TutorialFragment>(withHistory = false) }), ButtonDefinition(10, R.drawable.ic_help_square, R.drawable.ic_help_square_fill, R.string.tutorials, canToggle = true, { it.currentMain is TutorialFragment }, { it.navigate<TutorialFragment>(withHistory = false) }),
ButtonDefinition(11, R.drawable.ic_settings, R.drawable.ic_settings_filled, R.string.settings, canToggle = false, { false }, { ButtonDefinition(7, R.drawable.ic_settings, R.drawable.ic_settings_filled, R.string.settings, canToggle = false, { false }, {
val c = it.context ?: return@ButtonDefinition; val c = it.context ?: return@ButtonDefinition;
Logger.i(TAG, "settings preventPictureInPicture()"); Logger.i(TAG, "settings preventPictureInPicture()");
it.requireFragment<VideoDetailFragment>().preventPictureInPicture(); it.requireFragment<VideoDetailFragment>().preventPictureInPicture();

View file

@ -21,6 +21,7 @@ import android.widget.FrameLayout
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.graphics.drawable.toDrawable import androidx.core.graphics.drawable.toDrawable
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -122,6 +123,9 @@ class ShortView : FrameLayout {
private val videoTitle: TextView private val videoTitle: TextView
private val platformIndicator: PlatformIndicator private val platformIndicator: PlatformIndicator
private val backButton: MaterialButton
private val backButtonContainer: ConstraintLayout
private val likeContainer: FrameLayout private val likeContainer: FrameLayout
private val dislikeContainer: FrameLayout private val dislikeContainer: FrameLayout
private val likeButton: MaterialButton private val likeButton: MaterialButton
@ -132,6 +136,7 @@ class ShortView : FrameLayout {
private val commentsButton: MaterialButton private val commentsButton: MaterialButton
private val shareButton: MaterialButton private val shareButton: MaterialButton
private val refreshButton: MaterialButton private val refreshButton: MaterialButton
private val refreshButtonContainer: View
private val qualityButton: MaterialButton private val qualityButton: MaterialButton
private val playPauseOverlay: FrameLayout private val playPauseOverlay: FrameLayout
@ -180,129 +185,7 @@ class ShortView : FrameLayout {
dislikeCount.text = value.toString() dislikeCount.text = value.toString()
} }
// Required constructor for XML inflation constructor(inflater: LayoutInflater, fragment: MainFragment, overlayQualityContainer: FrameLayout) : this(inflater.context) {
constructor(context: Context) : super(context) {
inflate(context, R.layout.view_short, this)
player = findViewById(R.id.short_player)
channelInfo = findViewById(R.id.channel_info)
creatorThumbnail = findViewById(R.id.creator_thumbnail)
channelName = findViewById(R.id.channel_name)
videoTitle = findViewById(R.id.video_title)
platformIndicator = findViewById(R.id.short_platform_indicator)
likeContainer = findViewById(R.id.like_container)
dislikeContainer = findViewById(R.id.dislike_container)
likeButton = findViewById(R.id.like_button)
likeCount = findViewById(R.id.like_count)
dislikeButton = findViewById(R.id.dislike_button)
dislikeCount = findViewById(R.id.dislike_count)
commentsButton = findViewById(R.id.comments_button)
shareButton = findViewById(R.id.share_button)
refreshButton = findViewById(R.id.refresh_button)
qualityButton = findViewById(R.id.quality_button)
playPauseOverlay = findViewById(R.id.play_pause_overlay)
playPauseIcon = findViewById(R.id.play_pause_icon)
overlayLoading = findViewById(R.id.short_view_loading_overlay)
overlayLoadingSpinner = findViewById(R.id.short_view_loader)
init()
}
// Required constructor for XML inflation with attributes
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
inflate(context, R.layout.view_short, this)
player = findViewById(R.id.short_player)
channelInfo = findViewById(R.id.channel_info)
creatorThumbnail = findViewById(R.id.creator_thumbnail)
channelName = findViewById(R.id.channel_name)
videoTitle = findViewById(R.id.video_title)
platformIndicator = findViewById(R.id.short_platform_indicator)
likeContainer = findViewById(R.id.like_container)
dislikeContainer = findViewById(R.id.dislike_container)
likeButton = findViewById(R.id.like_button)
likeCount = findViewById(R.id.like_count)
dislikeButton = findViewById(R.id.dislike_button)
dislikeCount = findViewById(R.id.dislike_count)
commentsButton = findViewById(R.id.comments_button)
shareButton = findViewById(R.id.share_button)
refreshButton = findViewById(R.id.refresh_button)
qualityButton = findViewById(R.id.quality_button)
playPauseOverlay = findViewById(R.id.play_pause_overlay)
playPauseIcon = findViewById(R.id.play_pause_icon)
overlayLoading = findViewById(R.id.short_view_loading_overlay)
overlayLoadingSpinner = findViewById(R.id.short_view_loader)
init()
}
// Required constructor for XML inflation with attributes and style
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
inflate(context, R.layout.view_short, this)
player = findViewById(R.id.short_player)
channelInfo = findViewById(R.id.channel_info)
creatorThumbnail = findViewById(R.id.creator_thumbnail)
channelName = findViewById(R.id.channel_name)
videoTitle = findViewById(R.id.video_title)
platformIndicator = findViewById(R.id.short_platform_indicator)
likeContainer = findViewById(R.id.like_container)
dislikeContainer = findViewById(R.id.dislike_container)
likeButton = findViewById(R.id.like_button)
likeCount = findViewById(R.id.like_count)
dislikeButton = findViewById(R.id.dislike_button)
dislikeCount = findViewById(R.id.dislike_count)
commentsButton = findViewById(R.id.comments_button)
shareButton = findViewById(R.id.share_button)
refreshButton = findViewById(R.id.refresh_button)
qualityButton = findViewById(R.id.quality_button)
playPauseOverlay = findViewById(R.id.play_pause_overlay)
playPauseIcon = findViewById(R.id.play_pause_icon)
overlayLoading = findViewById(R.id.short_view_loading_overlay)
overlayLoadingSpinner = findViewById(R.id.short_view_loader)
init()
}
constructor(inflater: LayoutInflater, fragment: MainFragment, overlayQualityContainer: FrameLayout) : super(inflater.context) {
inflater.inflate(R.layout.view_short, this, true)
player = findViewById(R.id.short_player)
channelInfo = findViewById(R.id.channel_info)
creatorThumbnail = findViewById(R.id.creator_thumbnail)
channelName = findViewById(R.id.channel_name)
videoTitle = findViewById(R.id.video_title)
platformIndicator = findViewById(R.id.short_platform_indicator)
likeContainer = findViewById(R.id.like_container)
dislikeContainer = findViewById(R.id.dislike_container)
likeButton = findViewById(R.id.like_button)
likeCount = findViewById(R.id.like_count)
dislikeButton = findViewById(R.id.dislike_button)
dislikeCount = findViewById(R.id.dislike_count)
commentsButton = findViewById(R.id.comments_button)
shareButton = findViewById(R.id.share_button)
refreshButton = findViewById(R.id.refresh_button)
qualityButton = findViewById(R.id.quality_button)
playPauseOverlay = findViewById(R.id.play_pause_overlay)
playPauseIcon = findViewById(R.id.play_pause_icon)
overlayLoading = findViewById(R.id.short_view_loading_overlay)
overlayLoadingSpinner = findViewById(R.id.short_view_loader)
this.overlayQualityContainer = overlayQualityContainer this.overlayQualityContainer = overlayQualityContainer
layoutParams = LayoutParams( layoutParams = LayoutParams(
@ -311,11 +194,46 @@ class ShortView : FrameLayout {
this.mainFragment = fragment this.mainFragment = fragment
bottomSheet.mainFragment = fragment bottomSheet.mainFragment = fragment
init()
} }
private fun init() { // Required constructor for XML inflation
constructor(context: Context) : this(context, null, null)
// Required constructor for XML inflation with attributes
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, null)
// Required constructor for XML inflation with attributes and style
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int? = null) : super(
context, attrs, defStyleAttr ?: 0
) {
// Inflate the layout once here
inflate(context, R.layout.view_short, this)
// Initialize all val properties using findViewById
player = findViewById(R.id.short_player)
channelInfo = findViewById(R.id.channel_info)
creatorThumbnail = findViewById(R.id.creator_thumbnail)
channelName = findViewById(R.id.channel_name)
videoTitle = findViewById(R.id.video_title)
platformIndicator = findViewById(R.id.short_platform_indicator)
backButton = findViewById(R.id.back_button)
backButtonContainer = findViewById(R.id.back_button_container)
likeContainer = findViewById(R.id.like_container)
dislikeContainer = findViewById(R.id.dislike_container)
likeButton = findViewById(R.id.like_button)
likeCount = findViewById(R.id.like_count)
dislikeButton = findViewById(R.id.dislike_button)
dislikeCount = findViewById(R.id.dislike_count)
commentsButton = findViewById(R.id.comments_button)
shareButton = findViewById(R.id.share_button)
refreshButton = findViewById(R.id.refresh_button)
refreshButtonContainer = findViewById(R.id.refresh_button_container)
qualityButton = findViewById(R.id.quality_button)
playPauseOverlay = findViewById(R.id.play_pause_overlay)
playPauseIcon = findViewById(R.id.play_pause_icon)
overlayLoading = findViewById(R.id.short_view_loading_overlay)
overlayLoadingSpinner = findViewById(R.id.short_view_loader)
player.setOnClickListener { player.setOnClickListener {
if (player.activelyPlaying) { if (player.activelyPlaying) {
player.pause() player.pause()
@ -344,6 +262,11 @@ class ShortView : FrameLayout {
channelName.text = it?.author?.name channelName.text = it?.author?.name
} }
backButton.setOnClickListener {
playSoundEffect(SoundEffectConstants.CLICK)
mainFragment.closeSegment()
}
channelInfo.setOnClickListener { channelInfo.setOnClickListener {
playSoundEffect(SoundEffectConstants.CLICK) playSoundEffect(SoundEffectConstants.CLICK)
mainFragment.navigate<ChannelFragment>(video?.author) mainFragment.navigate<ChannelFragment>(video?.author)
@ -378,6 +301,11 @@ class ShortView : FrameLayout {
onResetTriggered.emit() onResetTriggered.emit()
} }
refreshButton.setOnLongClickListener {
UIDialogs.toast(context, "Reload all platform shorts pagers")
false
}
qualityButton.setOnClickListener { qualityButton.setOnClickListener {
playSoundEffect(SoundEffectConstants.CLICK) playSoundEffect(SoundEffectConstants.CLICK)
showVideoSettings() showVideoSettings()
@ -692,12 +620,23 @@ class ShortView : FrameLayout {
this.overlayQualityContainer = overlayQualityContainer this.overlayQualityContainer = overlayQualityContainer
} }
fun changeVideo(video: IPlatformVideo) { fun changeVideo(video: IPlatformVideo, isChannelShortsMode: Boolean) {
if (this.video?.url == video.url) { if (this.video?.url == video.url) {
return return
} }
this.video = video this.video = video
refreshButtonContainer.visibility = if (isChannelShortsMode) {
GONE
} else {
VISIBLE
}
backButtonContainer.visibility = if (isChannelShortsMode) {
VISIBLE
} else {
GONE
}
loadVideo(video.url) loadVideo(video.url)
} }

View file

@ -14,6 +14,7 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import com.futo.platformplayer.R import com.futo.platformplayer.R
import com.futo.platformplayer.UIDialogs import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.activities.MainActivity
import com.futo.platformplayer.api.media.models.video.IPlatformVideo import com.futo.platformplayer.api.media.models.video.IPlatformVideo
import com.futo.platformplayer.api.media.structures.IPager import com.futo.platformplayer.api.media.structures.IPager
import com.futo.platformplayer.constructs.Event0 import com.futo.platformplayer.constructs.Event0
@ -58,6 +59,7 @@ class ShortsFragment : MainFragment() {
// we just completely reset the data structure so we want to tell the adapter that // we just completely reset the data structure so we want to tell the adapter that
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
override fun onShownWithView(parameter: Any?, isBack: Boolean) { override fun onShownWithView(parameter: Any?, isBack: Boolean) {
(activity as MainActivity?)?.getFragment<VideoDetailFragment>()?.closeVideoDetails()
super.onShownWithView(parameter, isBack) super.onShownWithView(parameter, isBack)
if (parameter is Triple<*, *, *>) { if (parameter is Triple<*, *, *>) {
@ -114,7 +116,7 @@ class ShortsFragment : MainFragment() {
Logger.i(TAG, "Creating adapter") Logger.i(TAG, "Creating adapter")
val customViewAdapter = val customViewAdapter =
CustomViewAdapter(currentShorts, layoutInflater, this@ShortsFragment, overlayQualityContainer) { CustomViewAdapter(currentShorts, layoutInflater, this@ShortsFragment, overlayQualityContainer, { isChannelShortsMode }) {
if (!currentShortsPager!!.hasMorePages()) { if (!currentShortsPager!!.hasMorePages()) {
return@CustomViewAdapter return@CustomViewAdapter
} }
@ -288,6 +290,7 @@ class ShortsFragment : MainFragment() {
private val inflater: LayoutInflater, private val inflater: LayoutInflater,
private val fragment: MainFragment, private val fragment: MainFragment,
private val overlayQualityContainer: FrameLayout, private val overlayQualityContainer: FrameLayout,
private val isChannelShortsMode: () -> Boolean,
private val onNearEnd: () -> Unit, private val onNearEnd: () -> Unit,
) : RecyclerView.Adapter<CustomViewHolder>() { ) : RecyclerView.Adapter<CustomViewHolder>() {
val onResetTriggered = Event0() val onResetTriggered = Event0()
@ -297,7 +300,8 @@ class ShortsFragment : MainFragment() {
@OptIn(UnstableApi::class) @OptIn(UnstableApi::class)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
val shortView = ShortView(inflater, fragment, overlayQualityContainer) val shortView =
ShortView(inflater, fragment, overlayQualityContainer)
shortView.onResetTriggered.subscribe { shortView.onResetTriggered.subscribe {
onResetTriggered.emit() onResetTriggered.emit()
} }
@ -306,7 +310,7 @@ class ShortsFragment : MainFragment() {
@OptIn(UnstableApi::class) @OptIn(UnstableApi::class)
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) { override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
holder.shortView.changeVideo(videos[position]) holder.shortView.changeVideo(videos[position], isChannelShortsMode())
if (position == itemCount - 1) { if (position == itemCount - 1) {
onNearEnd() onNearEnd()

View file

@ -10,6 +10,45 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<!-- Back button -->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:visibility="gone"
android:id="@+id/back_button_container">
<ImageView
android:layout_width="0dp"
android:layout_height="0dp"
android:importantForAccessibility="no"
android:src="@drawable/button_shadow"
app:layout_constraintBottom_toBottomOf="@id/back_button"
app:layout_constraintEnd_toEndOf="@id/back_button"
app:layout_constraintStart_toStartOf="@id/back_button"
app:layout_constraintTop_toTopOf="@id/back_button"
app:tint="@color/black"
tools:ignore="ImageContrastCheck" />
<com.google.android.material.button.MaterialButton
android:id="@+id/back_button"
style="@style/Widget.Material3.Button.IconButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:contentDescription="@string/cd_button_back"
app:backgroundTint="@color/transparent"
app:icon="@drawable/ic_back_nav"
app:iconSize="24dp"
app:iconTint="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:rippleColor="@color/ripple" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- Title section --> <!-- Title section -->
<LinearLayout <LinearLayout
android:id="@+id/title_section" android:id="@+id/title_section"
@ -331,6 +370,7 @@
<!-- Refresh button --> <!-- Refresh button -->
<FrameLayout <FrameLayout
android:id="@+id/refresh_button_container"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"

@ -1 +1 @@
Subproject commit d11543001150f96f3383d83fec3341d9321746b8 Subproject commit 850eb8122dd8348904d55ceb9c3a26b49bcb8a45