image: use shapeappearance in coverview
This commit is contained in:
parent
6c640909f7
commit
fc90d460dc
7 changed files with 52 additions and 49 deletions
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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"
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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="">
|
||||||
|
|
Loading…
Reference in a new issue