ui: further apply elevation overlays
Apply elevation overlays to CompactPlaybackFragment and dragged queue items. This makes elevation more noticable on dark mode.
This commit is contained in:
parent
0433abc5cb
commit
00b7e0cac3
6 changed files with 44 additions and 30 deletions
|
@ -27,9 +27,9 @@ import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import org.oxycblt.auxio.databinding.FragmentMainBinding
|
import org.oxycblt.auxio.databinding.FragmentMainBinding
|
||||||
import org.oxycblt.auxio.music.Song
|
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.util.applyEdge
|
import org.oxycblt.auxio.util.applyEdge
|
||||||
|
import org.oxycblt.auxio.util.applyMaterialDrawable
|
||||||
import org.oxycblt.auxio.util.logD
|
import org.oxycblt.auxio.util.logD
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,29 +51,24 @@ class MainFragment : Fragment() {
|
||||||
binding.lifecycleOwner = viewLifecycleOwner
|
binding.lifecycleOwner = viewLifecycleOwner
|
||||||
|
|
||||||
binding.applyEdge { bars ->
|
binding.applyEdge { bars ->
|
||||||
binding.root.updatePadding(bottom = bars.bottom)
|
binding.mainPlayback.updatePadding(bottom = bars.bottom)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding.mainPlayback.applyMaterialDrawable()
|
||||||
|
|
||||||
// --- VIEWMODEL SETUP ---
|
// --- VIEWMODEL SETUP ---
|
||||||
|
|
||||||
playbackModel.setupPlayback(requireContext())
|
playbackModel.setupPlayback(requireContext())
|
||||||
|
|
||||||
// Change CompactPlaybackFragment's visibility here so that an animation occurs.
|
// Change CompactPlaybackFragment's visibility here so that an animation occurs.
|
||||||
handleCompactPlaybackVisibility(binding, playbackModel.song.value)
|
binding.mainPlayback.isVisible = playbackModel.song.value != null
|
||||||
|
|
||||||
playbackModel.song.observe(viewLifecycleOwner) { song ->
|
playbackModel.song.observe(viewLifecycleOwner) { song ->
|
||||||
handleCompactPlaybackVisibility(binding, song)
|
binding.mainPlayback.isVisible = song != null
|
||||||
}
|
}
|
||||||
|
|
||||||
logD("Fragment Created.")
|
logD("Fragment Created.")
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle the visibility of CompactPlaybackFragment. Done here so that there's a nice animation.
|
|
||||||
*/
|
|
||||||
private fun handleCompactPlaybackVisibility(binding: FragmentMainBinding, song: Song?) {
|
|
||||||
binding.mainPlayback.isVisible = song != null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.ui.BaseViewHolder
|
import org.oxycblt.auxio.ui.BaseViewHolder
|
||||||
import org.oxycblt.auxio.ui.DiffCallback
|
import org.oxycblt.auxio.ui.DiffCallback
|
||||||
import org.oxycblt.auxio.ui.HeaderViewHolder
|
import org.oxycblt.auxio.ui.HeaderViewHolder
|
||||||
|
import org.oxycblt.auxio.util.applyMaterialDrawable
|
||||||
import org.oxycblt.auxio.util.inflater
|
import org.oxycblt.auxio.util.inflater
|
||||||
import org.oxycblt.auxio.util.logE
|
import org.oxycblt.auxio.util.logE
|
||||||
|
|
||||||
|
@ -158,6 +159,10 @@ class QueueAdapter(
|
||||||
private val binding: ItemQueueSongBinding,
|
private val binding: ItemQueueSongBinding,
|
||||||
) : BaseViewHolder<Song>(binding) {
|
) : BaseViewHolder<Song>(binding) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
binding.root.applyMaterialDrawable()
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
override fun onBind(data: Song) {
|
override fun onBind(data: Song) {
|
||||||
binding.song = data
|
binding.song = data
|
||||||
|
|
|
@ -20,10 +20,9 @@ package org.oxycblt.auxio.playback.queue
|
||||||
|
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.view.animation.AccelerateDecelerateInterpolator
|
import android.view.animation.AccelerateDecelerateInterpolator
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.shape.MaterialShapeDrawable
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
@ -36,13 +35,8 @@ import kotlin.math.sign
|
||||||
* of the UI magic that makes up the queue UI.
|
* of the UI magic that makes up the queue UI.
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
class QueueDragCallback(
|
class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouchHelper.Callback() {
|
||||||
private val playbackModel: PlaybackViewModel,
|
|
||||||
private val coordinator: CoordinatorLayout,
|
|
||||||
private val appBar: AppBarLayout
|
|
||||||
) : ItemTouchHelper.Callback() {
|
|
||||||
private lateinit var queueAdapter: QueueAdapter
|
private lateinit var queueAdapter: QueueAdapter
|
||||||
private val tConsumed = IntArray(2)
|
|
||||||
private var shouldLift = true
|
private var shouldLift = true
|
||||||
|
|
||||||
override fun getMovementFlags(
|
override fun getMovementFlags(
|
||||||
|
@ -100,9 +94,12 @@ class QueueDragCallback(
|
||||||
val view = viewHolder.itemView
|
val view = viewHolder.itemView
|
||||||
|
|
||||||
if (shouldLift && isCurrentlyActive && actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
|
if (shouldLift && isCurrentlyActive && actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
|
||||||
|
val bg = view.background as MaterialShapeDrawable
|
||||||
|
|
||||||
view.animate()
|
view.animate()
|
||||||
.translationZ(view.resources.getDimension(R.dimen.elevation_small))
|
.translationZ(view.resources.getDimension(R.dimen.elevation_small))
|
||||||
.setDuration(100)
|
.setDuration(100)
|
||||||
|
.setUpdateListener { bg.elevation = view.translationZ }
|
||||||
.setInterpolator(AccelerateDecelerateInterpolator())
|
.setInterpolator(AccelerateDecelerateInterpolator())
|
||||||
.start()
|
.start()
|
||||||
|
|
||||||
|
@ -118,9 +115,12 @@ class QueueDragCallback(
|
||||||
val view = viewHolder.itemView
|
val view = viewHolder.itemView
|
||||||
|
|
||||||
if (view.translationZ != 0.0f) {
|
if (view.translationZ != 0.0f) {
|
||||||
|
val bg = view.background as MaterialShapeDrawable
|
||||||
|
|
||||||
view.animate()
|
view.animate()
|
||||||
.translationZ(0.0f)
|
.translationZ(0.0f)
|
||||||
.setDuration(100)
|
.setDuration(100)
|
||||||
|
.setUpdateListener { bg.elevation = view.translationZ }
|
||||||
.setInterpolator(AccelerateDecelerateInterpolator())
|
.setInterpolator(AccelerateDecelerateInterpolator())
|
||||||
.start()
|
.start()
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,11 +50,7 @@ class QueueFragment : Fragment() {
|
||||||
): View {
|
): View {
|
||||||
val binding = FragmentQueueBinding.inflate(inflater)
|
val binding = FragmentQueueBinding.inflate(inflater)
|
||||||
|
|
||||||
val callback = QueueDragCallback(
|
val callback = QueueDragCallback(playbackModel)
|
||||||
playbackModel,
|
|
||||||
binding.queueCoordinator,
|
|
||||||
binding.queueAppbar
|
|
||||||
)
|
|
||||||
|
|
||||||
val helper = ItemTouchHelper(callback)
|
val helper = ItemTouchHelper(callback)
|
||||||
val queueAdapter = QueueAdapter(helper, playbackModel)
|
val queueAdapter = QueueAdapter(helper, playbackModel)
|
||||||
|
|
|
@ -22,6 +22,7 @@ import android.content.Context
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
@ -35,8 +36,23 @@ import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.viewbinding.ViewBinding
|
import androidx.viewbinding.ViewBinding
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
|
import com.google.android.material.shape.MaterialShapeDrawable
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply a [MaterialShapeDrawable] to this view, automatically initializing the elevation overlay
|
||||||
|
* and setting the fill color. This assumes that the background is a [ColorDrawable] and will
|
||||||
|
* crash if not.
|
||||||
|
*/
|
||||||
|
fun View.applyMaterialDrawable() {
|
||||||
|
check(background is ColorDrawable) { "Background was not defined as a solid color" }
|
||||||
|
|
||||||
|
background = MaterialShapeDrawable.createWithElevationOverlay(context).apply {
|
||||||
|
elevation = this@applyMaterialDrawable.elevation
|
||||||
|
fillColor = ColorStateList.valueOf((background as ColorDrawable).color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply the recommended spans for a [RecyclerView].
|
* Apply the recommended spans for a [RecyclerView].
|
||||||
*
|
*
|
||||||
|
@ -65,6 +81,11 @@ fun RecyclerView.applySpans(shouldBeFullWidth: ((Int) -> Boolean)? = null) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether a recyclerview can scroll.
|
||||||
|
*/
|
||||||
|
fun RecyclerView.canScroll(): Boolean = computeVerticalScrollRange() > height
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable an image button.
|
* Disable an image button.
|
||||||
* TODO: Replace this fragile function with something else.
|
* TODO: Replace this fragile function with something else.
|
||||||
|
@ -75,10 +96,6 @@ fun ImageButton.disable() {
|
||||||
isEnabled = false
|
isEnabled = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Returns whether a recyclerview can scroll.
|
|
||||||
*/
|
|
||||||
fun RecyclerView.canScroll(): Boolean = computeVerticalScrollRange() > height
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve a color.
|
* Resolve a color.
|
||||||
|
|
|
@ -25,9 +25,10 @@
|
||||||
android:name="org.oxycblt.auxio.playback.CompactPlaybackFragment"
|
android:name="org.oxycblt.auxio.playback.CompactPlaybackFragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:elevation="@dimen/elevation_normal"
|
|
||||||
android:clipToPadding="true"
|
|
||||||
android:background="?attr/colorSurface"
|
android:background="?attr/colorSurface"
|
||||||
|
android:clipToPadding="true"
|
||||||
|
android:elevation="@dimen/elevation_normal"
|
||||||
tools:layout="@layout/fragment_compact_playback" />
|
tools:layout="@layout/fragment_compact_playback" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</layout>
|
</layout>
|
Loading…
Reference in a new issue