playback: make compact playback ui a view

Change CompactPlaybackFragment into a View. This completely fixes the
issue I tried to band-aid in ae39054. The code is a bit uglier, but
that's tolerable.
This commit is contained in:
OxygenCobalt 2021-10-31 11:14:04 -06:00
parent ae39054b63
commit c1e1329c21
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
13 changed files with 226 additions and 178 deletions

View file

@ -28,10 +28,13 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import org.oxycblt.auxio.databinding.FragmentMainBinding import org.oxycblt.auxio.databinding.FragmentMainBinding
import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.playback.PlaybackBarLayout
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logD
@ -39,8 +42,9 @@ import org.oxycblt.auxio.util.logD
* A wrapper around the home fragment that shows the playback fragment and controls * A wrapper around the home fragment that shows the playback fragment and controls
* the more high-level navigation features. * the more high-level navigation features.
*/ */
class MainFragment : Fragment() { class MainFragment : Fragment(), PlaybackBarLayout.ActionCallback {
private val playbackModel: PlaybackViewModel by activityViewModels() private val playbackModel: PlaybackViewModel by activityViewModels()
private val detailModel: DetailViewModel by activityViewModels()
private val musicModel: MusicViewModel by activityViewModels() private val musicModel: MusicViewModel by activityViewModels()
override fun onCreateView( override fun onCreateView(
@ -63,22 +67,26 @@ class MainFragment : Fragment() {
// --- VIEWMODEL SETUP --- // --- VIEWMODEL SETUP ---
if (playbackModel.song.value != null) { binding.mainBarLayout.setActionCallback(this)
binding.mainBarLayout.showBar()
} else { binding.mainBarLayout.setSong(playbackModel.song.value)
binding.mainBarLayout.hideBar() binding.mainBarLayout.setPlaying(playbackModel.isPlaying.value!!)
} binding.mainBarLayout.setPosition(playbackModel.position.value!!)
playbackModel.song.observe(viewLifecycleOwner) { song -> playbackModel.song.observe(viewLifecycleOwner) { song ->
if (song != null) { binding.mainBarLayout.setSong(song)
binding.mainBarLayout.showBar()
} else {
binding.mainBarLayout.hideBar()
}
} }
// Initialize music loading. Unlike MainFragment, we can not only do this here on startup playbackModel.isPlaying.observe(viewLifecycleOwner) { isPlaying ->
// but also show a SnackBar in a reasonable place in this fragment. binding.mainBarLayout.setPlaying(isPlaying)
}
playbackModel.position.observe(viewLifecycleOwner) { pos ->
binding.mainBarLayout.setPosition(pos)
}
// Initialize music loading. Do it here so that it shows on every fragment that this
// one contains.
musicModel.loadMusic(requireContext()) musicModel.loadMusic(requireContext())
// Handle the music loader response. // Handle the music loader response.
@ -132,4 +140,18 @@ class MainFragment : Fragment() {
return binding.root return binding.root
} }
override fun onPlayPauseClick() {
playbackModel.invertPlayingStatus()
}
override fun onNavToPlayback() {
findNavController().navigate(
MainFragmentDirections.actionGoToPlayback()
)
}
override fun onNavToItem() {
detailModel.navToItem(playbackModel.song.value ?: return)
}
} }

View file

@ -42,6 +42,7 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.util.canScroll import org.oxycblt.auxio.util.canScroll
import org.oxycblt.auxio.util.resolveAttr import org.oxycblt.auxio.util.resolveAttr
import org.oxycblt.auxio.util.resolveDrawable import org.oxycblt.auxio.util.resolveDrawable
import org.oxycblt.auxio.util.systemBarsCompat
import kotlin.math.abs import kotlin.math.abs
/** /**
@ -324,12 +325,14 @@ class FastScrollRecyclerView @JvmOverloads constructor(
} }
override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets { override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {
val bars = insets.systemBarsCompat
setPadding( setPadding(
initialPadding.left, initialPadding.top, initialPadding.right, initialPadding.left, initialPadding.top, initialPadding.right,
initialPadding.bottom + insets.systemWindowInsetBottom initialPadding.bottom + bars.bottom
) )
scrollerPadding.bottom = insets.systemWindowInsetBottom scrollerPadding.bottom = bars.bottom
return insets return insets
} }

View file

@ -1,90 +0,0 @@
/*
* Copyright (c) 2021 Auxio Project
* CompactPlaybackFragment.kt is part of Auxio.
*
* This program 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 program 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.auxio.playback
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import org.oxycblt.auxio.MainFragmentDirections
import org.oxycblt.auxio.databinding.FragmentCompactPlaybackBinding
import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.util.logD
/**
* A [Fragment] that displays the currently played song at a glance, with some basic controls.
* Extends into [PlaybackFragment] when clicked on.
*
* Instantiation is done by FragmentContainerView, **do not instantiate this fragment manually.**
* @author OxygenCobalt
* TODO: Add more controls to this view depending on screen width
*/
class CompactPlaybackFragment : Fragment() {
private val playbackModel: PlaybackViewModel by activityViewModels()
private val detailModel: DetailViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val binding = FragmentCompactPlaybackBinding.inflate(inflater)
// --- UI SETUP ---
binding.lifecycleOwner = viewLifecycleOwner
binding.playbackModel = playbackModel
binding.executePendingBindings()
binding.root.apply {
setOnClickListener {
findNavController().navigate(
MainFragmentDirections.actionGoToPlayback()
)
}
setOnLongClickListener {
detailModel.navToItem(playbackModel.song.value!!)
true
}
}
// --- VIEWMODEL SETUP ---
playbackModel.song.observe(viewLifecycleOwner) { song ->
if (song != null) {
logD("Updating song display to ${song.name}")
binding.song = song
binding.playbackProgress.max = song.seconds.toInt()
}
}
playbackModel.isPlaying.observe(viewLifecycleOwner) { isPlaying ->
binding.playbackPlayPause.isActivated = isPlaying
}
logD("Fragment Created")
return binding.root
}
}

