Added MD3 playing theme

This commit is contained in:
Prathamesh More 2022-05-07 11:47:36 +05:30
parent ae13590204
commit 7debeb13c0
13 changed files with 771 additions and 0 deletions

View file

@ -67,6 +67,7 @@
<h2>v5.8.3</h2>
<h3>What's New</h3>
<ul>
<li>Added a new MD3 now playing theme</li>
<li>Swipe down to dismiss Mini player</li>
<li>Add support for Just Black with Material You</li>
</ul>

View file

@ -49,6 +49,7 @@ import code.name.monkey.retromusic.fragments.player.flat.FlatPlayerFragment
import code.name.monkey.retromusic.fragments.player.full.FullPlayerFragment
import code.name.monkey.retromusic.fragments.player.gradient.GradientPlayerFragment
import code.name.monkey.retromusic.fragments.player.material.MaterialFragment
import code.name.monkey.retromusic.fragments.player.md3.MD3PlayerFragment
import code.name.monkey.retromusic.fragments.player.normal.PlayerFragment
import code.name.monkey.retromusic.fragments.player.peek.PeekPlayerFragment
import code.name.monkey.retromusic.fragments.player.plain.PlainPlayerFragment
@ -457,6 +458,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
Peek -> PeekPlayerFragment()
Circle -> CirclePlayerFragment()
Classic -> ClassicPlayerFragment()
MD3 -> MD3PlayerFragment()
else -> PlayerFragment()
} // must implement AbsPlayerFragment
supportFragmentManager.commit {

View file

@ -292,6 +292,9 @@ fun Context.accentColorVariant(): Int {
inline val @receiver:ColorInt Int.isColorLight
get() = ColorUtil.isColorLight(this)
inline val @receiver:ColorInt Int.lightColor
get() = ColorUtil.withAlpha(this, 0.5F)
inline val @receiver:ColorInt Int.lighterColor
get() = ColorUtil.lightenColor(this)

View file

@ -37,6 +37,7 @@ import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.card.MaterialCardView
import dev.chrisbanes.insetter.applyInsetter
@Suppress("UNCHECKED_CAST")
@ -163,6 +164,23 @@ fun BottomSheetBehavior<*>.peekHeightAnimate(value: Int): Animator {
}
}
fun MaterialCardView.animateRadius(cornerRadius: Float, pause: Boolean = true) {
ValueAnimator.ofFloat(radius, cornerRadius).apply {
addUpdateListener { radius = animatedValue as Float }
start()
}
ValueAnimator.ofInt(measuredWidth, if (pause) (height * 1.5).toInt() else height).apply {
addUpdateListener {
updateLayoutParams<ViewGroup.LayoutParams> { width = animatedValue as Int }
}
start()
}
}
fun MaterialCardView.animateToCircle() {
animateRadius(measuredHeight / 2F, pause = false)
}
fun View.focusAndShowKeyboard() {
/**
* This is to be called when the window already has focus.

View file

@ -39,6 +39,7 @@ enum class NowPlayingScreen constructor(
Full(R.string.full, R.drawable.np_full, 2, AlbumCoverStyle.Full),
Gradient(R.string.gradient, R.drawable.np_gradient, 17, AlbumCoverStyle.Full),
Material(R.string.material, R.drawable.np_material, 11, AlbumCoverStyle.Normal),
MD3(R.string.md3, R.drawable.np_normal, 18, AlbumCoverStyle.Normal),
Normal(R.string.normal, R.drawable.np_normal, 0, AlbumCoverStyle.Normal),
Peek(R.string.peek, R.drawable.np_peek, 14, AlbumCoverStyle.Normal),
Plain(R.string.plain, R.drawable.np_plain, 3, AlbumCoverStyle.Normal),

View file

@ -0,0 +1,234 @@
/*
* Copyright (c) 2020 Hemanth Savarla.
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
*/
package code.name.monkey.retromusic.fragments.player.md3
import android.animation.ObjectAnimator
import android.os.Bundle
import android.view.View
import android.view.animation.DecelerateInterpolator
import android.view.animation.LinearInterpolator
import android.widget.ImageButton
import android.widget.SeekBar
import android.widget.TextView
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentMd3PlayerPlaybackControlsBinding
import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class MD3PlaybackControlsFragment :
AbsPlayerControlsFragment(R.layout.fragment_md3_player_playback_controls) {
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
private var _binding: FragmentMd3PlayerPlaybackControlsBinding? = null
private val binding get() = _binding!!
override val progressSlider: SeekBar
get() = binding.progressSlider
override val shuffleButton: ImageButton
get() = binding.shuffleButton
override val repeatButton: ImageButton
get() = binding.repeatButton
override val nextButton: ImageButton
get() = binding.nextButton
override val previousButton: ImageButton
get() = binding.previousButton
override val songTotalTime: TextView
get() = binding.songTotalTime
override val songCurrentProgress: TextView
get() = binding.songCurrentProgress
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentMd3PlayerPlaybackControlsBinding.bind(view)
setUpMusicControllers()
binding.playPauseButton.setOnClickListener {
if (MusicPlayerRemote.isPlaying) {
MusicPlayerRemote.pauseSong()
} else {
MusicPlayerRemote.resumePlaying()
}
}
binding.title.isSelected = true
binding.text.isSelected = true
binding.title.setOnClickListener {
goToAlbum(requireActivity())
}
binding.text.setOnClickListener {
goToArtist(requireActivity())
}
}
override fun setColor(color: MediaNotificationProcessor) {
val colorBg = ATHUtil.resolveColor(requireContext(), android.R.attr.colorBackground)
if (ColorUtil.isColorLight(colorBg)) {
lastPlaybackControlsColor =
MaterialValueHelper.getSecondaryTextColor(requireContext(), true)
lastDisabledPlaybackControlsColor =
MaterialValueHelper.getSecondaryDisabledTextColor(requireContext(), true)
} else {
lastPlaybackControlsColor =
MaterialValueHelper.getPrimaryTextColor(requireContext(), false)
lastDisabledPlaybackControlsColor =
MaterialValueHelper.getPrimaryDisabledTextColor(requireContext(), false)
}
val colorFinal = if (PreferenceUtil.isAdaptiveColor) {
color.primaryTextColor
} else {
ThemeStore.accentColor(requireContext())
}.ripAlpha()
TintHelper.setTintAuto(
binding.playPauseButton,
MaterialValueHelper.getPrimaryTextColor(
requireContext(),
ColorUtil.isColorLight(colorFinal)
),
false
)
binding.playPauseCard.setCardBackgroundColor(colorFinal)
binding.progressSlider.applyColor(colorFinal)
volumeFragment?.setTintable(colorFinal)
updateRepeatState()
updateShuffleState()
updatePrevNextColor()
updatePlayPauseDrawableState()
}
private fun updateSong() {
val song = MusicPlayerRemote.currentSong
binding.title.text = song.title
binding.text.text = song.artistName
if (PreferenceUtil.isSongInfo) {
binding.songInfo.text = getSongInfo(song)
binding.songInfo.show()
} else {
binding.songInfo.hide()
}
}
override fun onResume() {
super.onResume()
progressViewUpdateHelper.start()
}
override fun onPause() {
super.onPause()
progressViewUpdateHelper.stop()
}
override fun onServiceConnected() {
updatePlayPauseDrawableState()
updateRepeatState()
updateShuffleState()
updateSong()
}
override fun onPlayingMetaChanged() {
super.onPlayingMetaChanged()
updateSong()
}
override fun onPlayStateChanged() {
updatePlayPauseDrawableState()
}
override fun onRepeatModeChanged() {
updateRepeatState()
}
override fun onShuffleModeChanged() {
updateShuffleState()
}
private fun setUpPlayPauseFab() {
binding.playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
updatePlayPauseDrawableState()
}
private fun updatePlayPauseDrawableState() {
if (MusicPlayerRemote.isPlaying) {
binding.playPauseButton.setImageResource(R.drawable.ic_pause_outline_small)
binding.playPauseCard.animateRadius(40F)
} else {
binding.playPauseButton.setImageResource(R.drawable.ic_play_arrow_outline_small)
binding.playPauseCard.animateToCircle()
}
}
private fun setUpMusicControllers() {
setUpPlayPauseFab()
}
public override fun show() {
binding.playPauseButton.animate()
.scaleX(1f)
.scaleY(1f)
.rotation(360f)
.setInterpolator(DecelerateInterpolator())
.start()
}
public override fun hide() {
binding.playPauseButton.apply {
scaleX = 0f
scaleY = 0f
rotation = 0f
}
}
override fun onUpdateProgressViews(progress: Int, total: Int) {
binding.progressSlider.max = total
val animator = ObjectAnimator.ofInt(binding.progressSlider, "progress", progress)
animator.duration = SLIDER_ANIMATION_TIME
animator.interpolator = LinearInterpolator()
animator.start()
binding.songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
binding.songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

View file

@ -0,0 +1,134 @@
/*
* Copyright (c) 2020 Hemanth Savarla.
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
*/
package code.name.monkey.retromusic.fragments.player.md3
import android.os.Bundle
import android.view.View
import androidx.appcompat.widget.Toolbar
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentMd3PlayerBinding
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class MD3PlayerFragment : AbsPlayerFragment(R.layout.fragment_md3_player) {
private var lastColor: Int = 0
override val paletteColor: Int
get() = lastColor
private lateinit var controlsFragment: MD3PlaybackControlsFragment
private var _binding: FragmentMd3PlayerBinding? = null
private val binding get() = _binding!!
override fun onShow() {
controlsFragment.show()
}
override fun onHide() {
controlsFragment.hide()
onBackPressed()
}
override fun onBackPressed(): Boolean {
return false
}
override fun toolbarIconColor(): Int {
return ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal)
}
override fun onColorChanged(color: MediaNotificationProcessor) {
controlsFragment.setColor(color)
lastColor = color.backgroundColor
libraryViewModel.updateColor(color.backgroundColor)
ToolbarContentTintHelper.colorizeToolbar(
binding.playerToolbar,
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
requireActivity()
)
}
override fun toggleFavorite(song: Song) {
super.toggleFavorite(song)
if (song.id == MusicPlayerRemote.currentSong.id) {
updateIsFavorite()
}
}
override fun onFavoriteToggled() {
toggleFavorite(MusicPlayerRemote.currentSong)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentMd3PlayerBinding.bind(view)
setUpSubFragments()
setUpPlayerToolbar()
playerToolbar().drawAboveSystemBars()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
private fun setUpSubFragments() {
controlsFragment =
childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as MD3PlaybackControlsFragment
val playerAlbumCoverFragment =
childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment
playerAlbumCoverFragment.setCallbacks(this)
}
private fun setUpPlayerToolbar() {
binding.playerToolbar.inflateMenu(R.menu.menu_player)
//binding.playerToolbar.menu.setUpWithIcons()
binding.playerToolbar.setNavigationOnClickListener { requireActivity().onBackPressed() }
binding.playerToolbar.setOnMenuItemClickListener(this)
ToolbarContentTintHelper.colorizeToolbar(
binding.playerToolbar,
ATHUtil.resolveColor(requireContext(), R.attr.colorControlNormal),
requireActivity()
)
}
override fun onServiceConnected() {
updateIsFavorite()
}
override fun onPlayingMetaChanged() {
updateIsFavorite()
}
override fun playerToolbar(): Toolbar {
return binding.playerToolbar
}
companion object {
fun newInstance(): MD3PlayerFragment {
return MD3PlayerFragment()
}
}
}

View file

@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:strokeWidth="1.5"
android:pathData="M6.5,5.5h3v13h-3z"
android:fillColor="#00000000"
android:strokeColor="#000000" />
<path
android:strokeWidth="1.5"
android:pathData="M14.5,5.5h3v13h-3z"
android:fillColor="#00000000"
android:strokeColor="#000000" />
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M10,8.64L15.27,12 10,15.36V8.64M8,5v14l11,-7L8,5z" />
</vector>

View file

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true">
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface" />
<View
android:id="@+id/colorGradientBackground"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<include layout="@layout/shadow_statusbar_toolbar" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include layout="@layout/status_bar" />
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<code.name.monkey.retromusic.views.HeightFitSquareLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/playerAlbumCoverFragment"
android:name="code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="@layout/fragment_album_cover" />
</code.name.monkey.retromusic.views.HeightFitSquareLayout>
<LinearLayout
android:id="@+id/controlsContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/playerToolbar"
style="@style/Toolbar"
android:navigationIcon="@drawable/ic_keyboard_arrow_down_black"
app:navigationIcon="@drawable/ic_keyboard_arrow_down_black" />
</FrameLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/playbackControlsFragment"
android:name="code.name.monkey.retromusic.fragments.player.md3.MD3PlaybackControlsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:layout_weight="1"
tools:layout="@layout/fragment_md3_player_playback_controls" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</FrameLayout>

View file

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface"
android:clickable="true"
android:focusable="true">
<include layout="@layout/shadow_statusbar_toolbar" />
<FrameLayout
android:id="@+id/statusBarContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<include layout="@layout/status_bar" />
</FrameLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/playerAlbumCoverFragment"
android:name="code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/statusBarContainer" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/playbackControlsFragment"
android:name="code.name.monkey.retromusic.fragments.player.md3.MD3PlaybackControlsFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/playerToolbar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/playerAlbumCoverFragment"
tools:layout="@layout/fragment_md3_player_playback_controls" />
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/playerToolbar"
style="@style/Toolbar"
android:layout_gravity="bottom"
android:navigationIcon="@drawable/ic_keyboard_arrow_down_black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/playbackControlsFragment"
app:navigationIcon="@drawable/ic_keyboard_arrow_down_black" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,210 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="vertical"
tools:ignore="MissingPrefix">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/songCurrentProgress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:gravity="center"
android:minWidth="40dp"
android:singleLine="true"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintBottom_toBottomOf="@+id/progressSlider"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/progressSlider"
tools:ignore="RtlHardcoded,RtlSymmetry"
tools:text="@tools:sample/date/hhmmss" />
<androidx.appcompat.widget.AppCompatSeekBar
android:id="@+id/progressSlider"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:maxHeight="2dp"
android:paddingVertical="@dimen/seekbar_padding"
android:progressDrawable="@drawable/color_progress_seek"
app:layout_constraintEnd_toStartOf="@id/songTotalTime"
app:layout_constraintStart_toEndOf="@id/songCurrentProgress"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="RtlHardcoded,UnusedAttribute"
tools:progress="20" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/songTotalTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:gravity="center"
android:minWidth="40dp"
android:singleLine="true"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintBottom_toBottomOf="@+id/progressSlider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/progressSlider"
tools:ignore="RtlHardcoded,RtlSymmetry"
tools:text="@tools:sample/date/hhmmss" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:ellipsize="marquee"
android:focusable="true"
android:freezesText="true"
android:gravity="center"
android:marqueeRepeatLimit="marquee_forever"
android:paddingHorizontal="16dp"
android:scrollHorizontally="true"
android:singleLine="true"
android:textAppearance="@style/TextViewHeadline6"
android:textColor="?android:attr/textColorPrimary"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/playPauseCard"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/progressSlider"
app:layout_constraintVertical_bias="0.3"
tools:text="@tools:sample/lorem/random" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
android:freezesText="true"
android:gravity="center"
android:marqueeRepeatLimit="marquee_forever"
android:paddingHorizontal="16dp"
android:paddingVertical="8dp"
android:scrollHorizontally="true"
android:singleLine="true"
android:textAppearance="@style/TextViewBody1"
android:textColor="?android:attr/textColorSecondary"
app:layout_constrainedWidth="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title"
tools:text="@tools:sample/full_names" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/songInfo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="2"
android:paddingHorizontal="16dp"
android:paddingBottom="8dp"
android:textColor="?android:attr/textColorSecondary"
android:textSize="12sp"
app:layout_constraintBottom_toTopOf="@+id/volumeFragmentContainer"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="@tools:sample/full_names" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/previousButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@null"
android:padding="16dp"
android:scaleType="fitCenter"
app:layout_constraintBottom_toBottomOf="@+id/playPauseCard"
app:layout_constraintEnd_toStartOf="@id/playPauseCard"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/playPauseCard"
app:srcCompat="@drawable/ic_skip_previous_outline"
tools:backgroundTint="?colorPrimaryDark"
tools:ignore="MissingPrefix"
tools:tint="@color/md_black_1000" />
<com.google.android.material.card.MaterialCardView
android:id="@+id/playPauseCard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardBackgroundColor="?colorSecondary"
app:cardCornerRadius="40dp"
app:layout_constraintBottom_toTopOf="@+id/repeatButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text"
tools:tint="@color/md_black_1000">
<ImageButton
android:id="@+id/playPauseButton"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/transparent"
android:padding="32dp"
app:srcCompat="@drawable/ic_pause_outline_small"
tools:tint="@color/md_black_1000" />
</com.google.android.material.card.MaterialCardView>
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/nextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@null"
android:padding="16dp"
android:scaleType="fitCenter"
app:layout_constraintBottom_toBottomOf="@+id/playPauseCard"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/playPauseCard"
app:layout_constraintTop_toTopOf="@+id/playPauseCard"
app:srcCompat="@drawable/ic_skip_next_outline"
tools:backgroundTint="?colorPrimaryDark"
tools:ignore="MissingPrefix"
tools:tint="@color/md_black_1000" />
<ImageButton
android:id="@+id/shuffleButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:background="@null"
android:paddingVertical="16dp"
android:src="@drawable/ic_shuffle"
app:layout_constraintBottom_toTopOf="@+id/songInfo"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/repeatButton"
app:layout_constraintTop_toBottomOf="@+id/playPauseCard"
tools:ignore="MissingPrefix"
tools:tint="@color/md_black_1000" />
<ImageButton
android:id="@+id/repeatButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:background="@null"
android:paddingVertical="16dp"
android:src="@drawable/ic_repeat"
app:layout_constraintBottom_toTopOf="@+id/songInfo"
app:layout_constraintEnd_toStartOf="@id/shuffleButton"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/playPauseCard"
tools:ignore="MissingPrefix"
tools:tint="@color/md_black_1000" />
<FrameLayout
android:id="@+id/volumeFragmentContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0"
app:layout_constraintBottom_toBottomOf="parent"
tools:background="@color/md_red_400"
tools:layout_height="52dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -239,6 +239,7 @@
<string name="material">Material</string>
<string name="md_error_label">Error</string>
<string name="md_storage_perm_error">Permission error</string>
<string name="md3" translatable="false">MD3</string>
<string name="my_name">Name</string>
<string name="my_top_tracks">Most played</string>
<string name="never">Never</string>