playback: animate playback bar

Make the playback bar animate when it's shown at runtime. This
completes the playback bar layout and honestly it looks amazing.
This commit is contained in:
OxygenCobalt 2021-11-01 20:01:46 -06:00
parent 282933fb37
commit 0a4b07e583
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
4 changed files with 99 additions and 19 deletions

View file

@ -64,7 +64,7 @@ dependencies {
// General
implementation "androidx.core:core-ktx:1.7.0"
implementation "androidx.activity:activity-ktx:1.3.1"
implementation "androidx.activity:activity-ktx:1.4.0"
implementation 'androidx.fragment:fragment-ktx:1.3.6'
// UI
@ -85,7 +85,7 @@ dependencies {
implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
// Media
// TODO: Migrate to media2 when I can figure out how to use it
// TODO: Migrate to Media3
implementation "androidx.media:media:1.4.3"
// Preferences

View file

@ -73,7 +73,7 @@ class MainFragment : Fragment(), PlaybackBarLayout.ActionCallback {
binding.mainBarLayout.setPosition(playbackModel.position.value!!)
playbackModel.song.observe(viewLifecycleOwner) { song ->
binding.mainBarLayout.setSong(song)
binding.mainBarLayout.setSong(song, animate = true)
}
playbackModel.isPlaying.observe(viewLifecycleOwner) { isPlaying ->

View file

@ -29,6 +29,7 @@ import androidx.annotation.AttrRes
import androidx.annotation.StyleRes
import androidx.core.view.children
import androidx.core.view.updatePadding
import androidx.customview.widget.ViewDragHelper
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.util.systemBarsCompat
@ -49,6 +50,7 @@ class PlaybackBarLayout @JvmOverloads constructor(
@StyleRes defStyleRes: Int = 0
) : ViewGroup(context, attrs, defStyleAttr, defStyleRes) {
private val playbackView = CompactPlaybackView(context)
private var barDragHelper = ViewDragHelper.create(this, ViewDragCallback())
private var lastInsets: WindowInsets? = null
init {
@ -76,7 +78,11 @@ class PlaybackBarLayout @JvmOverloads constructor(
playbackView.measure(barWidthSpec, barHeightSpec)
updateWindowInsets()
measureContent()
}
private fun measureContent() {
val barParams = playbackView.layoutParams as LayoutParams
val barHeightAdjusted = (playbackView.measuredHeight * barParams.offset).toInt()
val contentWidth = measuredWidth
@ -110,7 +116,17 @@ class PlaybackBarLayout @JvmOverloads constructor(
0, height - barHeightAdjusted,
width, height + (barHeight - barHeightAdjusted)
)
} else {
}
}
layoutContent()
}
private fun layoutContent() {
for (child in children) {
val childParams = child.layoutParams as LayoutParams
if (!childParams.isBar) {
child.layout(0, 0, child.measuredWidth, child.measuredHeight)
}
}
@ -125,6 +141,12 @@ class PlaybackBarLayout @JvmOverloads constructor(
return insets
}
override fun computeScroll() {
if (barDragHelper.continueSettling(true)) {
postInvalidateOnAnimation()
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
@ -174,12 +196,12 @@ class PlaybackBarLayout @JvmOverloads constructor(
}
}
fun setSong(song: Song?) {
fun setSong(song: Song?, animate: Boolean = false) {
if (song != null) {
showBar()
showBar(animate)
playbackView.setSong(song)
} else {
hideBar()
hideBar(animate)
}
}
@ -195,33 +217,53 @@ class PlaybackBarLayout @JvmOverloads constructor(
playbackView.setCallback(callback)
}
private fun showBar() {
private fun showBar(animate: Boolean) {
val barParams = playbackView.layoutParams as LayoutParams
if (barParams.offset == 1f) {
if (barParams.shown || barParams.offset == 1f) {
return
}
barParams.offset = 1f
barParams.shown = true
if (isLaidOut) {
updateWindowInsets()
if (animate) {
barDragHelper.smoothSlideViewTo(
playbackView, playbackView.left, height - playbackView.height
)
} else {
barParams.offset = 1f
if (isLaidOut) {
updateWindowInsets()
measureContent()
layoutContent()
}
}
invalidate()
}
private fun hideBar() {
private fun hideBar(animate: Boolean) {
val barParams = playbackView.layoutParams as LayoutParams
if (barParams.offset == 0f) {
if (barParams.shown || barParams.offset == 0f) {
return
}
barParams.offset = 0f
barParams.shown = false
if (isLaidOut) {
updateWindowInsets()
if (animate) {
barDragHelper.smoothSlideViewTo(
playbackView, playbackView.left, height
)
} else {
barParams.offset = 0f
if (isLaidOut) {
updateWindowInsets()
measureContent()
layoutContent()
}
}
invalidate()
@ -251,6 +293,7 @@ class PlaybackBarLayout @JvmOverloads constructor(
@Suppress("UNUSED")
class LayoutParams : ViewGroup.LayoutParams {
var isBar = false
var shown = false
var offset = 0f
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
@ -267,4 +310,42 @@ class PlaybackBarLayout @JvmOverloads constructor(
fun onNavToItem()
fun onNavToPlayback()
}
private inner class ViewDragCallback : ViewDragHelper.Callback() {
override fun tryCaptureView(child: View, pointerId: Int): Boolean = false
override fun onViewPositionChanged(
changedView: View,
left: Int,
top: Int,
dx: Int,
dy: Int
) {
val childRange = getViewVerticalDragRange(changedView)
val childLayoutParams = changedView.layoutParams as LayoutParams
val height = height
childLayoutParams.offset = (height - top).toFloat() / childRange
updateWindowInsets()
measureContent()
layoutContent()
}
override fun getViewVerticalDragRange(child: View): Int {
val childParams = child.layoutParams as LayoutParams
return if (childParams.isBar) {
child.height
} else {
0
}
}
override fun clampViewPositionHorizontal(child: View, left: Int, dx: Int): Int = child.left
override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int {
return top.coerceIn(height - getViewVerticalDragRange(child)..height)
}
}
}

View file

@ -179,8 +179,7 @@ class WidgetProvider : AppWidgetProvider() {
updateAppWidget(name, RemoteViews(views))
} else {
// Otherwise, we try our best to backport the responsive behavior to older versions.
// This is mostly a guess based on RemoteView's documentation. It seems to work well
// enough on most launchers.
// This seems to work well enough on most launchers.
// Each widget has independent dimensions, so we iterate through them all
// and do this for each.