coil: disable exoplayer loading

Disable exoplayer loading since apparently the way I use a blocking
call in the coil coroutine causes it to be uncancellable. This ends
up resulting in errors continuing to apply themselves to whatever
view they were requested from, even if it was an error. Because you
know. Android.
This commit is contained in:
OxygenCobalt 2021-11-16 06:35:05 -07:00
parent 1ea3ddb2e0
commit 71b15a3a6a
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
2 changed files with 92 additions and 94 deletions

View file

@ -27,11 +27,6 @@ import coil.fetch.FetchResult
import coil.fetch.Fetcher import coil.fetch.Fetcher
import coil.fetch.SourceResult import coil.fetch.SourceResult
import coil.size.Size import coil.size.Size
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.MediaMetadata
import com.google.android.exoplayer2.MetadataRetriever
import com.google.android.exoplayer2.metadata.flac.PictureFrame
import com.google.android.exoplayer2.metadata.id3.ApicFrame
import okio.buffer import okio.buffer
import okio.source import okio.source
import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Album
@ -39,9 +34,7 @@ import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.toAlbumArtURI import org.oxycblt.auxio.music.toAlbumArtURI
import org.oxycblt.auxio.music.toURI import org.oxycblt.auxio.music.toURI
import org.oxycblt.auxio.settings.SettingsManager import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.util.logD
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.lang.Exception
/** /**
* Fetcher that returns the album art for a given [Album]. Handles settings on whether to use * Fetcher that returns the album art for a given [Album]. Handles settings on whether to use
@ -91,14 +84,14 @@ class AlbumArtFetcher(private val context: Context) : Fetcher<Album> {
if (result != null) { if (result != null) {
return result return result
} }
//
// Our next fallback is to rely on ExoPlayer's largely half-baked and undocumented // // Our next fallback is to rely on ExoPlayer's largely half-baked and undocumented
// metadata system. // // metadata system.
val exoResult = fetchExoplayerCover(song) // val exoResult = fetchExoplayerCover(song)
//
if (exoResult != null) { // if (exoResult != null) {
return exoResult // return exoResult
} // }
// If the previous two failed, we resort to MediaStore's covers despite it literally // If the previous two failed, we resort to MediaStore's covers despite it literally
// going against the point of this setting. The previous two calls are just too unreliable // going against the point of this setting. The previous two calls are just too unreliable
@ -156,84 +149,86 @@ class AlbumArtFetcher(private val context: Context) : Fetcher<Album> {
return null return null
} }
private fun fetchExoplayerCover(song: Song): FetchResult? { // Disabled until I can figure out how the hell I can get a blocking call to play along in
val uri = song.id.toURI() // a suspend function. I doubt it's possible.
// private fun fetchExoplayerCover(song: Song): FetchResult? {
val future = MetadataRetriever.retrieveMetadata( // val uri = song.id.toURI()
context, MediaItem.fromUri(song.id.toURI()) //
) // val future = MetadataRetriever.retrieveMetadata(
// context, MediaItem.fromUri(song.id.toURI())
// Coil is async, we can just spin until the loading has ended // )
while (future.isDone) { /* no-op */ } //
// // Coil is async, we can just spin until the loading has ended
val tracks = try { // while (future.isDone) { /* no-op */ }
future.get() //
} catch (e: Exception) { // val tracks = try {
null // future.get()
} // } catch (e: Exception) {
// null
if (tracks == null || tracks.isEmpty) { // }
// Unrecognized format. This is expected, as ExoPlayer only supports a //
// subset of formats. // if (tracks == null || tracks.isEmpty) {
return null // // Unrecognized format. This is expected, as ExoPlayer only supports a
} // // subset of formats.
// return null
// The metadata extraction process of ExoPlayer is normalized into a superclass. // }
// That means we have to iterate through and find the cover art ourselves. //
val metadata = tracks[0].getFormat(0).metadata // // The metadata extraction process of ExoPlayer is normalized into a superclass.
// // That means we have to iterate through and find the cover art ourselves.
if (metadata == null || metadata.length() == 0) { // val metadata = tracks[0].getFormat(0).metadata
// No (parsable) metadata. This is also expected. //
return null // if (metadata == null || metadata.length() == 0) {
} // // No (parsable) metadata. This is also expected.
// return null
var stream: ByteArrayInputStream? = null // }
//
for (i in 0 until metadata.length()) { // var stream: ByteArrayInputStream? = null
// We can only extract pictures from two tags with this method, ID3v2's APIC or //
// FLAC's PICTURE. // for (i in 0 until metadata.length()) {
val pic: ByteArray? // // We can only extract pictures from two tags with this method, ID3v2's APIC or
val type: Int // // FLAC's PICTURE.
// val pic: ByteArray?
when (val entry = metadata.get(i)) { // val type: Int
is ApicFrame -> { //
pic = entry.pictureData // when (val entry = metadata.get(i)) {
type = entry.pictureType // is ApicFrame -> {
} // pic = entry.pictureData
is PictureFrame -> { // type = entry.pictureType
pic = entry.pictureData // }
type = entry.pictureType // is PictureFrame -> {
} // pic = entry.pictureData
else -> continue // type = entry.pictureType
} // }
// else -> continue
// Ensure the picture type here is a front cover image so that we don't extract // }
// an incorrect cover image. //
// Yes, this does add some latency, but its quality covers so we can prioritize // // Ensure the picture type here is a front cover image so that we don't extract
// correctness over speed. // // an incorrect cover image.
if (type == MediaMetadata.PICTURE_TYPE_FRONT_COVER) { // // Yes, this does add some latency, but its quality covers so we can prioritize
logD("Front cover successfully found") // // correctness over speed.
// if (type == MediaMetadata.PICTURE_TYPE_FRONT_COVER) {
// We have a front cover image. Great. // logD("Front cover successfully found")
stream = ByteArrayInputStream(pic) //
break // // We have a front cover image. Great.
} else if (stream != null) { // stream = ByteArrayInputStream(pic)
// In the case a front cover is not found, use the first image in the tag instead. // break
// This can be corrected later on if a front cover frame is found. // } else if (stream != null) {
logD("Image not a front cover, assigning image of type $type for now") // // In the case a front cover is not found, use the first image in the tag instead.
// // This can be corrected later on if a front cover frame is found.
stream = ByteArrayInputStream(pic) // logD("Image not a front cover, assigning image of type $type for now")
} //
} // stream = ByteArrayInputStream(pic)
// }
return stream?.use { stm -> // }
return SourceResult( //
source = stm.source().buffer(), // return stream?.use { stm ->
mimeType = context.contentResolver.getType(uri), // return SourceResult(
dataSource = DataSource.DISK // source = stm.source().buffer(),
) // mimeType = context.contentResolver.getType(uri),
} // dataSource = DataSource.DISK
} // )
// }
// }
override fun key(data: Album) = data.id.toString() override fun key(data: Album) = data.id.toString()
} }

View file

@ -25,6 +25,7 @@ import androidx.annotation.DrawableRes
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import androidx.databinding.BindingAdapter import androidx.databinding.BindingAdapter
import coil.Coil import coil.Coil
import coil.clear
import coil.fetch.Fetcher import coil.fetch.Fetcher
import coil.request.ImageRequest import coil.request.ImageRequest
import coil.size.OriginalSize import coil.size.OriginalSize
@ -83,7 +84,9 @@ inline fun <reified T : BaseModel> ImageView.load(
@DrawableRes error: Int, @DrawableRes error: Int,
fetcher: Fetcher<T>, fetcher: Fetcher<T>,
) { ) {
val disposable = Coil.imageLoader(context).enqueue( clear()
Coil.imageLoader(context).enqueue(
ImageRequest.Builder(context) ImageRequest.Builder(context)
.target(this) .target(this)
.data(data) .data(data)