Merge pull request #4981 from Bnyro/landscape-player-page

feat: landscape layout for player page
This commit is contained in:
Bnyro 2023-10-23 17:52:45 +02:00 committed by GitHub
commit fc9e3e6501
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 451 additions and 102 deletions

View File

@ -1,15 +1,14 @@
package com.github.libretube.helpers
import android.app.Activity
import android.os.Build
import android.view.Window
import android.view.WindowManager
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import com.github.libretube.ui.extensions.toggleSystemBars
object WindowHelper {
fun toggleFullscreen(activity: Activity, isFullscreen: Boolean) {
val window = activity.window
fun toggleFullscreen(window: Window, isFullscreen: Boolean) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
window.attributes.layoutInDisplayCutoutMode = if (isFullscreen) {
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
@ -30,7 +29,7 @@ object WindowHelper {
// Show the system bars when it is not fullscreen and hide them when it is fullscreen
// System bars means status bar and the navigation bar
// See: https://developer.android.com/training/system-ui/immersive#kotlin
activity.toggleSystemBars(
window.toggleSystemBars(
types = WindowInsetsCompat.Type.systemBars(),
showBars = !isFullscreen
)

View File

@ -486,8 +486,8 @@ class MainActivity : BaseActivity() {
super.onConfigurationChanged(newConfig)
when (newConfig.orientation) {
Configuration.ORIENTATION_PORTRAIT -> WindowHelper.toggleFullscreen(this, false)
Configuration.ORIENTATION_LANDSCAPE -> WindowHelper.toggleFullscreen(this, true)
Configuration.ORIENTATION_PORTRAIT -> WindowHelper.toggleFullscreen(window, false)
Configuration.ORIENTATION_LANDSCAPE -> WindowHelper.toggleFullscreen(window, true)
}
}

View File

@ -56,7 +56,7 @@ class OfflinePlayerActivity : BaseActivity() {
private val playerViewModel: PlayerViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
WindowHelper.toggleFullscreen(this, true)
WindowHelper.toggleFullscreen(window, true)
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE

View File

@ -13,7 +13,7 @@ import com.github.libretube.helpers.ThemeHelper
* Activity that applies the LibreTube theme and the in-app language
*/
open class BaseActivity : AppCompatActivity() {
private val screenOrientationPref by lazy {
val screenOrientationPref by lazy {
val orientationPref = PreferenceHelper.getString(
PreferenceKeys.ORIENTATION,
resources.getString(R.string.config_default_orientation_pref)

View File

@ -1,12 +1,12 @@
package com.github.libretube.ui.extensions
import android.app.Activity
import android.view.Window
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat.Type.InsetsType
import androidx.core.view.WindowInsetsControllerCompat
fun Activity.toggleSystemBars(@InsetsType types: Int, showBars: Boolean) {
WindowCompat.getInsetsController(window, window.decorView).apply {
fun Window.toggleSystemBars(@InsetsType types: Int, showBars: Boolean) {
WindowCompat.getInsetsController(this, decorView).apply {
systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
if (showBars) {
show(types)

View File

@ -1,6 +1,7 @@
package com.github.libretube.ui.fragments
import android.annotation.SuppressLint
import android.app.Dialog
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
@ -17,7 +18,9 @@ import android.text.format.DateUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams
import android.widget.Toast
import android.window.OnBackInvokedDispatcher
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.constraintlayout.motion.widget.TransitionAdapter
import androidx.core.content.getSystemService
@ -85,12 +88,14 @@ import com.github.libretube.helpers.PlayerHelper.getVideoStats
import com.github.libretube.helpers.PlayerHelper.isInSegment
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.helpers.ProxyHelper
import com.github.libretube.helpers.WindowHelper
import com.github.libretube.obj.PlayerNotificationData
import com.github.libretube.obj.ShareData
import com.github.libretube.obj.VideoResolution
import com.github.libretube.parcelable.PlayerData
import com.github.libretube.ui.activities.MainActivity
import com.github.libretube.ui.adapters.VideosAdapter
import com.github.libretube.ui.base.BaseActivity
import com.github.libretube.ui.dialogs.AddToPlaylistDialog
import com.github.libretube.ui.dialogs.DownloadDialog
import com.github.libretube.ui.dialogs.ShareDialog
@ -188,6 +193,21 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
private var scrubbingTimeBar = false
private var chaptersBottomSheet: ChaptersBottomSheet? = null
/**
* The orientation of the `fragment_player.xml` that's currently used
* This is needed in order to figure out if the current layout is the landscape one or not.
*/
private var playerLayoutOrientation = Int.MIN_VALUE
private val fullscreenDialog by lazy {
object: Dialog(requireContext(), android.R.style.Theme_Black_NoTitleBar_Fullscreen) {
override fun onBackPressed() {
super.onBackPressed()
unsetFullscreen()
}
}
}
/**
* Receiver for all actions in the PiP mode
*/
@ -236,6 +256,8 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
keepQueue = playerData.keepQueue
timeStamp = playerData.timestamp
playerLayoutOrientation = resources.configuration.orientation
// broadcast receiver for PiP actions
context?.registerReceiver(
broadcastReceiver,
@ -365,17 +387,19 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
.isPictureInPictureAvailable(activity)
}
// actions that don't depend on video information
private fun initializeOnClickActions() {
binding.closeImageView.setOnClickListener {
private fun onManualPlayerClose() {
PlayingQueue.clear()
BackgroundHelper.stopBackgroundPlay(requireContext())
killPlayerFragment()
}
// actions that don't depend on video information
private fun initializeOnClickActions() {
binding.closeImageView.setOnClickListener {
onManualPlayerClose()
}
playerBinding.closeImageButton.setOnClickListener {
PlayingQueue.clear()
BackgroundHelper.stopBackgroundPlay(requireContext())
killPlayerFragment()
onManualPlayerClose()
}
playerBinding.autoPlay.isVisible = true
@ -499,50 +523,30 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
NavigationHelper.startAudioPlayer(requireContext())
}
/**
* If enabled, determine the orientation o use based on the video's aspect ratio
* Expected behavior: Portrait for shorts, Landscape for normal videos
*/
private fun updateFullscreenOrientation() {
if (!PlayerHelper.autoFullscreenEnabled) {
val height = streams.videoStreams.firstOrNull()?.height ?: exoPlayer.videoSize.height
val width = streams.videoStreams.firstOrNull()?.width ?: exoPlayer.videoSize.width
// different orientations of the video are only available when autorotation is disabled
val orientation = PlayerHelper.getOrientation(width, height)
mainActivity.requestedOrientation = orientation
}
}
private fun setFullscreen() {
with(binding.playerMotionLayout) {
getConstraintSet(R.id.start).constrainHeight(R.id.player, -1)
enableTransition(R.id.yt_transition, false)
}
// set status bar icon color to white
windowInsetsControllerCompat.isAppearanceLightStatusBars = false
binding.mainContainer.isClickable = true
binding.linLayout.isGone = true
viewModel.isFullscreen.value = true
if (mainActivity.screenOrientationPref == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT) {
val height = streams.videoStreams.firstOrNull()?.height ?: exoPlayer.videoSize.height
val width = streams.videoStreams.firstOrNull()?.width ?: exoPlayer.videoSize.width
mainActivity.requestedOrientation = PlayerHelper.getOrientation(width, height)
}
commentsViewModel.setCommentSheetExpand(null)
playerBinding.fullscreen.setImageResource(R.drawable.ic_fullscreen_exit)
playerBinding.exoTitle.isVisible = true
updateFullscreenOrientation()
viewModel.isFullscreen.value = true
updateResolutionOnFullscreenChange(true)
openOrCloseFullscreenDialog(true)
}
@SuppressLint("SourceLockedOrientationActivity")
fun unsetFullscreen() {
// leave fullscreen mode
with(binding.playerMotionLayout) {
getConstraintSet(R.id.start).constrainHeight(R.id.player, 0)
enableTransition(R.id.yt_transition, true)
}
// set status bar icon color back to theme color
windowInsetsControllerCompat.isAppearanceLightStatusBars =
when (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
@ -551,19 +555,40 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
else -> true
}
binding.mainContainer.isClickable = false
binding.linLayout.isVisible = true
playerBinding.fullscreen.setImageResource(R.drawable.ic_fullscreen)
playerBinding.exoTitle.isInvisible = true
viewModel.isFullscreen.value = false
if (!PlayerHelper.autoFullscreenEnabled) {
// switch back to portrait mode if autorotation disabled
if (mainActivity.screenOrientationPref == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT) {
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
}
viewModel.isFullscreen.value = false
playerBinding.fullscreen.setImageResource(R.drawable.ic_fullscreen)
playerBinding.exoTitle.isInvisible = true
updateResolutionOnFullscreenChange(false)
openOrCloseFullscreenDialog(false)
checkForNecessaryOrientationRestart()
}
private fun openOrCloseFullscreenDialog(open: Boolean) {
val playerView = binding.player
(playerView.parent as ViewGroup).removeView(playerView)
if (open) {
fullscreenDialog.addContentView(
binding.player,
LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
)
fullscreenDialog.show()
playerView.currentWindow = fullscreenDialog.window
} else {
binding.playerMotionLayout.addView(playerView)
playerView.currentWindow = null
fullscreenDialog.dismiss()
}
WindowHelper.toggleFullscreen(fullscreenDialog.window!!, open)
}
override fun onPause() {
@ -735,8 +760,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
initializePlayerView()
setupSeekbarPreview()
if (viewModel.isFullscreen.value == true) updateFullscreenOrientation()
exoPlayer.playWhenReady = PlayerHelper.playAutomatically
exoPlayer.prepare()
@ -1341,7 +1364,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
onConfigurationChanged(resources.configuration)
} else {
// go to portrait mode
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
mainActivity.requestedOrientation = (requireActivity() as BaseActivity).screenOrientationPref
}
}
@ -1470,13 +1493,9 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
viewModel.isMiniPlayerVisible.value = false
}
with(binding.playerMotionLayout) {
getConstraintSet(R.id.start).constrainHeight(R.id.player, -1)
enableTransition(R.id.yt_transition, false)
}
binding.linLayout.isGone = true
updateCurrentSubtitle(null)
openOrCloseFullscreenDialog(true)
} else {
// close button got clicked in PiP mode
// pause the video and keep the app alive
@ -1485,20 +1504,13 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
// enable exoPlayer controls again
binding.player.useController = true
// set back to portrait mode
if (viewModel.isFullscreen.value != true) {
with(binding.playerMotionLayout) {
getConstraintSet(R.id.start).constrainHeight(R.id.player, 0)
enableTransition(R.id.yt_transition, true)
}
binding.linLayout.isVisible = true
}
updateCurrentSubtitle(currentSubtitle)
binding.optionsLL.post {
binding.optionsLL.requestLayout()
}
openOrCloseFullscreenDialog(false)
}
}
@ -1564,6 +1576,12 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
private fun killPlayerFragment() {
viewModel.isFullscreen.value = false
viewModel.isMiniPlayerVisible.value = false
// dismiss the fullscreen dialog if it's currently visible
// otherwise it would stay alive while being detached from this fragment
fullscreenDialog.dismiss()
binding.player.currentWindow = null
binding.playerMotionLayout.transitionToEnd()
mainActivity.supportFragmentManager.commit {
remove(this@PlayerFragment)
@ -1572,21 +1590,43 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
onDestroy()
}
/**
* Check if the activity needs to be recreated due to an orientation change
* If true, the activity will be automatically restarted
*/
private fun checkForNecessaryOrientationRestart() {
val lockedOrientations = listOf(ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
if (mainActivity.screenOrientationPref in lockedOrientations) return
val orientation = resources.configuration.orientation
if (viewModel.isFullscreen.value != true && orientation != playerLayoutOrientation) {
if (this::exoPlayer.isInitialized) {
arguments?.putLong(IntentData.timeStamp, exoPlayer.currentPosition / 1000)
}
playerLayoutOrientation = orientation
activity?.recreate()
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
if (!PlayerHelper.autoFullscreenEnabled || _binding == null ||
if (_binding == null ||
// If in PiP mode, orientation is given as landscape.
PictureInPictureCompat.isInPictureInPictureMode(requireActivity())
) {
return
}
if (PlayerHelper.autoFullscreenEnabled) {
when (newConfig.orientation) {
// go to fullscreen mode
Configuration.ORIENTATION_LANDSCAPE -> setFullscreen()
// exit fullscreen if not landscape
else -> unsetFullscreen()
}
} else {
checkForNecessaryOrientationRestart()
}
}
}

View File

@ -39,19 +39,6 @@ class CommentsSheet : UndimmedBottomSheet() {
val binding = binding
binding.dragHandle.viewTreeObserver.addOnGlobalLayoutListener(object :
ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
binding.dragHandle.viewTreeObserver.removeOnGlobalLayoutListener(this)
// limit the recyclerview height to not cover the video
binding.standardBottomSheet.layoutParams =
binding.commentFragContainer.layoutParams.apply {
height = playerViewModel.maxSheetHeightPx
}
}
})
binding.btnBack.setOnClickListener {
if (childFragmentManager.backStackEntryCount > 0) {
childFragmentManager.popBackStack()

View File

@ -1,6 +1,7 @@
package com.github.libretube.ui.sheets
import android.app.Dialog
import android.content.res.Configuration
import android.os.Bundle
import android.view.KeyEvent
import android.view.View
@ -21,11 +22,13 @@ abstract class UndimmedBottomSheet : ExpandedBottomSheet() {
override fun onGlobalLayout() {
getDragHandle().viewTreeObserver.removeOnGlobalLayoutListener(this)
if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
// limit the recyclerview height to not cover the video
getBottomSheet().updateLayoutParams {
height = getSheetMaxHeightPx()
}
}
}
})
}

View File

@ -11,6 +11,7 @@ import android.text.format.DateUtils
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.Window
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
@ -87,6 +88,12 @@ open class CustomExoPlayerView(
updateCurrentPosition()
}
/**
* The window that needs to be addressed for showing and hiding the system bars
* If null, the activity's default/main window will be used
*/
var currentWindow: Window? = null
/**
* Preferences
*/
@ -143,7 +150,7 @@ open class CustomExoPlayerView(
// change locked status
isPlayerLocked = !isPlayerLocked
activity.toggleSystemBars(
(currentWindow ?: activity.window).toggleSystemBars(
types = WindowInsetsCompat.Type.statusBars(),
showBars = !isPlayerLocked
)

View File

@ -13,13 +13,13 @@ class OfflinePlayerView(
override fun hideController() {
super.hideController()
// hide the status bars when continuing to watch video
activity.toggleSystemBars(WindowInsetsCompat.Type.systemBars(), false)
activity.window.toggleSystemBars(WindowInsetsCompat.Type.systemBars(), false)
}
override fun showController() {
super.showController()
// show status bar when showing player options
activity.toggleSystemBars(WindowInsetsCompat.Type.statusBars(), true)
activity.window.toggleSystemBars(WindowInsetsCompat.Type.statusBars(), true)
}
override fun getTopBarMarginDp(): Int {

View File

@ -145,7 +145,7 @@ class OnlinePlayerView(
this.playerOptions = playerOptions
playerViewModel.isFullscreen.observe(viewLifecycleOwner) { isFullscreen ->
WindowHelper.toggleFullscreen(activity, isFullscreen)
WindowHelper.toggleFullscreen(activity.window, isFullscreen)
updateTopBarMargin()
}
@ -154,7 +154,7 @@ class OnlinePlayerView(
playerViewModel.isFullscreen.value?.let { isFullscreen ->
if (!isFullscreen) return@let
// Show status bar only not navigation bar if the player controls are visible and hide it otherwise
activity.toggleSystemBars(
activity.window.toggleSystemBars(
types = WindowInsetsCompat.Type.statusBars(),
showBars = visibility == View.VISIBLE && !isPlayerLocked
)
@ -189,7 +189,7 @@ class OnlinePlayerView(
super.hideController()
if (playerViewModel?.isFullscreen?.value == true) {
WindowHelper.toggleFullscreen(activity, true)
WindowHelper.toggleFullscreen(activity.window, true)
}
updateTopBarMargin()
}

View File

@ -0,0 +1,312 @@
<?xml version="1.0" encoding="utf-8"?>
<com.github.libretube.ui.views.SingleViewTouchableMotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/playerMotionLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="@xml/player_scene">
<ScrollView
android:id="@+id/player_scrollView"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="?android:attr/colorBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/related_container"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/main_container">
<LinearLayout
android:id="@+id/linLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.github.libretube.ui.views.DescriptionLayout
android:id="@+id/descriptionLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true" />
<com.google.android.material.card.MaterialCardView
style="@style/Widget.Material3.CardView.Elevated"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:layout_marginVertical="17dp"
app:cardCornerRadius="27dp">
<LinearLayout
android:id="@+id/optionsLL"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginHorizontal="8dp"
android:layout_marginVertical="18dp"
android:gravity="center"
android:orientation="horizontal">
<com.github.libretube.ui.views.DrawableTextView
android:id="@+id/relPlayer_share"
style="@style/PlayerActionsText"
android:text="@string/share"
app:drawableTopCompat="@drawable/ic_share" />
<com.github.libretube.ui.views.DrawableTextView
android:id="@+id/relPlayer_download"
style="@style/PlayerActionsText"
android:text="@string/download"
app:drawableTopCompat="@drawable/ic_download" />
<com.github.libretube.ui.views.DrawableTextView
android:id="@+id/relPlayer_pip"
style="@style/PlayerActionsText"
android:text="@string/pop_up"
app:drawableTopCompat="@drawable/ic_open" />
<com.github.libretube.ui.views.DrawableTextView
android:id="@+id/relPlayer_background"
style="@style/PlayerActionsText"
android:text="@string/audio"
app:drawableTopCompat="@drawable/ic_headphones" />
<com.github.libretube.ui.views.DrawableTextView
android:id="@+id/relPlayer_save"
style="@style/PlayerActionsText"
android:text="@string/save"
app:drawableTopCompat="@drawable/ic_save" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<LinearLayout
android:id="@+id/player_channel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
android:layout_marginVertical="10dp"
android:background="@drawable/rounded_ripple"
android:orientation="horizontal"
android:padding="5dp">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/player_channelImage"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:layout_marginEnd="4dp"
app:shapeAppearance="@style/CircleImageView" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:layout_marginStart="5dp"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:layoutDirection="locale"
android:orientation="vertical">
<TextView
android:id="@+id/player_channelName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textSize="15sp" />
<TextView
android:id="@+id/player_channelSubCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textSize="11sp"
tools:text="2.5M subscribers" />
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/player_subscribe"
style="@style/Widget.Material3.Button.ElevatedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/subscribe"
android:textColor="?colorControlNormal"
android:textSize="12sp"
app:cornerRadius="16dp" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/alternativeTrendingRec"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"/>
<com.google.android.material.card.MaterialCardView
android:id="@+id/comments_toggle"
style="@style/Widget.Material3.CardView.Elevated"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="12dp"
android:layout_marginVertical="16dp"
app:cardCornerRadius="18dp">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:paddingHorizontal="8dp"
android:text="@string/comments"
android:textAlignment="viewStart"
app:drawableEndCompat="@drawable/ic_arrow_up_down" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</ScrollView>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/main_container"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="?attr/colorSurface"
app:layout_constraintDimensionRatio="16:9"
app:layout_constraintWidth_percent=".55"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="@id/related_container"
app:layout_constraintWidth_default="percent" />
<RelativeLayout
android:id="@+id/related_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:descendantFocusability="blocksDescendants"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/main_container"
android:background="?android:windowBackground">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/related_rec_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="10dp"
android:layout_marginBottom="10dp"
android:nestedScrollingEnabled="false" />
</RelativeLayout>
<com.github.libretube.ui.views.OnlinePlayerView
android:id="@+id/player"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@android:color/black"
app:controller_layout_id="@layout/exo_styled_player_control_view"
app:layout_constraintBottom_toBottomOf="@id/main_container"
app:layout_constraintStart_toStartOf="@id/main_container"
app:layout_constraintTop_toTopOf="@id/main_container"
app:layout_constraintEnd_toEndOf="@id/main_container"
app:show_buffering="when_playing">
<com.github.libretube.ui.views.DoubleTapOverlay
android:id="@+id/doubleTapOverlay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center" />
<com.github.libretube.ui.views.PlayerGestureControlsView
android:id="@+id/playerGestureControlsView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center" />
<com.google.android.material.card.MaterialCardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center|end"
android:layout_marginTop="15dp"
android:layout_marginEnd="-10dp"
android:paddingEnd="20dp"
app:cardBackgroundColor="#88000000"
app:strokeWidth="1dp"
tools:ignore="RtlSymmetry">
<com.github.libretube.ui.views.DrawableTextView
android:id="@+id/sb_skip_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="20dp"
android:padding="10dp"
android:text="@string/skip_segment"
android:textColor="@android:color/white"
android:textSize="18sp"
android:visibility="gone"
app:drawableEndCompat="@drawable/ic_next"
app:drawableEndDimen="20dp"
app:drawableTint="@android:color/white" />
</com.google.android.material.card.MaterialCardView>
<com.github.libretube.ui.views.AutoplayCountdownView
android:id="@+id/autoplay_countdown"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
</com.github.libretube.ui.views.OnlinePlayerView>
<ImageView
android:id="@+id/close_imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0"
android:background="?attr/selectableItemBackgroundBorderless"
android:padding="8dp"
android:src="@drawable/ic_close"
android:tooltipText="@string/tooltip_close"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/main_container"
app:layout_constraintEnd_toEndOf="@id/main_container"
app:layout_constraintTop_toTopOf="@id/main_container" />
<ImageView
android:id="@+id/play_imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0"
android:background="?attr/selectableItemBackgroundBorderless"
android:padding="8dp"
android:src="@drawable/ic_play"
android:tooltipText="@string/tooltip_play"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/close_imageView"
app:layout_constraintEnd_toStartOf="@+id/close_imageView"
app:layout_constraintTop_toTopOf="@+id/close_imageView" />
<TextView
android:id="@+id/title_textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:alpha="0"
android:ellipsize="end"
android:maxLines="1"
android:paddingHorizontal="8dp"
android:paddingVertical="15dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/play_imageView"
app:layout_constraintEnd_toStartOf="@+id/play_imageView"
app:layout_constraintStart_toEndOf="@+id/player"
app:layout_constraintTop_toTopOf="@+id/play_imageView" />
</com.github.libretube.ui.views.SingleViewTouchableMotionLayout>

View File

@ -33,9 +33,10 @@
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/player"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="0dp"
motion:layout_constraintBottom_toBottomOf="@id/main_container"
motion:layout_constraintEnd_toEndOf="@id/main_container"
motion:layout_constraintStart_toStartOf="@id/main_container"
motion:layout_constraintTop_toTopOf="@id/main_container" />
<Constraint android:id="@+id/doubleTapOverlay" />