image: use shapeappearance in coverview

This commit is contained in:
Alexander Capehart 2024-04-19 10:26:53 -06:00
parent 6c640909f7
commit fc90d460dc
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
7 changed files with 52 additions and 49 deletions

View file

@ -33,9 +33,8 @@ import android.view.Gravity
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.ImageView import android.widget.ImageView
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.annotation.DimenRes
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.core.content.res.getIntOrThrow import androidx.annotation.Px
import androidx.core.graphics.drawable.DrawableCompat import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.view.children import androidx.core.view.children
import androidx.core.view.updateMarginsRelative import androidx.core.view.updateMarginsRelative
@ -45,6 +44,7 @@ import coil.request.ImageRequest
import coil.util.CoilUtils import coil.util.CoilUtils
import com.google.android.material.R as MR import com.google.android.material.R as MR
import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.shape.ShapeAppearanceModel
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject import javax.inject.Inject
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
@ -58,7 +58,6 @@ import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.ui.UISettings import org.oxycblt.auxio.ui.UISettings
import org.oxycblt.auxio.util.getAttrColorCompat import org.oxycblt.auxio.util.getAttrColorCompat
import org.oxycblt.auxio.util.getColorCompat import org.oxycblt.auxio.util.getColorCompat
import org.oxycblt.auxio.util.getDimen
import org.oxycblt.auxio.util.getDimenPixels import org.oxycblt.auxio.util.getDimenPixels
import org.oxycblt.auxio.util.getDrawableCompat import org.oxycblt.auxio.util.getDrawableCompat
import org.oxycblt.auxio.util.getInteger import org.oxycblt.auxio.util.getInteger
@ -91,16 +90,15 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
private val playbackIndicator: PlaybackIndicator? private val playbackIndicator: PlaybackIndicator?
private val selectionBadge: ImageView? private val selectionBadge: ImageView?
private val iconSize: Int?
private val sizing: Int
@DimenRes private val iconSizeRes: Int?
@DimenRes private var cornerRadiusRes: Int?
private var fadeAnimator: ValueAnimator? = null private var fadeAnimator: ValueAnimator? = null
private val indicatorMatrix = Matrix() private val indicatorMatrix = Matrix()
private val indicatorMatrixSrc = RectF() private val indicatorMatrixSrc = RectF()
private val indicatorMatrixDst = RectF() private val indicatorMatrixDst = RectF()
private val shapeAppearance: ShapeAppearanceModel
private data class Cover( private data class Cover(
val songs: Collection<Song>, val songs: Collection<Song>,
val desc: String, val desc: String,
@ -114,9 +112,21 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
@SuppressLint("CustomViewStyleable") @SuppressLint("CustomViewStyleable")
val styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.CoverView) val styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.CoverView)
sizing = styledAttrs.getIntOrThrow(R.styleable.CoverView_sizing) val shapeAppearanceRes = styledAttrs.getResourceId(R.styleable.CoverView_shapeAppearance, 0)
iconSizeRes = SIZING_ICON_SIZE[sizing] shapeAppearance =
cornerRadiusRes = getCornerRadiusRes() if (shapeAppearanceRes != 0) {
ShapeAppearanceModel.builder(context, shapeAppearanceRes, -1).build()
} else {
ShapeAppearanceModel.builder(
context,
com.google.android.material.R.style.ShapeAppearance_Material3_Corner_Medium,
-1)
.build()
}
iconSize =
styledAttrs.getDimensionPixelSize(R.styleable.CoverView_iconSize, -1).takeIf {
it != -1
}
val playbackIndicatorEnabled = val playbackIndicatorEnabled =
styledAttrs.getBoolean(R.styleable.CoverView_enablePlaybackIndicator, true) styledAttrs.getBoolean(R.styleable.CoverView_enablePlaybackIndicator, true)
@ -190,7 +200,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
// AnimatedVectorDrawable cannot be placed in a StyledDrawable, we must replicate the // AnimatedVectorDrawable cannot be placed in a StyledDrawable, we must replicate the
// behavior with a matrix. // behavior with a matrix.
val playbackIndicator = (playbackIndicator ?: return).view val playbackIndicator = (playbackIndicator ?: return).view
val iconSize = iconSizeRes?.let(context::getDimenPixels) ?: (measuredWidth / 2) val iconSize = iconSize ?: (measuredWidth / 2)
playbackIndicator.apply { playbackIndicator.apply {
imageMatrix = imageMatrix =
indicatorMatrix.apply { indicatorMatrix.apply {
@ -254,14 +264,8 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
} }
} }
private fun getCornerRadiusRes() =
if (!isInEditMode && uiSettings.roundMode) {
SIZING_CORNER_RADII[sizing]
} else {
null
}
private fun applyBackgroundsToChildren() { private fun applyBackgroundsToChildren() {
// Add backgrounds to each child for visual consistency // Add backgrounds to each child for visual consistency
for (child in children) { for (child in children) {
child.apply { child.apply {
@ -271,7 +275,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
background = background =
MaterialShapeDrawable().apply { MaterialShapeDrawable().apply {
fillColor = context.getColorCompat(R.color.sel_cover_bg) fillColor = context.getColorCompat(R.color.sel_cover_bg)
setCornerSize(cornerRadiusRes?.let(context::getDimen) ?: 0f) shapeAppearanceModel = shapeAppearance
} }
} }
} }
@ -402,11 +406,13 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
val request = val request =
ImageRequest.Builder(context) ImageRequest.Builder(context)
.data(songs) .data(songs)
.error(StyledDrawable(context, context.getDrawableCompat(errorRes), iconSizeRes)) .error(StyledDrawable(context, context.getDrawableCompat(errorRes), iconSize))
.target(image) .target(image)
val cornersTransformation = val cornersTransformation =
RoundedRectTransformation(cornerRadiusRes?.let(context::getDimen) ?: 0f) RoundedRectTransformation(
shapeAppearance.topLeftCornerSize.getCornerSize(
RectF(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat())))
if (imageSettings.forceSquareCovers) { if (imageSettings.forceSquareCovers) {
request.transformations(SquareCropTransformation.INSTANCE, cornersTransformation) request.transformations(SquareCropTransformation.INSTANCE, cornersTransformation)
} else { } else {
@ -427,7 +433,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
private class StyledDrawable( private class StyledDrawable(
context: Context, context: Context,
private val inner: Drawable, private val inner: Drawable,
@DimenRes iconSizeRes: Int? @Px val iconSize: Int?
) : Drawable() { ) : Drawable() {
init { init {
// Re-tint the drawable to use the analogous "on surface" color for // Re-tint the drawable to use the analogous "on surface" color for
@ -435,12 +441,10 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
DrawableCompat.setTintList(inner, context.getColorCompat(R.color.sel_on_cover_bg)) DrawableCompat.setTintList(inner, context.getColorCompat(R.color.sel_on_cover_bg))
} }
private val dimen = iconSizeRes?.let(context::getDimenPixels)
override fun draw(canvas: Canvas) { override fun draw(canvas: Canvas) {
// Resize the drawable such that it's always 1/4 the size of the image and // Resize the drawable such that it's always 1/4 the size of the image and
// centered in the middle of the canvas. // centered in the middle of the canvas.
val adj = dimen?.let { (bounds.width() - it) / 2 } ?: (bounds.width() / 4) val adj = iconSize?.let { (bounds.width() - it) / 2 } ?: (bounds.width() / 4)
inner.bounds.set(adj, adj, bounds.width() - adj, bounds.height() - adj) inner.bounds.set(adj, adj, bounds.width() - adj, bounds.height() - adj)
inner.draw(canvas) inner.draw(canvas)
} }
@ -457,13 +461,4 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
override fun getOpacity(): Int = PixelFormat.TRANSLUCENT override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
} }
companion object {
val SIZING_CORNER_RADII =
arrayOf(
R.dimen.size_corners_small,
R.dimen.size_corners_medium,
R.dimen.size_corners_mid_large)
val SIZING_ICON_SIZE = arrayOf(R.dimen.size_icon_small, R.dimen.size_icon_medium, null)
}
} }

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?attr/colorPrimaryContainer" android:state_selected="true" /> <item android:color="?attr/colorPrimaryContainer" android:state_selected="true" />
<item android:color="?attr/colorOnSurfaceInverse" /> <item android:color="?attr/colorSurfaceContainer" />
</selector> </selector>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?attr/colorOnPrimaryContainer" android:state_selected="true" /> <item android:color="?attr/colorOnPrimaryContainer" android:state_selected="true" />
<item android:color="?attr/colorSurfaceInverse" /> <item android:color="?attr/colorOnSurface" />
</selector> </selector>

View file

@ -24,7 +24,7 @@
<org.oxycblt.auxio.image.CoverView <org.oxycblt.auxio.image.CoverView
android:id="@+id/menu_cover" android:id="@+id/menu_cover"
style="@style/Widget.Auxio.Image.Full" style="@style/Widget.Auxio.Image.MidFull"
android:layout_marginStart="@dimen/spacing_medium" android:layout_marginStart="@dimen/spacing_medium"
android:layout_marginBottom="@dimen/spacing_medium" android:layout_marginBottom="@dimen/spacing_medium"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"

View file

@ -11,12 +11,8 @@
<integer name="anim_fade_exit_duration">100</integer> <integer name="anim_fade_exit_duration">100</integer>
<declare-styleable name="CoverView"> <declare-styleable name="CoverView">
<attr name="cornerRadius" format="dimension" /> <attr name="shapeAppearance" format="reference" />
<attr name="sizing" format="enum"> <attr name="iconSize" format="dimension" />
<enum name="small" value="0" />
<enum name="medium" value="1" />
<enum name="large" value="2" />
</attr>
<attr name="enablePlaybackIndicator" format="boolean" /> <attr name="enablePlaybackIndicator" format="boolean" />
<attr name="enableSelectionBadge" format="boolean" /> <attr name="enableSelectionBadge" format="boolean" />
</declare-styleable> </declare-styleable>

View file

@ -29,6 +29,8 @@
<dimen name="size_icon_small">24dp</dimen> <dimen name="size_icon_small">24dp</dimen>
<dimen name="size_icon_medium">32dp</dimen> <dimen name="size_icon_medium">32dp</dimen>
<dimen name="size_icon_large">40dp</dimen>
<dimen name="size_icon_huge">48dp</dimen>
<dimen name="size_pre_amp_ticker">56dp</dimen> <dimen name="size_pre_amp_ticker">56dp</dimen>

View file

@ -66,38 +66,48 @@
<style name="Widget.Auxio.Image.Small" parent=""> <style name="Widget.Auxio.Image.Small" parent="">
<item name="android:layout_width">@dimen/size_cover_compact</item> <item name="android:layout_width">@dimen/size_cover_compact</item>
<item name="android:layout_height">@dimen/size_cover_compact</item> <item name="android:layout_height">@dimen/size_cover_compact</item>
<item name="sizing">small</item> <item name="shapeAppearance">@style/ShapeAppearance.Material3.Corner.Medium</item>
<item name="iconSize">@dimen/size_icon_small</item>
</style> </style>
<style name="Widget.Auxio.Image.Medium" parent=""> <style name="Widget.Auxio.Image.Medium" parent="">
<item name="android:layout_width">@dimen/size_cover_medium</item> <item name="android:layout_width">@dimen/size_cover_medium</item>
<item name="android:layout_height">@dimen/size_cover_medium</item> <item name="android:layout_height">@dimen/size_cover_medium</item>
<item name="sizing">medium</item> <item name="shapeAppearance">@style/ShapeAppearance.Material3.Corner.Medium</item>
<item name="iconSize">@dimen/size_icon_medium</item>
</style> </style>
<style name="Widget.Auxio.Image.Large" parent=""> <style name="Widget.Auxio.Image.Large" parent="">
<item name="android:layout_width">@dimen/size_cover_large</item> <item name="android:layout_width">@dimen/size_cover_large</item>
<item name="android:layout_height">@dimen/size_cover_large</item> <item name="android:layout_height">@dimen/size_cover_large</item>
<item name="sizing">large</item> <item name="shapeAppearance">@style/ShapeAppearance.Material3.Corner.ExtraLarge</item>
</style> </style>
<style name="Widget.Auxio.Image.MidHuge" parent=""> <style name="Widget.Auxio.Image.MidHuge" parent="">
<item name="android:layout_width">@dimen/size_cover_mid_huge</item> <item name="android:layout_width">@dimen/size_cover_mid_huge</item>
<item name="android:layout_height">@dimen/size_cover_mid_huge</item> <item name="android:layout_height">@dimen/size_cover_mid_huge</item>
<item name="sizing">large</item> <item name="shapeAppearance">@style/ShapeAppearance.Material3.Corner.ExtraLarge</item>
</style> </style>
<style name="Widget.Auxio.Image.Huge" parent=""> <style name="Widget.Auxio.Image.Huge" parent="">
<item name="android:layout_width">@dimen/size_cover_huge</item> <item name="android:layout_width">@dimen/size_cover_huge</item>
<item name="android:layout_height">@dimen/size_cover_huge</item> <item name="android:layout_height">@dimen/size_cover_huge</item>
<item name="sizing">large</item> <item name="shapeAppearance">@style/ShapeAppearance.Material3.Corner.ExtraLarge</item>
</style>
<style name="Widget.Auxio.Image.MidFull" parent="">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">0dp</item>
<item name="layout_constraintDimensionRatio">1</item>
<item name="shapeAppearance">@style/ShapeAppearance.Material3.Corner.Large</item>
</style> </style>
<style name="Widget.Auxio.Image.Full" parent=""> <style name="Widget.Auxio.Image.Full" parent="">
<item name="android:layout_width">0dp</item> <item name="android:layout_width">0dp</item>
<item name="android:layout_height">0dp</item> <item name="android:layout_height">0dp</item>
<item name="layout_constraintDimensionRatio">1</item> <item name="layout_constraintDimensionRatio">1</item>
<item name="sizing">large</item> <item name="shapeAppearance">@style/ShapeAppearance.Material3.Corner.ExtraLarge</item>
</style> </style>
<style name="Widget.Auxio.RecyclerView.Linear" parent=""> <style name="Widget.Auxio.RecyclerView.Linear" parent="">