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.ImageView
import androidx.annotation.AttrRes
import androidx.annotation.DimenRes
import androidx.annotation.DrawableRes
import androidx.core.content.res.getIntOrThrow
import androidx.annotation.Px
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.view.children
import androidx.core.view.updateMarginsRelative
@ -45,6 +44,7 @@ import coil.request.ImageRequest
import coil.util.CoilUtils
import com.google.android.material.R as MR
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.shape.ShapeAppearanceModel
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
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.util.getAttrColorCompat
import org.oxycblt.auxio.util.getColorCompat
import org.oxycblt.auxio.util.getDimen
import org.oxycblt.auxio.util.getDimenPixels
import org.oxycblt.auxio.util.getDrawableCompat
import org.oxycblt.auxio.util.getInteger
@ -91,16 +90,15 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
private val playbackIndicator: PlaybackIndicator?
private val selectionBadge: ImageView?
private val sizing: Int
@DimenRes private val iconSizeRes: Int?
@DimenRes private var cornerRadiusRes: Int?
private val iconSize: Int?
private var fadeAnimator: ValueAnimator? = null
private val indicatorMatrix = Matrix()
private val indicatorMatrixSrc = RectF()
private val indicatorMatrixDst = RectF()
private val shapeAppearance: ShapeAppearanceModel
private data class Cover(
val songs: Collection<Song>,
val desc: String,
@ -114,9 +112,21 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
@SuppressLint("CustomViewStyleable")
val styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.CoverView)
sizing = styledAttrs.getIntOrThrow(R.styleable.CoverView_sizing)
iconSizeRes = SIZING_ICON_SIZE[sizing]
cornerRadiusRes = getCornerRadiusRes()
val shapeAppearanceRes = styledAttrs.getResourceId(R.styleable.CoverView_shapeAppearance, 0)
shapeAppearance =
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 =
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
// behavior with a matrix.
val playbackIndicator = (playbackIndicator ?: return).view
val iconSize = iconSizeRes?.let(context::getDimenPixels) ?: (measuredWidth / 2)
val iconSize = iconSize ?: (measuredWidth / 2)
playbackIndicator.apply {
imageMatrix =
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() {
// Add backgrounds to each child for visual consistency
for (child in children) {
child.apply {
@ -271,7 +275,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
background =
MaterialShapeDrawable().apply {
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 =
ImageRequest.Builder(context)
.data(songs)
.error(StyledDrawable(context, context.getDrawableCompat(errorRes), iconSizeRes))
.error(StyledDrawable(context, context.getDrawableCompat(errorRes), iconSize))
.target(image)
val cornersTransformation =
RoundedRectTransformation(cornerRadiusRes?.let(context::getDimen) ?: 0f)
RoundedRectTransformation(
shapeAppearance.topLeftCornerSize.getCornerSize(
RectF(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat())))
if (imageSettings.forceSquareCovers) {
request.transformations(SquareCropTransformation.INSTANCE, cornersTransformation)
} else {
@ -427,7 +433,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
private class StyledDrawable(
context: Context,
private val inner: Drawable,
@DimenRes iconSizeRes: Int?
@Px val iconSize: Int?
) : Drawable() {
init {
// 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))
}
private val dimen = iconSizeRes?.let(context::getDimenPixels)
override fun draw(canvas: Canvas) {
// Resize the drawable such that it's always 1/4 the size of the image and
// 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.draw(canvas)
}
@ -457,13 +461,4 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
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"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?attr/colorPrimaryContainer" android:state_selected="true" />
<item android:color="?attr/colorOnSurfaceInverse" />
<item android:color="?attr/colorSurfaceContainer" />
</selector>

View file

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

View file

@ -24,7 +24,7 @@
<org.oxycblt.auxio.image.CoverView
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_marginBottom="@dimen/spacing_medium"
app:layout_constraintStart_toStartOf="parent"

View file

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

View file

@ -29,6 +29,8 @@
<dimen name="size_icon_small">24dp</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>

View file

@ -66,38 +66,48 @@
<style name="Widget.Auxio.Image.Small" parent="">
<item name="android:layout_width">@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 name="Widget.Auxio.Image.Medium" parent="">
<item name="android:layout_width">@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 name="Widget.Auxio.Image.Large" parent="">
<item name="android:layout_width">@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 name="Widget.Auxio.Image.MidHuge" parent="">
<item name="android:layout_width">@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 name="Widget.Auxio.Image.Huge" parent="">
<item name="android:layout_width">@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 name="Widget.Auxio.Image.Full" parent="">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">0dp</item>
<item name="layout_constraintDimensionRatio">1</item>
<item name="sizing">large</item>
<item name="shapeAppearance">@style/ShapeAppearance.Material3.Corner.ExtraLarge</item>
</style>
<style name="Widget.Auxio.RecyclerView.Linear" parent="">