Add artist images

Add artist images back, now with proper mosaics.
This commit is contained in:
OxygenCobalt 2020-09-07 09:34:19 -06:00
parent 016d664e51
commit a93c0f4af3
6 changed files with 177 additions and 11 deletions

View file

@ -3,12 +3,14 @@ package org.oxycblt.auxio.music
import android.content.ContentUris
import android.net.Uri
import android.provider.MediaStore
import android.util.Log
import android.widget.ImageView
import android.widget.TextView
import androidx.databinding.BindingAdapter
import coil.Coil
import coil.load
import coil.request.ImageRequest
import org.oxycblt.auxio.R
import org.oxycblt.auxio.music.coil.ArtistImageFetcher
import org.oxycblt.auxio.music.models.Album
import org.oxycblt.auxio.music.models.Artist
import org.oxycblt.auxio.music.models.Song
@ -82,8 +84,6 @@ fun TextView.getArtistCounts(artist: Artist) {
R.plurals.format_song_count, artist.numSongs, artist.numSongs
)
Log.d("getArtistCounts", albums)
text = context.getString(R.string.format_double_counts, albums, songs)
}
@ -102,6 +102,34 @@ fun ImageView.getCoverArt(album: Album) {
load(album.coverUri) {
crossfade(true)
placeholder(android.R.color.transparent)
error(R.drawable.ic_music)
error(R.drawable.ic_album)
}
}
@BindingAdapter("artistImage")
fun ImageView.getArtistImage(artist: Artist) {
if (artist.numAlbums >= 4) {
val uris = mutableListOf<Uri>()
for (i in 0..3) {
uris.add(artist.albums[i].coverUri)
}
val request = ImageRequest.Builder(context)
.data(uris)
.fetcher(ArtistImageFetcher(context))
.crossfade(true)
.placeholder(android.R.color.transparent)
.error(R.drawable.ic_artist)
.target(this)
.build()
Coil.imageLoader(context).enqueue(request)
} else {
load(artist.albums[0].coverUri) {
crossfade(true)
placeholder(android.R.color.transparent)
error(R.drawable.ic_music)
}
}
}

View file

@ -0,0 +1,97 @@
package org.oxycblt.auxio.music.coil
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.net.Uri
import androidx.core.graphics.drawable.toDrawable
import coil.bitmap.BitmapPool
import coil.decode.DataSource
import coil.decode.Options
import coil.fetch.DrawableResult
import coil.fetch.FetchResult
import coil.fetch.Fetcher
import coil.fetch.SourceResult
import coil.size.Size
import okio.buffer
import okio.source
import java.io.InputStream
const val MOSAIC_BITMAP_SIZE = 512
class ArtistImageFetcher(private val context: Context) : Fetcher<List<Uri>> {
override suspend fun fetch(
pool: BitmapPool,
data: List<Uri>,
size: Size,
options: Options
): FetchResult {
val streams = mutableListOf<InputStream>()
for (uri in data) {
val stream: InputStream? = context.contentResolver.openInputStream(uri)
if (stream != null) {
streams.add(stream)
}
}
// If so many streams failed that there's not enough images to make a mosaic, then
// just return the first album.
if (streams.size < 4) {
streams.forEach { it.close() }
return SourceResult(
source = streams[0].source().buffer(),
mimeType = context.contentResolver.getType(data[0]),
dataSource = DataSource.DISK
)
}
// Create the mosaic, code adapted from Phonograph.
// https://github.com/kabouzeid/Phonograph
val finalBitmap = Bitmap.createBitmap(
MOSAIC_BITMAP_SIZE, MOSAIC_BITMAP_SIZE, Bitmap.Config.RGB_565
)
val canvas = Canvas(finalBitmap)
var x = 0
var y = 0
val increment = MOSAIC_BITMAP_SIZE / 2
for (stream in streams) {
val bitmap = Bitmap.createScaledBitmap(
BitmapFactory.decodeStream(stream),
increment,
increment,
true
)
canvas.drawBitmap(bitmap, x.toFloat(), y.toFloat(), null)
x += increment
if (x == MOSAIC_BITMAP_SIZE) {
x = 0
y += increment
if (y == MOSAIC_BITMAP_SIZE) {
break
}
}
}
// Close all the streams when done.
streams.forEach { it.close() }
return DrawableResult(
drawable = finalBitmap.toDrawable(context.resources),
isSampled = false,
dataSource = DataSource.DISK
)
}
override fun key(data: List<Uri>): String? = data.toString()
}

View file

@ -154,10 +154,18 @@ class MusicSorter(
albums.forEach { if (it.name == "") it.name = albumPlaceholder }
}
// Sort all music into
// Sort all music
private fun finalizeMusic() {
genres.sortBy { it.name }
artists.sortBy { it.name }
albums.sortBy { it.name }
genres.sortWith(
compareBy(String.CASE_INSENSITIVE_ORDER, { it.name })
)
artists.sortWith(
compareBy(String.CASE_INSENSITIVE_ORDER, { it.name })
)
albums.sortWith(
compareBy(String.CASE_INSENSITIVE_ORDER, { it.name })
)
}
}

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorPrimary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,16.5c-2.49,0 -4.5,-2.01 -4.5,-4.5S9.51,7.5 12,7.5s4.5,2.01 4.5,4.5 -2.01,4.5 -4.5,4.5zM12,11c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1z" />
</vector>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorPrimary">
<path
android:fillColor="@android:color/white"
android:pathData="M9,11.75c-0.69,0 -1.25,0.56 -1.25,1.25s0.56,1.25 1.25,1.25 1.25,-0.56 1.25,-1.25 -0.56,-1.25 -1.25,-1.25zM15,11.75c-0.69,0 -1.25,0.56 -1.25,1.25s0.56,1.25 1.25,1.25 1.25,-0.56 1.25,-1.25 -0.56,-1.25 -1.25,-1.25zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8 0,-0.29 0.02,-0.58 0.05,-0.86 2.36,-1.05 4.23,-2.98 5.21,-5.37C11.07,8.33 14.05,10 17.42,10c0.78,0 1.53,-0.09 2.25,-0.26 0.21,0.71 0.33,1.47 0.33,2.26 0,4.41 -3.59,8 -8,8z" />
</vector>

View file

@ -18,7 +18,16 @@
android:focusable="true"
android:padding="@dimen/padding_medium">
<!-- TODO: Artist images -->
<ImageView
android:id="@+id/artist_image"
android:layout_width="@dimen/cover_size_normal"
android:layout_height="@dimen/cover_size_normal"
app:artistImage="@{artist}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@tools:sample/backgrounds/scenic"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/artist_name"
@ -27,11 +36,12 @@
android:ellipsize="end"
android:maxLines="1"
android:text="@{artist.name}"
android:layout_marginStart="@dimen/margin_medium"
android:textAppearance="?android:attr/textAppearanceListItem"
android:textColor="?android:attr/textColorPrimary"
app:layout_constraintBottom_toTopOf="@+id/album_song_count"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintStart_toEndOf="@+id/artist_image"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="Artist Name" />
@ -41,10 +51,11 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:layout_marginStart="@dimen/margin_medium"
android:textColor="?android:attr/textColorSecondary"
app:artistCounts="@{artist}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintStart_toEndOf="@+id/artist_image"
app:layout_constraintTop_toBottomOf="@+id/artist_name"
tools:text="2 Albums, 20 Songs" />