View file

@ -0,0 +1,109 @@
/*
* Copyright (c) 2021 Auxio Project
* CompactPlaybackView.kt is part of Auxio.
*
* This program 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 program 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.auxio.playback
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.drawable.RippleDrawable
import android.util.AttributeSet
import androidx.constraintlayout.widget.ConstraintLayout
import com.google.android.material.shape.MaterialShapeDrawable
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.ViewCompactPlaybackBinding
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.util.inflater
import org.oxycblt.auxio.util.resolveAttr
import org.oxycblt.auxio.util.resolveDrawable
/**
* A view displaying the playback state in a compact manner. This is only meant to be used
* by [PlaybackBarLayout].
*/
class CompactPlaybackView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = -1
) : ConstraintLayout(context, attrs, defStyleAttr) {
private val binding = ViewCompactPlaybackBinding.inflate(context.inflater, this, true)
private var mCallback: PlaybackBarLayout.ActionCallback? = null
init {
id = R.id.playback_bar
elevation = resources.getDimensionPixelSize(R.dimen.elevation_normal).toFloat()
// To get a MaterialShapeDrawable to co-exist with a ripple drawable, we need to layer
// this drawable on top of the existing ripple drawable. RippleDrawable actually inherits
// LayerDrawable though, so we can do this. However, adding a new drawable layer directly
// is only available on API 23+, but we're on API 21. So we create a drawable resource with
// an empty drawable with a hard-coded ID, filling the drawable in with a
// MaterialShapeDrawable at runtime and allowing this code to work on API 21.
background = R.drawable.ui_shape_ripple.resolveDrawable(context).apply {
val backgroundDrawable = MaterialShapeDrawable.createWithElevationOverlay(context).apply {
elevation = this@CompactPlaybackView.elevation
fillColor = ColorStateList.valueOf(R.attr.colorSurface.resolveAttr(context))
}
(this as RippleDrawable).setDrawableByLayerId(
android.R.id.background, backgroundDrawable
)
}
isClickable = true
isFocusable = true
setOnClickListener {
mCallback?.onNavToPlayback()
}
setOnLongClickListener {
mCallback?.onNavToItem()
true
}
binding.playbackPlayPause.setOnClickListener {
mCallback?.onPlayPauseClick()
}
}
fun setSong(song: Song) {
binding.song = song
}
fun setPlaying(isPlaying: Boolean) {
binding.playbackPlayPause.isActivated = isPlaying
}
fun setPosition(position: Long) {
if (binding.song == null) {
binding.playbackProgress.progress = 0
return
}
binding.playbackProgress.progress = position.toInt()
}
fun setCallback(callback: PlaybackBarLayout.ActionCallback) {
mCallback = callback
}
fun clearCallback() {
mCallback = null
}
}

