diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt index e25d0d739..de1195df2 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt @@ -199,8 +199,6 @@ class DetailViewModel(application: Application) : null } - // Ensure that we don't include the functionally useless - // "audio/raw" mime type MimeType(song.mimeType.fromExtension, formatMimeType) } diff --git a/app/src/main/java/org/oxycblt/auxio/image/BaseStyledImageView.kt b/app/src/main/java/org/oxycblt/auxio/image/BaseStyledImageView.kt deleted file mode 100644 index 7d8e800ad..000000000 --- a/app/src/main/java/org/oxycblt/auxio/image/BaseStyledImageView.kt +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2022 Auxio Project - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.oxycblt.auxio.image - -import android.content.Context -import android.graphics.Canvas -import android.graphics.ColorFilter -import android.graphics.PixelFormat -import android.graphics.drawable.Drawable -import android.util.AttributeSet -import androidx.annotation.AttrRes -import androidx.annotation.DrawableRes -import androidx.appcompat.widget.AppCompatImageView -import androidx.core.graphics.drawable.DrawableCompat -import coil.dispose -import coil.load -import com.google.android.material.shape.MaterialShapeDrawable -import org.oxycblt.auxio.R -import org.oxycblt.auxio.music.Album -import org.oxycblt.auxio.music.Artist -import org.oxycblt.auxio.music.Genre -import org.oxycblt.auxio.music.Music -import org.oxycblt.auxio.music.Song -import org.oxycblt.auxio.util.getColorStateListSafe -import org.oxycblt.auxio.util.getDrawableSafe - -/** - * The base class for Auxio's images. Do not use this class outside of this module. - * - * Default behavior includes the addition of a tonal background and automatic icon sizing. Other - * behavior is implemented by [StyledImageView] and [ImageGroup]. - * - * @author OxygenCobalt - */ -open class BaseStyledImageView -@JvmOverloads -constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr: Int = 0) : - AppCompatImageView(context, attrs, defStyleAttr) { - private var staticIcon = 0 - - init { - val styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.StyledImageView) - staticIcon = styledAttrs.getResourceId(R.styleable.StyledImageView_staticIcon, -1) - styledAttrs.recycle() - - if (staticIcon > -1) { - @Suppress("LeakingThis") - setImageDrawable(StyledDrawable(context, context.getDrawableSafe(staticIcon))) - } - - background = - MaterialShapeDrawable().apply { - fillColor = context.getColorStateListSafe(R.color.sel_cover_bg) - } - } - - /** Bind the album cover for a [song]. */ - open fun bind(song: Song) = loadImpl(song, R.drawable.ic_song) - - /** Bind the album cover for an [album]. */ - open fun bind(album: Album) = loadImpl(album, R.drawable.ic_album) - - /** Bind the image for an [artist] */ - open fun bind(artist: Artist) = loadImpl(artist, R.drawable.ic_artist) - - /** Bind the image for a [genre] */ - open fun bind(genre: Genre) = loadImpl(genre, R.drawable.ic_genre) - - private fun loadImpl(music: T, @DrawableRes error: Int) { - if (staticIcon > -1) { - throw IllegalStateException("Static StyledImageViews cannot bind new images") - } - - dispose() - load(music) { - error(StyledDrawable(context, error)) - transformations(SquareFrameTransform.INSTANCE) - } - } - - /** - * A companion drawable that can be used with the style that [StyledImageView] provides. - * @author OxygenCobalt - */ - class StyledDrawable(context: Context, private val src: Drawable) : Drawable() { - constructor( - context: Context, - @DrawableRes res: Int - ) : this(context, context.getDrawableSafe(res)) - - init { - // Re-tint the drawable to something that will play along with the background - DrawableCompat.setTintList(src, context.getColorStateListSafe(R.color.sel_on_cover_bg)) - } - - override fun draw(canvas: Canvas) { - src.bounds.set(canvas.clipBounds) - val adjustWidth = src.bounds.width() / 4 - val adjustHeight = src.bounds.height() / 4 - src.bounds.set( - adjustWidth, - adjustHeight, - src.bounds.width() - adjustWidth, - src.bounds.height() - adjustHeight) - src.draw(canvas) - } - - override fun setAlpha(alpha: Int) { - src.alpha = alpha - } - - override fun setColorFilter(colorFilter: ColorFilter?) { - src.colorFilter = colorFilter - } - - override fun getOpacity(): Int = PixelFormat.TRANSLUCENT - } -} diff --git a/app/src/main/java/org/oxycblt/auxio/image/ImageGroup.kt b/app/src/main/java/org/oxycblt/auxio/image/ImageGroup.kt index 905f0a3b9..7432903f9 100644 --- a/app/src/main/java/org/oxycblt/auxio/image/ImageGroup.kt +++ b/app/src/main/java/org/oxycblt/auxio/image/ImageGroup.kt @@ -29,7 +29,6 @@ import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Song -import org.oxycblt.auxio.settings.SettingsManager import org.oxycblt.auxio.util.getColorStateListSafe import org.oxycblt.auxio.util.getDrawableSafe @@ -52,13 +51,9 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr FrameLayout(context, attrs, defStyleAttr) { private val cornerRadius: Float - private val inner = BaseStyledImageView(context, attrs) + private val inner: StyledImageView private var customView: View? = null - private val indicator = - BaseStyledImageView(context).apply { - setImageDrawable( - StyledDrawable(context, context.getDrawableSafe(R.drawable.ic_equalizer))) - } + private val indicator: StyledImageView init { // Android wants you to make separate attributes for each view type, but will @@ -68,23 +63,14 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr cornerRadius = styledAttrs.getDimension(R.styleable.StyledImageView_cornerRadius, 0f) styledAttrs.recycle() - addView(inner) - - // Use clipToOutline and a background drawable to crop images. While Coil's transformation - // could theoretically be used to round corners, the corner radius is dependent on the - // dimensions of the image, which will result in inconsistent corners across different - // album covers unless we resize all covers to be the same size. clipToOutline is both - // cheaper and more elegant. As a side-note, this also allows us to re-use the same - // background for both the tonal background color and the corner rounding. - background = MaterialShapeDrawable() - clipToOutline = true - - if (!isInEditMode) { - val settingsManager = SettingsManager.getInstance() - if (settingsManager.roundCovers) { - (background as MaterialShapeDrawable).setCornerSize(cornerRadius) + inner = StyledImageView(context, attrs) + indicator = + StyledImageView(context).apply { + cornerRadius = this@ImageGroup.cornerRadius + staticIcon = context.getDrawableSafe(R.drawable.ic_equalizer) } - } + + addView(inner) } override fun onFinishInflate() { @@ -99,6 +85,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr background = MaterialShapeDrawable().apply { fillColor = context.getColorStateListSafe(R.color.sel_cover_bg) + setCornerSize(cornerRadius) } } diff --git a/app/src/main/java/org/oxycblt/auxio/image/StyledImageView.kt b/app/src/main/java/org/oxycblt/auxio/image/StyledImageView.kt index 87dde24b3..bc90edb2e 100644 --- a/app/src/main/java/org/oxycblt/auxio/image/StyledImageView.kt +++ b/app/src/main/java/org/oxycblt/auxio/image/StyledImageView.kt @@ -18,16 +18,25 @@ package org.oxycblt.auxio.image import android.content.Context +import android.graphics.drawable.Drawable import android.util.AttributeSet import androidx.annotation.AttrRes +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes import androidx.appcompat.widget.AppCompatImageView +import androidx.core.content.res.ResourcesCompat +import coil.dispose +import coil.load import com.google.android.material.shape.MaterialShapeDrawable import org.oxycblt.auxio.R import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Genre +import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.settings.SettingsManager +import org.oxycblt.auxio.util.getColorStateListSafe +import org.oxycblt.auxio.util.getDrawableSafe /** * An [AppCompatImageView] that applies many of the stylistic choices that Auxio uses regarding @@ -41,14 +50,27 @@ import org.oxycblt.auxio.settings.SettingsManager class StyledImageView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr: Int = 0) : - BaseStyledImageView(context, attrs, defStyleAttr) { - private var cornerRadius = 0f + AppCompatImageView(context, attrs, defStyleAttr) { + var cornerRadius = 0f + set(value) { + field = value + (background as? MaterialShapeDrawable)?.let { bg -> + if (!isInEditMode && SettingsManager.getInstance().roundCovers) { + bg.setCornerSize(value) + } else { + bg.setCornerSize(0f) + } + } + } + + var staticIcon: Drawable? = null + set(value) { + val wrapped = value?.let { StyledDrawable(context, it) } + field = wrapped + setImageDrawable(field) + } init { - val styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.StyledImageView) - cornerRadius = styledAttrs.getDimension(R.styleable.StyledImageView_cornerRadius, 0f) - styledAttrs.recycle() - // Use clipToOutline and a background drawable to crop images. While Coil's transformation // could theoretically be used to round corners, the corner radius is dependent on the // dimensions of the image, which will result in inconsistent corners across different @@ -56,36 +78,47 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr // cheaper and more elegant. As a side-note, this also allows us to re-use the same // background for both the tonal background color and the corner rounding. clipToOutline = true - - if (!isInEditMode) { - val settingsManager = SettingsManager.getInstance() - if (settingsManager.roundCovers) { - (background as MaterialShapeDrawable).setCornerSize(cornerRadius) + background = + MaterialShapeDrawable().apply { + fillColor = context.getColorStateListSafe(R.color.sel_cover_bg) + setCornerSize(cornerRadius) } + + val styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.StyledImageView) + val staticIconRes = + styledAttrs.getResourceId( + R.styleable.StyledImageView_staticIcon, ResourcesCompat.ID_NULL) + if (staticIconRes != ResourcesCompat.ID_NULL) { + staticIcon = context.getDrawableSafe(staticIconRes) + } + + cornerRadius = styledAttrs.getDimension(R.styleable.StyledImageView_cornerRadius, 0f) + styledAttrs.recycle() + } + + /** Bind the album cover for a [song]. */ + fun bind(song: Song) = loadImpl(song, R.drawable.ic_song, R.string.desc_album_cover) + + /** Bind the album cover for an [album]. */ + fun bind(album: Album) = loadImpl(album, R.drawable.ic_album, R.string.desc_album_cover) + + /** Bind the image for an [artist] */ + fun bind(artist: Artist) = loadImpl(artist, R.drawable.ic_artist, R.string.desc_artist_image) + + /** Bind the image for a [genre] */ + fun bind(genre: Genre) = loadImpl(genre, R.drawable.ic_genre, R.string.desc_genre_image) + + private fun loadImpl(music: T, @DrawableRes error: Int, @StringRes desc: Int) { + if (staticIcon != null) { + error("Static StyledImageViews cannot bind new images") + } + + contentDescription = context.getString(desc, music.resolveName(context)) + + dispose() + load(music) { + error(StyledDrawable(context, context.getDrawableSafe(error))) + transformations(SquareFrameTransform.INSTANCE) } } - - override fun bind(song: Song) { - super.bind(song) - contentDescription = - context.getString(R.string.desc_album_cover, song.album.resolveName(context)) - } - - override fun bind(album: Album) { - super.bind(album) - contentDescription = - context.getString(R.string.desc_album_cover, album.resolveName(context)) - } - - override fun bind(artist: Artist) { - super.bind(artist) - contentDescription = - context.getString(R.string.desc_artist_image, artist.resolveName(context)) - } - - override fun bind(genre: Genre) { - super.bind(genre) - contentDescription = - context.getString(R.string.desc_genre_image, genre.resolveName(context)) - } }