mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-01-07 18:10:31 +05:30
Merge pull request #3335 from Bnyro/audio-mini-player
Audio mini player
This commit is contained in:
commit
a5f44c4e25
@ -18,6 +18,7 @@ import com.github.libretube.constants.PreferenceKeys
|
|||||||
import com.github.libretube.enums.PlaylistType
|
import com.github.libretube.enums.PlaylistType
|
||||||
import com.github.libretube.extensions.toID
|
import com.github.libretube.extensions.toID
|
||||||
import com.github.libretube.ui.activities.MainActivity
|
import com.github.libretube.ui.activities.MainActivity
|
||||||
|
import com.github.libretube.ui.fragments.AudioPlayerFragment
|
||||||
import com.github.libretube.ui.fragments.PlayerFragment
|
import com.github.libretube.ui.fragments.PlayerFragment
|
||||||
import com.github.libretube.ui.views.SingleViewTouchableMotionLayout
|
import com.github.libretube.ui.views.SingleViewTouchableMotionLayout
|
||||||
|
|
||||||
@ -117,7 +118,9 @@ object NavigationHelper {
|
|||||||
*/
|
*/
|
||||||
fun startAudioPlayer(context: Context) {
|
fun startAudioPlayer(context: Context) {
|
||||||
val activity = unwrap(context)
|
val activity = unwrap(context)
|
||||||
activity.navController.navigate(R.id.audioPlayerFragment)
|
activity.supportFragmentManager.commitNow {
|
||||||
|
replace<AudioPlayerFragment>(R.id.container)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -362,7 +362,7 @@ class MainActivity : BaseActivity() {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.action_audio -> {
|
R.id.action_audio -> {
|
||||||
navController.navigate(R.id.audioPlayerFragment)
|
NavigationHelper.startAudioPlayer(this)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
|
@ -13,7 +13,10 @@ import android.text.format.DateUtils
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.fragment.app.commit
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.api.obj.StreamItem
|
import com.github.libretube.api.obj.StreamItem
|
||||||
@ -31,14 +34,22 @@ import com.github.libretube.ui.activities.MainActivity
|
|||||||
import com.github.libretube.ui.dialogs.ShareDialog
|
import com.github.libretube.ui.dialogs.ShareDialog
|
||||||
import com.github.libretube.ui.interfaces.AudioPlayerOptions
|
import com.github.libretube.ui.interfaces.AudioPlayerOptions
|
||||||
import com.github.libretube.ui.listeners.AudioPlayerThumbnailListener
|
import com.github.libretube.ui.listeners.AudioPlayerThumbnailListener
|
||||||
|
import com.github.libretube.ui.models.PlayerViewModel
|
||||||
import com.github.libretube.ui.sheets.PlaybackOptionsSheet
|
import com.github.libretube.ui.sheets.PlaybackOptionsSheet
|
||||||
import com.github.libretube.ui.sheets.PlayingQueueSheet
|
import com.github.libretube.ui.sheets.PlayingQueueSheet
|
||||||
import com.github.libretube.ui.sheets.VideoOptionsBottomSheet
|
import com.github.libretube.ui.sheets.VideoOptionsBottomSheet
|
||||||
import com.github.libretube.util.PlayingQueue
|
import com.github.libretube.util.PlayingQueue
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
||||||
private lateinit var binding: FragmentAudioPlayerBinding
|
private lateinit var binding: FragmentAudioPlayerBinding
|
||||||
private lateinit var audioHelper: AudioHelper
|
private lateinit var audioHelper: AudioHelper
|
||||||
|
private val mainActivity get() = context as MainActivity
|
||||||
|
private val viewModel: PlayerViewModel by activityViewModels()
|
||||||
|
|
||||||
|
// for the transition
|
||||||
|
private var sId: Int = 0
|
||||||
|
private var eId: Int = 0
|
||||||
|
|
||||||
private val onTrackChangeListener: (StreamItem) -> Unit = {
|
private val onTrackChangeListener: (StreamItem) -> Unit = {
|
||||||
updateStreamInfo()
|
updateStreamInfo()
|
||||||
@ -57,16 +68,7 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
|||||||
handleServiceConnection()
|
handleServiceConnection()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onServiceDisconnected(arg0: ComponentName) {
|
override fun onServiceDisconnected(arg0: ComponentName) {}
|
||||||
val mainActivity = activity as MainActivity
|
|
||||||
if (mainActivity.navController.currentDestination?.id == R.id.audioPlayerFragment) {
|
|
||||||
mainActivity.navController.popBackStack()
|
|
||||||
} else {
|
|
||||||
mainActivity.navController.backQueue.removeIf {
|
|
||||||
it.destination.id == R.id.audioPlayerFragment
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@ -91,6 +93,8 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
initializeTransitionLayout()
|
||||||
|
|
||||||
// select the title TV in order for it to automatically scroll
|
// select the title TV in order for it to automatically scroll
|
||||||
binding.title.isSelected = true
|
binding.title.isSelected = true
|
||||||
binding.uploader.isSelected = true
|
binding.uploader.isSelected = true
|
||||||
@ -142,7 +146,13 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
|||||||
binding.close.setOnClickListener {
|
binding.close.setOnClickListener {
|
||||||
activity?.unbindService(connection)
|
activity?.unbindService(connection)
|
||||||
BackgroundHelper.stopBackgroundPlay(requireContext())
|
BackgroundHelper.stopBackgroundPlay(requireContext())
|
||||||
findNavController().popBackStack()
|
killFragment()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.miniPlayerClose.setOnClickListener {
|
||||||
|
activity?.unbindService(connection)
|
||||||
|
BackgroundHelper.stopBackgroundPlay(requireContext())
|
||||||
|
killFragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
val listener = AudioPlayerThumbnailListener(requireContext(), this)
|
val listener = AudioPlayerThumbnailListener(requireContext(), this)
|
||||||
@ -155,6 +165,10 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
|||||||
if (isPaused) playerService?.play() else playerService?.pause()
|
if (isPaused) playerService?.play() else playerService?.pause()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding.miniPlayerPause.setOnClickListener {
|
||||||
|
if (isPaused) playerService?.play() else playerService?.pause()
|
||||||
|
}
|
||||||
|
|
||||||
// load the stream info into the UI
|
// load the stream info into the UI
|
||||||
updateStreamInfo()
|
updateStreamInfo()
|
||||||
|
|
||||||
@ -164,6 +178,63 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun killFragment() {
|
||||||
|
viewModel.isFullscreen.value = false
|
||||||
|
binding.playerMotionLayout.transitionToEnd()
|
||||||
|
mainActivity.supportFragmentManager.commit {
|
||||||
|
remove(this@AudioPlayerFragment)
|
||||||
|
}
|
||||||
|
|
||||||
|
onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
private fun initializeTransitionLayout() {
|
||||||
|
mainActivity.binding.container.visibility = View.VISIBLE
|
||||||
|
val mainMotionLayout = mainActivity.binding.mainMotionLayout
|
||||||
|
|
||||||
|
binding.playerMotionLayout.addTransitionListener(object : MotionLayout.TransitionListener {
|
||||||
|
override fun onTransitionStarted(
|
||||||
|
motionLayout: MotionLayout?,
|
||||||
|
startId: Int,
|
||||||
|
endId: Int
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTransitionChange(
|
||||||
|
motionLayout: MotionLayout?,
|
||||||
|
startId: Int,
|
||||||
|
endId: Int,
|
||||||
|
progress: Float
|
||||||
|
) {
|
||||||
|
mainMotionLayout.progress = abs(progress)
|
||||||
|
eId = endId
|
||||||
|
sId = startId
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
|
||||||
|
if (currentId == eId) {
|
||||||
|
viewModel.isMiniPlayerVisible.value = true
|
||||||
|
mainMotionLayout.progress = 1F
|
||||||
|
} else if (currentId == sId) {
|
||||||
|
viewModel.isMiniPlayerVisible.value = false
|
||||||
|
mainMotionLayout.progress = 0F
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTransitionTrigger(
|
||||||
|
MotionLayout: MotionLayout?,
|
||||||
|
triggerId: Int,
|
||||||
|
positive: Boolean,
|
||||||
|
progress: Float
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
binding.playerMotionLayout.progress = 1.toFloat()
|
||||||
|
binding.playerMotionLayout.transitionToStart()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the information from a new stream into the UI
|
* Load the information from a new stream into the UI
|
||||||
*/
|
*/
|
||||||
@ -172,6 +243,8 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
|||||||
current ?: return
|
current ?: return
|
||||||
|
|
||||||
binding.title.text = current.title
|
binding.title.text = current.title
|
||||||
|
binding.miniPlayerTitle.text = current.title
|
||||||
|
|
||||||
binding.uploader.text = current.uploaderName
|
binding.uploader.text = current.uploaderName
|
||||||
binding.uploader.setOnClickListener {
|
binding.uploader.setOnClickListener {
|
||||||
NavigationHelper.navigateChannel(requireContext(), current.uploaderUrl?.toID())
|
NavigationHelper.navigateChannel(requireContext(), current.uploaderUrl?.toID())
|
||||||
@ -188,6 +261,7 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
|||||||
|
|
||||||
ImageHelper.getAsync(requireContext(), thumbnailUrl) {
|
ImageHelper.getAsync(requireContext(), thumbnailUrl) {
|
||||||
binding.thumbnail.setImageBitmap(it)
|
binding.thumbnail.setImageBitmap(it)
|
||||||
|
binding.miniPlayerThumbnail.setImageBitmap(it)
|
||||||
binding.thumbnail.visibility = View.VISIBLE
|
binding.thumbnail.visibility = View.VISIBLE
|
||||||
binding.progress.visibility = View.GONE
|
binding.progress.visibility = View.GONE
|
||||||
}
|
}
|
||||||
@ -232,9 +306,9 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
|||||||
|
|
||||||
private fun handleServiceConnection() {
|
private fun handleServiceConnection() {
|
||||||
playerService?.onIsPlayingChanged = { isPlaying ->
|
playerService?.onIsPlayingChanged = { isPlaying ->
|
||||||
binding.playPause.setIconResource(
|
val iconResource = if (isPlaying) R.drawable.ic_pause else R.drawable.ic_play
|
||||||
if (isPlaying) R.drawable.ic_pause else R.drawable.ic_play
|
binding.playPause.setIconResource(iconResource)
|
||||||
)
|
binding.miniPlayerPause.setImageResource(iconResource)
|
||||||
isPaused = !isPlaying
|
isPaused = !isPlaying
|
||||||
}
|
}
|
||||||
initializeSeekBar()
|
initializeSeekBar()
|
||||||
|
@ -14,7 +14,7 @@ class SingleViewTouchableMotionLayout(context: Context, attributeSet: AttributeS
|
|||||||
MotionLayout(context, attributeSet) {
|
MotionLayout(context, attributeSet) {
|
||||||
|
|
||||||
private val viewToDetectTouch by lazy {
|
private val viewToDetectTouch by lazy {
|
||||||
findViewById<View>(R.id.main_container) // TODO move to Attributes
|
findViewById<View>(R.id.main_container) ?: findViewById(R.id.audio_player_container)
|
||||||
}
|
}
|
||||||
private val viewRect = Rect()
|
private val viewRect = Rect()
|
||||||
private var touchStarted = false
|
private var touchStarted = false
|
||||||
|
@ -1,204 +1,275 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<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:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/playerMotionLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
app:layoutDescription="@xml/audio_player_scene">
|
||||||
|
|
||||||
<FrameLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/audio_player_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="1">
|
android:background="?android:attr/colorBackground"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<com.google.android.material.imageview.ShapeableImageView
|
<LinearLayout
|
||||||
android:id="@+id/thumbnail"
|
android:id="@+id/audio_player_main"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/audio_player_container"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/audio_player_container"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/audio_player_container"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/audio_player_container">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="0dp"
|
||||||
android:layout_gravity="center"
|
android:layout_weight="1">
|
||||||
android:layout_marginHorizontal="20dp"
|
|
||||||
android:layout_marginVertical="10dp"
|
|
||||||
android:adjustViewBounds="true"
|
|
||||||
android:scaleType="fitCenter"
|
|
||||||
app:shapeAppearanceOverlay="@style/ShapeAppearance.Material3.Corner.Small"
|
|
||||||
tools:src="@tools:sample/backgrounds/scenic" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
android:id="@+id/volumeControls"
|
android:id="@+id/thumbnail"
|
||||||
android:layout_width="42dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
|
||||||
android:background="@drawable/controls_layout_bg"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:ignore="RtlHardcoded">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/volume_textView"
|
|
||||||
style="@style/SwipeControlString"
|
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
app:autoSizeTextType="uniform" />
|
android:layout_marginHorizontal="20dp"
|
||||||
|
android:layout_marginVertical="10dp"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
app:shapeAppearanceOverlay="@style/ShapeAppearance.Material3.Corner.Small"
|
||||||
|
tools:src="@tools:sample/backgrounds/scenic" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/volumeControls"
|
||||||
|
android:layout_width="42dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:background="@drawable/controls_layout_bg"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:ignore="RtlHardcoded">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/volume_textView"
|
||||||
|
style="@style/SwipeControlString"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
app:autoSizeTextType="uniform" />
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/volume_progressBar"
|
||||||
|
style="@android:style/Widget.ProgressBar.Horizontal"
|
||||||
|
android:layout_width="7dp"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginVertical="4dp"
|
||||||
|
android:progressDrawable="@drawable/vertical_progressbar" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/volume_imageView"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:contentDescription="@string/volume"
|
||||||
|
android:src="@drawable/ic_volume_up" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/volume_progressBar"
|
android:id="@+id/progress"
|
||||||
style="@android:style/Widget.ProgressBar.Horizontal"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="7dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="100dp"
|
android:layout_gravity="center" />
|
||||||
android:layout_gravity="center"
|
|
||||||
android:layout_marginVertical="4dp"
|
|
||||||
android:progressDrawable="@drawable/vertical_progressbar" />
|
|
||||||
|
|
||||||
<ImageView
|
</FrameLayout>
|
||||||
android:id="@+id/volume_imageView"
|
|
||||||
android:layout_width="24dp"
|
<LinearLayout
|
||||||
android:layout_height="24dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_gravity="center"
|
android:layout_height="wrap_content"
|
||||||
android:contentDescription="@string/volume"
|
android:layout_margin="20dp"
|
||||||
android:src="@drawable/ic_volume_up" />
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
style="@style/TextViewMarquee"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="24sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/uploader"
|
||||||
|
style="@style/TextViewMarquee"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<ProgressBar
|
<com.google.android.material.slider.Slider
|
||||||
android:id="@+id/progress"
|
android:id="@+id/time_bar"
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center" />
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="20dp"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/title"
|
|
||||||
style="@style/TextViewMarquee"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textSize="24sp" />
|
android:layout_marginHorizontal="20dp"
|
||||||
|
app:labelBehavior="gone" />
|
||||||
|
|
||||||
<TextView
|
<FrameLayout
|
||||||
android:id="@+id/uploader"
|
|
||||||
style="@style/TextViewMarquee"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="10dp"
|
android:paddingHorizontal="20dp">
|
||||||
android:textSize="18sp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
<TextView
|
||||||
|
android:id="@+id/current_position"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="start|center"
|
||||||
|
tools:text="00:00" />
|
||||||
|
|
||||||
<com.google.android.material.slider.Slider
|
<TextView
|
||||||
android:id="@+id/time_bar"
|
android:id="@+id/duration"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="20dp"
|
android:layout_gravity="end|center"
|
||||||
app:labelBehavior="gone" />
|
tools:text="10:15" />
|
||||||
|
|
||||||
<FrameLayout
|
</FrameLayout>
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingHorizontal="20dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/current_position"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="start|center"
|
|
||||||
tools:text="00:00" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/duration"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="end|center"
|
|
||||||
tools:text="10:15" />
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:layout_marginBottom="36dp">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/prev"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:layout_marginHorizontal="20dp"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:src="@drawable/ic_prev" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/play_pause"
|
|
||||||
android:layout_width="72dp"
|
|
||||||
android:layout_height="72dp"
|
|
||||||
android:insetLeft="0dp"
|
|
||||||
android:insetTop="0dp"
|
|
||||||
android:insetRight="0dp"
|
|
||||||
android:insetBottom="0dp"
|
|
||||||
app:icon="@drawable/ic_pause"
|
|
||||||
app:iconSize="24dp"
|
|
||||||
app:shapeAppearanceOverlay="@style/ShapeAppearance.Material3.Corner.Full" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/next"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:layout_marginHorizontal="20dp"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:src="@drawable/ic_next" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
style="@style/Widget.Material3.CardView.Elevated"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:layout_marginBottom="30dp"
|
|
||||||
app:cardCornerRadius="18dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:padding="15dp">
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_marginBottom="36dp">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/open_queue"
|
android:id="@+id/prev"
|
||||||
style="@style/AudioPlayerButton"
|
android:layout_width="48dp"
|
||||||
android:src="@drawable/ic_queue" />
|
android:layout_height="48dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginHorizontal="20dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:src="@drawable/ic_prev" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/play_pause"
|
||||||
|
android:layout_width="72dp"
|
||||||
|
android:layout_height="72dp"
|
||||||
|
android:insetLeft="0dp"
|
||||||
|
android:insetTop="0dp"
|
||||||
|
android:insetRight="0dp"
|
||||||
|
android:insetBottom="0dp"
|
||||||
|
app:icon="@drawable/ic_pause"
|
||||||
|
app:iconSize="24dp"
|
||||||
|
app:shapeAppearanceOverlay="@style/ShapeAppearance.Material3.Corner.Full" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/playback_options"
|
android:id="@+id/next"
|
||||||
style="@style/AudioPlayerButton"
|
android:layout_width="48dp"
|
||||||
android:layout_width="27dp"
|
android:layout_height="48dp"
|
||||||
android:layout_height="27dp"
|
android:layout_gravity="center"
|
||||||
android:src="@drawable/ic_speed" />
|
android:layout_marginHorizontal="20dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
<ImageView
|
android:src="@drawable/ic_next" />
|
||||||
android:id="@+id/open_video"
|
|
||||||
style="@style/AudioPlayerButton"
|
|
||||||
android:src="@drawable/ic_video" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/share"
|
|
||||||
style="@style/AudioPlayerButton"
|
|
||||||
android:src="@drawable/ic_share" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/close"
|
|
||||||
style="@style/AudioPlayerButton"
|
|
||||||
android:src="@drawable/ic_close" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
style="@style/Widget.Material3.CardView.Elevated"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginBottom="30dp"
|
||||||
|
app:cardCornerRadius="18dp">
|
||||||
|
|
||||||
</LinearLayout>
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:padding="15dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/open_queue"
|
||||||
|
style="@style/AudioPlayerButton"
|
||||||
|
android:src="@drawable/ic_queue" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/playback_options"
|
||||||
|
style="@style/AudioPlayerButton"
|
||||||
|
android:layout_width="27dp"
|
||||||
|
android:layout_height="27dp"
|
||||||
|
android:src="@drawable/ic_speed" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/open_video"
|
||||||
|
style="@style/AudioPlayerButton"
|
||||||
|
android:src="@drawable/ic_video" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/share"
|
||||||
|
style="@style/AudioPlayerButton"
|
||||||
|
android:src="@drawable/ic_share" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/close"
|
||||||
|
style="@style/AudioPlayerButton"
|
||||||
|
android:src="@drawable/ic_close" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/miniPlayerControls"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:alpha="0"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/audio_player_container"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/audio_player_container"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/audio_player_container"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/audio_player_container">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/miniPlayerThumbnail"
|
||||||
|
android:layout_width="96dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:padding="8dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/miniPlayerTitle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:paddingHorizontal="8dp"
|
||||||
|
android:paddingVertical="15dp" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/miniPlayerPause"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:src="@drawable/ic_pause" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/miniPlayerClose"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:src="@drawable/ic_close" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.github.libretube.ui.views.SingleViewTouchableMotionLayout>
|
@ -54,9 +54,4 @@
|
|||||||
android:name="com.github.libretube.ui.fragments.DownloadsFragment"
|
android:name="com.github.libretube.ui.fragments.DownloadsFragment"
|
||||||
android:label="@string/downloads"
|
android:label="@string/downloads"
|
||||||
tools:layout="@layout/fragment_downloads" />
|
tools:layout="@layout/fragment_downloads" />
|
||||||
<fragment
|
|
||||||
android:id="@+id/audioPlayerFragment"
|
|
||||||
android:name="com.github.libretube.ui.fragments.AudioPlayerFragment"
|
|
||||||
android:label="@string/audio_player"
|
|
||||||
tools:layout="@layout/fragment_audio_player" />
|
|
||||||
</navigation>
|
</navigation>
|
67
app/src/main/res/xml/audio_player_scene.xml
Normal file
67
app/src/main/res/xml/audio_player_scene.xml
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:motion="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<Transition
|
||||||
|
android:id="@+id/mini_player_transition"
|
||||||
|
motion:constraintSetEnd="@+id/end"
|
||||||
|
motion:constraintSetStart="@id/start"
|
||||||
|
motion:duration="500"
|
||||||
|
motion:motionInterpolator="easeInOut">
|
||||||
|
<KeyFrameSet>
|
||||||
|
<KeyAttribute
|
||||||
|
android:alpha="0"
|
||||||
|
motion:framePosition="90"
|
||||||
|
motion:motionTarget="@+id/miniPlayerControls" />
|
||||||
|
</KeyFrameSet>
|
||||||
|
<OnSwipe
|
||||||
|
motion:dragDirection="dragDown"
|
||||||
|
motion:dragScale="6"
|
||||||
|
motion:maxAcceleration="40"
|
||||||
|
motion:touchAnchorId="@+id/audio_player_container"
|
||||||
|
motion:touchAnchorSide="bottom" />
|
||||||
|
</Transition>
|
||||||
|
|
||||||
|
<ConstraintSet android:id="@+id/start">
|
||||||
|
<Constraint
|
||||||
|
android:id="@+id/audio_player_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
motion:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
motion:layout_constraintEnd_toEndOf="parent"
|
||||||
|
motion:layout_constraintStart_toStartOf="parent"
|
||||||
|
motion:layout_constraintTop_toTopOf="parent" />
|
||||||
|
</ConstraintSet>
|
||||||
|
|
||||||
|
<ConstraintSet android:id="@+id/end">
|
||||||
|
<Constraint
|
||||||
|
android:id="@+id/audio_player_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
motion:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
motion:layout_constraintEnd_toEndOf="parent"
|
||||||
|
motion:layout_constraintHorizontal_bias="0.5"
|
||||||
|
motion:layout_constraintStart_toStartOf="parent"
|
||||||
|
motion:layout_constraintTop_toTopOf="parent"
|
||||||
|
motion:layout_constraintVertical_bias="1.0" />
|
||||||
|
<Constraint
|
||||||
|
android:id="@+id/audio_player_main"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
motion:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
motion:layout_constraintEnd_toEndOf="parent"
|
||||||
|
motion:layout_constraintStart_toStartOf="parent"
|
||||||
|
motion:layout_constraintTop_toBottomOf="@+id/main_container" />
|
||||||
|
<Constraint
|
||||||
|
android:id="@+id/miniPlayerControls"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:alpha="1"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:visibility="visible"
|
||||||
|
motion:layout_constraintBottom_toBottomOf="@+id/audio_player_container"
|
||||||
|
motion:layout_constraintEnd_toEndOf="@id/audio_player_container"
|
||||||
|
motion:layout_constraintStart_toStartOf="@id/audio_player_container"
|
||||||
|
motion:layout_constraintTop_toTopOf="@+id/audio_player_container" />
|
||||||
|
</ConstraintSet>
|
||||||
|
</MotionScene>
|
@ -31,34 +31,6 @@
|
|||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
<ConstraintSet android:id="@+id/start">
|
<ConstraintSet android:id="@+id/start">
|
||||||
<Constraint
|
|
||||||
android:id="@+id/player"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
motion:layout_constraintBottom_toBottomOf="@id/main_container"
|
|
||||||
motion:layout_constraintStart_toStartOf="@id/main_container"
|
|
||||||
motion:layout_constraintTop_toTopOf="@id/main_container" />
|
|
||||||
<Constraint
|
|
||||||
android:id="@+id/player"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
motion:layout_constraintBottom_toBottomOf="@id/main_container"
|
|
||||||
motion:layout_constraintStart_toStartOf="@id/main_container"
|
|
||||||
motion:layout_constraintTop_toTopOf="@id/main_container" />
|
|
||||||
<Constraint
|
|
||||||
android:id="@+id/player"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
motion:layout_constraintBottom_toBottomOf="@id/main_container"
|
|
||||||
motion:layout_constraintStart_toStartOf="@id/main_container"
|
|
||||||
motion:layout_constraintTop_toTopOf="@id/main_container" />
|
|
||||||
<Constraint
|
|
||||||
android:id="@+id/player"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
motion:layout_constraintBottom_toBottomOf="@id/main_container"
|
|
||||||
motion:layout_constraintStart_toStartOf="@id/main_container"
|
|
||||||
motion:layout_constraintTop_toTopOf="@id/main_container" />
|
|
||||||
<Constraint
|
<Constraint
|
||||||
android:id="@+id/player"
|
android:id="@+id/player"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
Loading…
Reference in New Issue
Block a user