View file

@ -19,25 +19,18 @@
package org.oxycblt.auxio.playback package org.oxycblt.auxio.playback
import android.content.Context import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Insets import android.graphics.Insets
import android.os.Build import android.os.Build
import android.os.Parcelable
import android.util.AttributeSet import android.util.AttributeSet
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.WindowInsets import android.view.WindowInsets
import android.widget.FrameLayout
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.annotation.StyleRes import androidx.annotation.StyleRes
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.children import androidx.core.view.children
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import com.google.android.material.shape.MaterialShapeDrawable import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.R
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.resolveAttr
import org.oxycblt.auxio.util.systemBarsCompat import org.oxycblt.auxio.util.systemBarsCompat
/** /**
@ -46,9 +39,6 @@ import org.oxycblt.auxio.util.systemBarsCompat
* this class was primarily written by me and I plan to expand this layout to become part of * this class was primarily written by me and I plan to expand this layout to become part of
* the playback navigation process. * the playback navigation process.
* *
* TODO: Migrate CompactPlaybackFragment to a view. This is okay, as updates can be delivered
* via MainFragment and it would fix the issue where the actual layout won't measure until
* the fragment is shown.
* TODO: Implement animation * TODO: Implement animation
* TODO: Implement the swipe-up behavior. This needs to occur, as the way the main fragment * TODO: Implement the swipe-up behavior. This needs to occur, as the way the main fragment
* saves state results in' * saves state results in'
@ -59,37 +49,18 @@ class PlaybackBarLayout @JvmOverloads constructor(
@AttrRes defStyleAttr: Int = 0, @AttrRes defStyleAttr: Int = 0,
@StyleRes defStyleRes: Int = 0 @StyleRes defStyleRes: Int = 0
) : ViewGroup(context, attrs, defStyleAttr, defStyleRes) { ) : ViewGroup(context, attrs, defStyleAttr, defStyleRes) {
private val barLayout = FrameLayout(context) private val playbackView = CompactPlaybackView(context)
private val playbackFragment = CompactPlaybackFragment()
private var lastInsets: WindowInsets? = null private var lastInsets: WindowInsets? = null
init { init {
addView(barLayout) addView(playbackView)
barLayout.apply {
id = R.id.main_playback
elevation = resources.getDimensionPixelSize(R.dimen.elevation_normal).toFloat()
playbackView.apply {
(layoutParams as LayoutParams).apply { (layoutParams as LayoutParams).apply {
width = ViewGroup.LayoutParams.MATCH_PARENT width = ViewGroup.LayoutParams.MATCH_PARENT
height = ViewGroup.LayoutParams.WRAP_CONTENT height = ViewGroup.LayoutParams.WRAP_CONTENT
isBar = true isBar = true
} }
background = MaterialShapeDrawable.createWithElevationOverlay(context).apply {
elevation = barLayout.elevation
fillColor = ColorStateList.valueOf(R.attr.colorSurface.resolveAttr(context))
}
}
if (!isInEditMode) {
(context as AppCompatActivity).supportFragmentManager.apply {
this
.beginTransaction()
.replace(R.id.main_playback, playbackFragment)
.commit()
}
} }
} }
@ -101,15 +72,15 @@ class PlaybackBarLayout @JvmOverloads constructor(
setMeasuredDimension(widthSize, heightSize) setMeasuredDimension(widthSize, heightSize)
val barParams = barLayout.layoutParams as LayoutParams val barParams = playbackView.layoutParams as LayoutParams
val barWidthSpec = getChildMeasureSpec(widthMeasureSpec, 0, barParams.width) val barWidthSpec = getChildMeasureSpec(widthMeasureSpec, 0, barParams.width)
val barHeightSpec = getChildMeasureSpec(heightMeasureSpec, 0, barParams.height) val barHeightSpec = getChildMeasureSpec(heightMeasureSpec, 0, barParams.height)
barLayout.measure(barWidthSpec, barHeightSpec) playbackView.measure(barWidthSpec, barHeightSpec)
updateWindowInsets() updateWindowInsets()
val barHeightAdjusted = (barLayout.measuredHeight * barParams.offset).toInt() val barHeightAdjusted = (playbackView.measuredHeight * barParams.offset).toInt()
val contentWidth = measuredWidth val contentWidth = measuredWidth
val contentHeight = measuredHeight - barHeightAdjusted val contentHeight = measuredHeight - barHeightAdjusted
@ -129,13 +100,13 @@ class PlaybackBarLayout @JvmOverloads constructor(
} }
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
val barHeight = if (barLayout.isVisible) { val barHeight = if (playbackView.isVisible) {
barLayout.measuredHeight playbackView.measuredHeight
} else { } else {
0 0
} }
val barHeightAdjusted = (barHeight * (barLayout.layoutParams as LayoutParams).offset).toInt() val barHeightAdjusted = (barHeight * (playbackView.layoutParams as LayoutParams).offset).toInt()
for (child in children) { for (child in children) {
if (child.visibility == View.GONE) continue if (child.visibility == View.GONE) continue
@ -154,7 +125,7 @@ class PlaybackBarLayout @JvmOverloads constructor(
} }
override fun dispatchApplyWindowInsets(insets: WindowInsets): WindowInsets { override fun dispatchApplyWindowInsets(insets: WindowInsets): WindowInsets {
barLayout.updatePadding(bottom = insets.systemBarsCompat.bottom) playbackView.updatePadding(bottom = insets.systemBarsCompat.bottom)
lastInsets = insets lastInsets = insets
updateWindowInsets() updateWindowInsets()
@ -162,6 +133,12 @@ class PlaybackBarLayout @JvmOverloads constructor(
return insets return insets
} }
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
playbackView.clearCallback()
}
private fun updateWindowInsets() { private fun updateWindowInsets() {
val insets = lastInsets val insets = lastInsets
@ -171,8 +148,8 @@ class PlaybackBarLayout @JvmOverloads constructor(
} }
private fun mutateInsets(insets: WindowInsets): WindowInsets { private fun mutateInsets(insets: WindowInsets): WindowInsets {
val barParams = barLayout.layoutParams as LayoutParams val barParams = playbackView.layoutParams as LayoutParams
val childConsumedInset = (barLayout.measuredHeight * barParams.offset).toInt() val childConsumedInset = (playbackView.measuredHeight * barParams.offset).toInt()
val bars = insets.systemBarsCompat val bars = insets.systemBarsCompat
@ -194,8 +171,29 @@ class PlaybackBarLayout @JvmOverloads constructor(
return insets return insets
} }
fun showBar() { fun setSong(song: Song?) {
val barParams = barLayout.layoutParams as LayoutParams if (song != null) {
showBar()
playbackView.setSong(song)
} else {
hideBar()
}
}
fun setPlaying(isPlaying: Boolean) {
playbackView.setPlaying(isPlaying)
}
fun setPosition(position: Long) {
playbackView.setPosition(position)
}
fun setActionCallback(callback: ActionCallback) {
playbackView.setCallback(callback)
}
private fun showBar() {
val barParams = playbackView.layoutParams as LayoutParams
if (barParams.offset == 1f) { if (barParams.offset == 1f) {
return return
@ -210,8 +208,8 @@ class PlaybackBarLayout @JvmOverloads constructor(
invalidate() invalidate()
} }
fun hideBar() { private fun hideBar() {
val barParams = barLayout.layoutParams as LayoutParams val barParams = playbackView.layoutParams as LayoutParams
if (barParams.offset == 0f) { if (barParams.offset == 0f) {
return return
@ -260,4 +258,10 @@ class PlaybackBarLayout @JvmOverloads constructor(
constructor(source: ViewGroup.LayoutParams) : super(source) constructor(source: ViewGroup.LayoutParams) : super(source)
} }
interface ActionCallback {
fun onPlayPauseClick()
fun onNavToItem()
fun onNavToPlayback()
}
} }

View file

@ -419,7 +419,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
* Restore playback on startup. This can do one of two things: * Restore playback on startup. This can do one of two things:
* - Play a file intent that was given by MainActivity in [playWithUri] * - Play a file intent that was given by MainActivity in [playWithUri]
* - Restore the last playback state if there is no active file intent. * - Restore the last playback state if there is no active file intent.
* TODO: Re-add this to HomeFragment once state can be restored
*/ */
fun setupPlayback(context: Context) { fun setupPlayback(context: Context) {
val intentUri = mIntentUri val intentUri = mIntentUri

View file

@ -138,6 +138,10 @@ fun @receiver:AttrRes Int.resolveAttr(context: Context): Int {
return color.resolveColor(context) return color.resolveColor(context)
} }
/**
* Resolve window insets in a version-aware manner. This can be used to apply padding to
* a view that properly
*/
val WindowInsets.systemBarsCompat: Rect get() { val WindowInsets.systemBarsCompat: Rect get() {
return when { return when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> { Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Hack to make MaterialShapeDrawable cooperate with RippleDrawable. See
CompactPlaybackView for more info. -->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?attr/colorControlHighlight">
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
<solid android:color="@android:color/white" />
</shape>
</item>
<item android:id="@android:id/background">
<shape android:shape="rectangle" />
</item>
</ripple>

View file

@ -32,7 +32,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipToPadding="false" android:clipToPadding="false"
app:layout_role="content"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
tools:listitem="@layout/item_detail" /> tools:listitem="@layout/item_detail" />

View file

@ -2,7 +2,7 @@
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout 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"
tools:context=".playback.CompactPlaybackFragment"> tools:context=".playback.CompactPlaybackView">
<data> <data>
@ -10,19 +10,17 @@
name="song" name="song"
type="org.oxycblt.auxio.music.Song" /> type="org.oxycblt.auxio.music.Song" />
<variable
name="playbackModel"
type="org.oxycblt.auxio.playback.PlaybackViewModel" />
</data> </data>
<androidx.constraintlayout.widget.ConstraintLayout <merge
android:id="@+id/playback_layout" android:id="@+id/playback_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:animateLayoutChanges="true" android:animateLayoutChanges="true"
android:background="@drawable/ui_background_ripple" android:background="@drawable/ui_background_ripple"
android:clickable="true" android:clickable="true"
android:focusable="true"> android:focusable="true"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<ImageView <ImageView
android:id="@+id/playback_cover" android:id="@+id/playback_cover"
@ -73,7 +71,6 @@
android:src="@drawable/sel_playing_state" android:src="@drawable/sel_playing_state"
android:layout_margin="@dimen/spacing_small" android:layout_margin="@dimen/spacing_small"
android:contentDescription="@string/desc_play_pause" android:contentDescription="@string/desc_play_pause"
android:onClick="@{() -> playbackModel.invertPlayingStatus()}"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
@ -82,12 +79,12 @@
android:id="@+id/playback_progress" android:id="@+id/playback_progress"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/size_stroke_large" android:layout_height="@dimen/size_stroke_large"
android:max="@{(int) song.seconds}"
style="@style/Widget.Auxio.ProgressBar" style="@style/Widget.Auxio.ProgressBar"
android:progress="@{playbackModel.positionAsProgress}"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
tools:progress="70" /> tools:progress="70" />
</androidx.constraintlayout.widget.ConstraintLayout> </merge>
</layout> </layout>

View file

@ -1,11 +0,0 @@
<?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:id="@+id/main_playback"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorSurface"
android:clipToPadding="true"
android:elevation="@dimen/elevation_normal"
tools:layout="@layout/fragment_compact_playback" />

View file

@ -4,9 +4,4 @@
<attr name="entries" format="reference" /> <attr name="entries" format="reference" />
<attr name="entryValues" format="reference" /> <attr name="entryValues" format="reference" />
</declare-styleable> </declare-styleable>
<attr format="enum" name="layout_role">
<enum name="content" value="0" />
<enum name="floating" value="1" />
</attr>
</resources> </resources>

View file

@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<!-- This is for PlaybackBarLayout -->
<item name="playback_bar" type="id" />
<!-- This is for HomeFragment's AppBarLayout. Explanations for these can be found there. --> <!-- This is for HomeFragment's AppBarLayout. Explanations for these can be found there. -->
<item name="home_song_list" type="id" /> <item name="home_song_list" type="id" />
<item name="home_album_list" type="id" /> <item name="home_album_list" type="id" />