image: use di w/coil
Use dependency injection with Coil. This allows me to use the coil-base artifact which should remove a bit of superfluous dexcode, assuming dagger uses less. It probably doesn't.
This commit is contained in:
parent
63e5a7ee69
commit
dd2017c510
13 changed files with 112 additions and 81 deletions
|
@ -113,7 +113,7 @@ dependencies {
|
||||||
implementation fileTree(dir: "libs", include: ["extension-*.aar"])
|
implementation fileTree(dir: "libs", include: ["extension-*.aar"])
|
||||||
|
|
||||||
// Image loading
|
// Image loading
|
||||||
implementation "io.coil-kt:coil:2.1.0"
|
implementation "io.coil-kt:coil-base:2.1.0"
|
||||||
|
|
||||||
// Material
|
// Material
|
||||||
// Locked below 1.7.0-alpha03 to avoid the same ripple bug
|
// Locked below 1.7.0-alpha03 to avoid the same ripple bug
|
||||||
|
|
|
@ -22,17 +22,9 @@ import android.content.Intent
|
||||||
import androidx.core.content.pm.ShortcutInfoCompat
|
import androidx.core.content.pm.ShortcutInfoCompat
|
||||||
import androidx.core.content.pm.ShortcutManagerCompat
|
import androidx.core.content.pm.ShortcutManagerCompat
|
||||||
import androidx.core.graphics.drawable.IconCompat
|
import androidx.core.graphics.drawable.IconCompat
|
||||||
import coil.ImageLoader
|
|
||||||
import coil.ImageLoaderFactory
|
|
||||||
import coil.request.CachePolicy
|
|
||||||
import dagger.hilt.android.HiltAndroidApp
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import org.oxycblt.auxio.image.ImageSettings
|
import org.oxycblt.auxio.image.ImageSettings
|
||||||
import org.oxycblt.auxio.image.extractor.AlbumCoverFetcher
|
|
||||||
import org.oxycblt.auxio.image.extractor.ArtistImageFetcher
|
|
||||||
import org.oxycblt.auxio.image.extractor.ErrorCrossfadeTransitionFactory
|
|
||||||
import org.oxycblt.auxio.image.extractor.GenreImageFetcher
|
|
||||||
import org.oxycblt.auxio.image.extractor.MusicKeyer
|
|
||||||
import org.oxycblt.auxio.playback.PlaybackSettings
|
import org.oxycblt.auxio.playback.PlaybackSettings
|
||||||
import org.oxycblt.auxio.ui.UISettings
|
import org.oxycblt.auxio.ui.UISettings
|
||||||
|
|
||||||
|
@ -41,7 +33,7 @@ import org.oxycblt.auxio.ui.UISettings
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
@HiltAndroidApp
|
@HiltAndroidApp
|
||||||
class Auxio : Application(), ImageLoaderFactory {
|
class Auxio : Application() {
|
||||||
@Inject lateinit var imageSettings: ImageSettings
|
@Inject lateinit var imageSettings: ImageSettings
|
||||||
@Inject lateinit var playbackSettings: PlaybackSettings
|
@Inject lateinit var playbackSettings: PlaybackSettings
|
||||||
@Inject lateinit var uiSettings: UISettings
|
@Inject lateinit var uiSettings: UISettings
|
||||||
|
@ -68,22 +60,6 @@ class Auxio : Application(), ImageLoaderFactory {
|
||||||
.build()))
|
.build()))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun newImageLoader() =
|
|
||||||
ImageLoader.Builder(applicationContext)
|
|
||||||
.components {
|
|
||||||
// Add fetchers for Music components to make them usable with ImageRequest
|
|
||||||
add(MusicKeyer())
|
|
||||||
add(AlbumCoverFetcher.SongFactory())
|
|
||||||
add(AlbumCoverFetcher.AlbumFactory())
|
|
||||||
add(ArtistImageFetcher.Factory())
|
|
||||||
add(GenreImageFetcher.Factory())
|
|
||||||
}
|
|
||||||
// Use our own crossfade with error drawable support
|
|
||||||
.transitionFactory(ErrorCrossfadeTransitionFactory())
|
|
||||||
// Not downloading anything, so no disk-caching
|
|
||||||
.diskCachePolicy(CachePolicy.DISABLED)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/** The [Intent] name for the "Shuffle All" shortcut. */
|
/** The [Intent] name for the "Shuffle All" shortcut. */
|
||||||
const val INTENT_KEY_SHORTCUT_SHUFFLE = BuildConfig.APPLICATION_ID + ".action.SHUFFLE_ALL"
|
const val INTENT_KEY_SHORTCUT_SHUFFLE = BuildConfig.APPLICATION_ID + ".action.SHUFFLE_ALL"
|
||||||
|
|
|
@ -20,10 +20,12 @@ package org.oxycblt.auxio.image
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
import coil.imageLoader
|
import coil.ImageLoader
|
||||||
import coil.request.Disposable
|
import coil.request.Disposable
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import coil.size.Size
|
import coil.size.Size
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import javax.inject.Inject
|
||||||
import org.oxycblt.auxio.image.extractor.SquareFrameTransform
|
import org.oxycblt.auxio.image.extractor.SquareFrameTransform
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
|
|
||||||
|
@ -38,7 +40,12 @@ import org.oxycblt.auxio.music.Song
|
||||||
* @param context [Context] required to load images.
|
* @param context [Context] required to load images.
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
class BitmapProvider(private val context: Context) {
|
class BitmapProvider
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
@ApplicationContext private val context: Context,
|
||||||
|
private val imageLoader: ImageLoader
|
||||||
|
) {
|
||||||
/**
|
/**
|
||||||
* An extension of [Disposable] with an additional [Target] to deliver the final [Bitmap] to.
|
* An extension of [Disposable] with an additional [Target] to deliver the final [Bitmap] to.
|
||||||
*/
|
*/
|
||||||
|
@ -94,7 +101,7 @@ class BitmapProvider(private val context: Context) {
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
if (currentHandle == handle) {
|
if (currentHandle == handle) {
|
||||||
// Has not been superceded by a new request, can deliver
|
// Has not been superseded by a new request, can deliver
|
||||||
// this result.
|
// this result.
|
||||||
target.onCompleted(it.toBitmap())
|
target.onCompleted(it.toBitmap())
|
||||||
}
|
}
|
||||||
|
@ -103,13 +110,13 @@ class BitmapProvider(private val context: Context) {
|
||||||
onError = {
|
onError = {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
if (currentHandle == handle) {
|
if (currentHandle == handle) {
|
||||||
// Has not been superceded by a new request, can deliver
|
// Has not been superseded by a new request, can deliver
|
||||||
// this result.
|
// this result.
|
||||||
target.onCompleted(null)
|
target.onCompleted(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
currentRequest = Request(context.imageLoader.enqueue(imageRequest.build()), target)
|
currentRequest = Request(imageLoader.enqueue(imageRequest.build()), target)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Release this instance, cancelling any currently running operations. */
|
/** Release this instance, cancelling any currently running operations. */
|
||||||
|
|
|
@ -17,13 +17,52 @@
|
||||||
|
|
||||||
package org.oxycblt.auxio.image
|
package org.oxycblt.auxio.image
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import coil.ImageLoader
|
||||||
|
import coil.request.CachePolicy
|
||||||
import dagger.Binds
|
import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import javax.inject.Singleton
|
||||||
|
import org.oxycblt.auxio.image.extractor.AlbumCoverFetcher
|
||||||
|
import org.oxycblt.auxio.image.extractor.ArtistImageFetcher
|
||||||
|
import org.oxycblt.auxio.image.extractor.ErrorCrossfadeTransitionFactory
|
||||||
|
import org.oxycblt.auxio.image.extractor.GenreImageFetcher
|
||||||
|
import org.oxycblt.auxio.image.extractor.MusicKeyer
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
interface ImageModule {
|
interface ImageModule {
|
||||||
@Binds fun settings(imageSettings: ImageSettingsImpl): ImageSettings
|
@Binds fun settings(imageSettings: ImageSettingsImpl): ImageSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
class CoilModule {
|
||||||
|
@Singleton
|
||||||
|
@Provides
|
||||||
|
fun imageLoader(
|
||||||
|
@ApplicationContext context: Context,
|
||||||
|
songFactory: AlbumCoverFetcher.SongFactory,
|
||||||
|
albumFactory: AlbumCoverFetcher.AlbumFactory,
|
||||||
|
artistFactory: ArtistImageFetcher.Factory,
|
||||||
|
genreFactory: GenreImageFetcher.Factory
|
||||||
|
) =
|
||||||
|
ImageLoader.Builder(context)
|
||||||
|
.components {
|
||||||
|
// Add fetchers for Music components to make them usable with ImageRequest
|
||||||
|
add(MusicKeyer())
|
||||||
|
add(songFactory)
|
||||||
|
add(albumFactory)
|
||||||
|
add(artistFactory)
|
||||||
|
add(genreFactory)
|
||||||
|
}
|
||||||
|
// Use our own crossfade with error drawable support
|
||||||
|
.transitionFactory(ErrorCrossfadeTransitionFactory())
|
||||||
|
// Not downloading anything, so no disk-caching
|
||||||
|
.diskCachePolicy(CachePolicy.DISABLED)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
|
@ -37,14 +37,6 @@ interface ImageSettings : Settings<ImageSettings.Listener> {
|
||||||
/** Called when [coverMode] changes. */
|
/** Called when [coverMode] changes. */
|
||||||
fun onCoverModeChanged() {}
|
fun onCoverModeChanged() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Get a framework-backed implementation.
|
|
||||||
* @param context [Context] required.
|
|
||||||
*/
|
|
||||||
fun from(context: Context): ImageSettings = ImageSettingsImpl(context)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ImageSettingsImpl @Inject constructor(@ApplicationContext context: Context) :
|
class ImageSettingsImpl @Inject constructor(@ApplicationContext context: Context) :
|
||||||
|
|
|
@ -29,8 +29,9 @@ import androidx.annotation.StringRes
|
||||||
import androidx.appcompat.widget.AppCompatImageView
|
import androidx.appcompat.widget.AppCompatImageView
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import androidx.core.graphics.drawable.DrawableCompat
|
import androidx.core.graphics.drawable.DrawableCompat
|
||||||
import coil.dispose
|
import coil.ImageLoader
|
||||||
import coil.load
|
import coil.request.ImageRequest
|
||||||
|
import coil.util.CoilUtils
|
||||||
import com.google.android.material.shape.MaterialShapeDrawable
|
import com.google.android.material.shape.MaterialShapeDrawable
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -60,6 +61,7 @@ class StyledImageView
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr: Int = 0) :
|
constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr: Int = 0) :
|
||||||
AppCompatImageView(context, attrs, defStyleAttr) {
|
AppCompatImageView(context, attrs, defStyleAttr) {
|
||||||
|
@Inject lateinit var imageLoader: ImageLoader
|
||||||
@Inject lateinit var uiSettings: UISettings
|
@Inject lateinit var uiSettings: UISettings
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -125,13 +127,16 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
||||||
* field for the name of the [Music].
|
* field for the name of the [Music].
|
||||||
*/
|
*/
|
||||||
private fun bindImpl(music: Music, @DrawableRes errorRes: Int, @StringRes descRes: Int) {
|
private fun bindImpl(music: Music, @DrawableRes errorRes: Int, @StringRes descRes: Int) {
|
||||||
|
val request =
|
||||||
|
ImageRequest.Builder(context)
|
||||||
|
.data(music)
|
||||||
|
.error(StyledDrawable(context, context.getDrawableCompat(errorRes)))
|
||||||
|
.transformations(SquareFrameTransform.INSTANCE)
|
||||||
|
.target(this)
|
||||||
|
.build()
|
||||||
// Dispose of any previous image request and load a new image.
|
// Dispose of any previous image request and load a new image.
|
||||||
dispose()
|
CoilUtils.dispose(this)
|
||||||
load(music) {
|
imageLoader.enqueue(request)
|
||||||
error(StyledDrawable(context, context.getDrawableCompat(errorRes)))
|
|
||||||
transformations(SquareFrameTransform.INSTANCE)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the content description to the specified resource.
|
// Update the content description to the specified resource.
|
||||||
contentDescription = context.getString(descRes, music.resolveName(context))
|
contentDescription = context.getString(descRes, music.resolveName(context))
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,11 @@ import coil.fetch.SourceResult
|
||||||
import coil.key.Keyer
|
import coil.key.Keyer
|
||||||
import coil.request.Options
|
import coil.request.Options
|
||||||
import coil.size.Size
|
import coil.size.Size
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
import okio.source
|
import okio.source
|
||||||
|
import org.oxycblt.auxio.image.ImageSettings
|
||||||
import org.oxycblt.auxio.list.Sort
|
import org.oxycblt.auxio.list.Sort
|
||||||
import org.oxycblt.auxio.music.Album
|
import org.oxycblt.auxio.music.Album
|
||||||
import org.oxycblt.auxio.music.Artist
|
import org.oxycblt.auxio.music.Artist
|
||||||
|
@ -57,9 +59,13 @@ class MusicKeyer : Keyer<Music> {
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
class AlbumCoverFetcher
|
class AlbumCoverFetcher
|
||||||
private constructor(private val context: Context, private val album: Album) : Fetcher {
|
private constructor(
|
||||||
|
private val context: Context,
|
||||||
|
private val imageSettings: ImageSettings,
|
||||||
|
private val album: Album
|
||||||
|
) : Fetcher {
|
||||||
override suspend fun fetch(): FetchResult? =
|
override suspend fun fetch(): FetchResult? =
|
||||||
Covers.fetch(context, album)?.run {
|
Covers.fetch(context, imageSettings, album)?.run {
|
||||||
SourceResult(
|
SourceResult(
|
||||||
source = ImageSource(source().buffer(), context),
|
source = ImageSource(source().buffer(), context),
|
||||||
mimeType = null,
|
mimeType = null,
|
||||||
|
@ -67,15 +73,17 @@ private constructor(private val context: Context, private val album: Album) : Fe
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A [Fetcher.Factory] implementation that works with [Song]s. */
|
/** A [Fetcher.Factory] implementation that works with [Song]s. */
|
||||||
class SongFactory : Fetcher.Factory<Song> {
|
class SongFactory @Inject constructor(private val imageSettings: ImageSettings) :
|
||||||
|
Fetcher.Factory<Song> {
|
||||||
override fun create(data: Song, options: Options, imageLoader: ImageLoader) =
|
override fun create(data: Song, options: Options, imageLoader: ImageLoader) =
|
||||||
AlbumCoverFetcher(options.context, data.album)
|
AlbumCoverFetcher(options.context, imageSettings, data.album)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A [Fetcher.Factory] implementation that works with [Album]s. */
|
/** A [Fetcher.Factory] implementation that works with [Album]s. */
|
||||||
class AlbumFactory : Fetcher.Factory<Album> {
|
class AlbumFactory @Inject constructor(private val imageSettings: ImageSettings) :
|
||||||
|
Fetcher.Factory<Album> {
|
||||||
override fun create(data: Album, options: Options, imageLoader: ImageLoader) =
|
override fun create(data: Album, options: Options, imageLoader: ImageLoader) =
|
||||||
AlbumCoverFetcher(options.context, data)
|
AlbumCoverFetcher(options.context, imageSettings, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,20 +94,23 @@ private constructor(private val context: Context, private val album: Album) : Fe
|
||||||
class ArtistImageFetcher
|
class ArtistImageFetcher
|
||||||
private constructor(
|
private constructor(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
|
private val imageSettings: ImageSettings,
|
||||||
private val size: Size,
|
private val size: Size,
|
||||||
private val artist: Artist
|
private val artist: Artist
|
||||||
) : Fetcher {
|
) : Fetcher {
|
||||||
override suspend fun fetch(): FetchResult? {
|
override suspend fun fetch(): FetchResult? {
|
||||||
// Pick the "most prominent" albums (i.e albums with the most songs) to show in the image.
|
// Pick the "most prominent" albums (i.e albums with the most songs) to show in the image.
|
||||||
val albums = Sort(Sort.Mode.ByCount, Sort.Direction.DESCENDING).albums(artist.albums)
|
val albums = Sort(Sort.Mode.ByCount, Sort.Direction.DESCENDING).albums(artist.albums)
|
||||||
val results = albums.mapAtMostNotNull(4) { album -> Covers.fetch(context, album) }
|
val results =
|
||||||
|
albums.mapAtMostNotNull(4) { album -> Covers.fetch(context, imageSettings, album) }
|
||||||
return Images.createMosaic(context, results, size)
|
return Images.createMosaic(context, results, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** [Fetcher.Factory] implementation. */
|
/** [Fetcher.Factory] implementation. */
|
||||||
class Factory : Fetcher.Factory<Artist> {
|
class Factory @Inject constructor(private val imageSettings: ImageSettings) :
|
||||||
|
Fetcher.Factory<Artist> {
|
||||||
override fun create(data: Artist, options: Options, imageLoader: ImageLoader) =
|
override fun create(data: Artist, options: Options, imageLoader: ImageLoader) =
|
||||||
ArtistImageFetcher(options.context, options.size, data)
|
ArtistImageFetcher(options.context, imageSettings, options.size, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,18 +121,20 @@ private constructor(
|
||||||
class GenreImageFetcher
|
class GenreImageFetcher
|
||||||
private constructor(
|
private constructor(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
|
private val imageSettings: ImageSettings,
|
||||||
private val size: Size,
|
private val size: Size,
|
||||||
private val genre: Genre
|
private val genre: Genre
|
||||||
) : Fetcher {
|
) : Fetcher {
|
||||||
override suspend fun fetch(): FetchResult? {
|
override suspend fun fetch(): FetchResult? {
|
||||||
val results = genre.albums.mapAtMostNotNull(4) { Covers.fetch(context, it) }
|
val results = genre.albums.mapAtMostNotNull(4) { Covers.fetch(context, imageSettings, it) }
|
||||||
return Images.createMosaic(context, results, size)
|
return Images.createMosaic(context, results, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** [Fetcher.Factory] implementation. */
|
/** [Fetcher.Factory] implementation. */
|
||||||
class Factory : Fetcher.Factory<Genre> {
|
class Factory @Inject constructor(private val imageSettings: ImageSettings) :
|
||||||
|
Fetcher.Factory<Genre> {
|
||||||
override fun create(data: Genre, options: Options, imageLoader: ImageLoader) =
|
override fun create(data: Genre, options: Options, imageLoader: ImageLoader) =
|
||||||
GenreImageFetcher(options.context, options.size, data)
|
GenreImageFetcher(options.context, imageSettings, options.size, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,13 +42,14 @@ object Covers {
|
||||||
/**
|
/**
|
||||||
* Fetch an album cover, respecting the current cover configuration.
|
* Fetch an album cover, respecting the current cover configuration.
|
||||||
* @param context [Context] required to load the image.
|
* @param context [Context] required to load the image.
|
||||||
|
* @param imageSettings [ImageSettings] required to obtain configuration information.
|
||||||
* @param album [Album] to load the cover from.
|
* @param album [Album] to load the cover from.
|
||||||
* @return An [InputStream] of image data if the cover loading was successful, null if the cover
|
* @return An [InputStream] of image data if the cover loading was successful, null if the cover
|
||||||
* loading failed or should not occur.
|
* loading failed or should not occur.
|
||||||
*/
|
*/
|
||||||
suspend fun fetch(context: Context, album: Album): InputStream? {
|
suspend fun fetch(context: Context, imageSettings: ImageSettings, album: Album): InputStream? {
|
||||||
return try {
|
return try {
|
||||||
when (ImageSettings.from(context).coverMode) {
|
when (imageSettings.coverMode) {
|
||||||
CoverMode.OFF -> null
|
CoverMode.OFF -> null
|
||||||
CoverMode.MEDIA_STORE -> fetchMediaStoreCovers(context, album)
|
CoverMode.MEDIA_STORE -> fetchMediaStoreCovers(context, album)
|
||||||
CoverMode.QUALITY -> fetchQualityCovers(context, album)
|
CoverMode.QUALITY -> fetchQualityCovers(context, album)
|
||||||
|
|
|
@ -25,7 +25,7 @@ import android.os.IBinder
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import coil.imageLoader
|
import coil.ImageLoader
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
@ -68,6 +68,7 @@ class IndexerService : Service(), Indexer.Controller, MusicSettings.Listener {
|
||||||
private lateinit var wakeLock: PowerManager.WakeLock
|
private lateinit var wakeLock: PowerManager.WakeLock
|
||||||
private lateinit var indexerContentObserver: SystemContentObserver
|
private lateinit var indexerContentObserver: SystemContentObserver
|
||||||
@Inject lateinit var musicSettings: MusicSettings
|
@Inject lateinit var musicSettings: MusicSettings
|
||||||
|
@Inject lateinit var imageLoader: ImageLoader
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
|
@ -52,8 +52,9 @@ class MediaSessionComponent
|
||||||
@Inject
|
@Inject
|
||||||
constructor(
|
constructor(
|
||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context,
|
||||||
|
private val bitmapProvider: BitmapProvider,
|
||||||
private val playbackManager: PlaybackStateManager,
|
private val playbackManager: PlaybackStateManager,
|
||||||
private val playbackSettings: PlaybackSettings
|
private val playbackSettings: PlaybackSettings,
|
||||||
) :
|
) :
|
||||||
MediaSessionCompat.Callback(),
|
MediaSessionCompat.Callback(),
|
||||||
PlaybackStateManager.Listener,
|
PlaybackStateManager.Listener,
|
||||||
|
@ -66,7 +67,6 @@ constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private val notification = NotificationComponent(context, mediaSession.sessionToken)
|
private val notification = NotificationComponent(context, mediaSession.sessionToken)
|
||||||
private val provider = BitmapProvider(context)
|
|
||||||
|
|
||||||
private var listener: Listener? = null
|
private var listener: Listener? = null
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ constructor(
|
||||||
*/
|
*/
|
||||||
fun release() {
|
fun release() {
|
||||||
listener = null
|
listener = null
|
||||||
provider.release()
|
bitmapProvider.release()
|
||||||
playbackSettings.unregisterListener(this)
|
playbackSettings.unregisterListener(this)
|
||||||
playbackManager.removeListener(this)
|
playbackManager.removeListener(this)
|
||||||
mediaSession.apply {
|
mediaSession.apply {
|
||||||
|
@ -148,7 +148,7 @@ constructor(
|
||||||
override fun onStateChanged(state: InternalPlayer.State) {
|
override fun onStateChanged(state: InternalPlayer.State) {
|
||||||
invalidateSessionState()
|
invalidateSessionState()
|
||||||
notification.updatePlaying(playbackManager.playerState.isPlaying)
|
notification.updatePlaying(playbackManager.playerState.isPlaying)
|
||||||
if (!provider.isBusy) {
|
if (!bitmapProvider.isBusy) {
|
||||||
listener?.onPostNotification(notification)
|
listener?.onPostNotification(notification)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -321,7 +321,7 @@ constructor(
|
||||||
// We are normally supposed to use URIs for album art, but that removes some of the
|
// We are normally supposed to use URIs for album art, but that removes some of the
|
||||||
// nice things we can do like square cropping or high quality covers. Instead,
|
// nice things we can do like square cropping or high quality covers. Instead,
|
||||||
// we load a full-size bitmap into the media session and take the performance hit.
|
// we load a full-size bitmap into the media session and take the performance hit.
|
||||||
provider.load(
|
bitmapProvider.load(
|
||||||
song,
|
song,
|
||||||
object : BitmapProvider.Target {
|
object : BitmapProvider.Target {
|
||||||
override fun onCompleted(bitmap: Bitmap?) {
|
override fun onCompleted(bitmap: Bitmap?) {
|
||||||
|
@ -416,7 +416,7 @@ constructor(
|
||||||
else -> notification.updateRepeatMode(playbackManager.repeatMode)
|
else -> notification.updateRepeatMode(playbackManager.repeatMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!provider.isBusy) {
|
if (!bitmapProvider.isBusy) {
|
||||||
listener?.onPostNotification(notification)
|
listener?.onPostNotification(notification)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,9 @@ package org.oxycblt.auxio.settings.categories
|
||||||
|
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import coil.Coil
|
import coil.ImageLoader
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import javax.inject.Inject
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.settings.BasePreferenceFragment
|
import org.oxycblt.auxio.settings.BasePreferenceFragment
|
||||||
import org.oxycblt.auxio.settings.ui.WrappedDialogPreference
|
import org.oxycblt.auxio.settings.ui.WrappedDialogPreference
|
||||||
|
@ -28,7 +30,10 @@ import org.oxycblt.auxio.settings.ui.WrappedDialogPreference
|
||||||
* "Content" settings.
|
* "Content" settings.
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
|
@AndroidEntryPoint
|
||||||
class MusicPreferenceFragment : BasePreferenceFragment(R.xml.preferences_music) {
|
class MusicPreferenceFragment : BasePreferenceFragment(R.xml.preferences_music) {
|
||||||
|
@Inject lateinit var imageLoader: ImageLoader
|
||||||
|
|
||||||
override fun onOpenDialogPreference(preference: WrappedDialogPreference) {
|
override fun onOpenDialogPreference(preference: WrappedDialogPreference) {
|
||||||
if (preference.key == getString(R.string.set_key_separators)) {
|
if (preference.key == getString(R.string.set_key_separators)) {
|
||||||
findNavController().navigate(MusicPreferenceFragmentDirections.goToSeparatorsDialog())
|
findNavController().navigate(MusicPreferenceFragmentDirections.goToSeparatorsDialog())
|
||||||
|
@ -39,7 +44,7 @@ class MusicPreferenceFragment : BasePreferenceFragment(R.xml.preferences_music)
|
||||||
if (preference.key == getString(R.string.set_key_cover_mode)) {
|
if (preference.key == getString(R.string.set_key_cover_mode)) {
|
||||||
preference.onPreferenceChangeListener =
|
preference.onPreferenceChangeListener =
|
||||||
Preference.OnPreferenceChangeListener { _, _ ->
|
Preference.OnPreferenceChangeListener { _, _ ->
|
||||||
Coil.imageLoader(requireContext()).memoryCache?.clear()
|
imageLoader.memoryCache?.clear()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,14 +46,6 @@ interface UISettings : Settings<UISettings.Listener> {
|
||||||
/** Called when [roundMode] changes. */
|
/** Called when [roundMode] changes. */
|
||||||
fun onRoundModeChanged()
|
fun onRoundModeChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Get a framework-backed implementation.
|
|
||||||
* @param context [Context] required.
|
|
||||||
*/
|
|
||||||
fun from(context: Context): UISettings = UISettingsImpl(context)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class UISettingsImpl @Inject constructor(@ApplicationContext context: Context) :
|
class UISettingsImpl @Inject constructor(@ApplicationContext context: Context) :
|
||||||
|
|
|
@ -48,11 +48,11 @@ class WidgetComponent
|
||||||
constructor(
|
constructor(
|
||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context,
|
||||||
private val imageSettings: ImageSettings,
|
private val imageSettings: ImageSettings,
|
||||||
|
private val bitmapProvider: BitmapProvider,
|
||||||
private val playbackManager: PlaybackStateManager,
|
private val playbackManager: PlaybackStateManager,
|
||||||
private val uiSettings: UISettings
|
private val uiSettings: UISettings
|
||||||
) : PlaybackStateManager.Listener, UISettings.Listener, ImageSettings.Listener {
|
) : PlaybackStateManager.Listener, UISettings.Listener, ImageSettings.Listener {
|
||||||
private val widgetProvider = WidgetProvider()
|
private val widgetProvider = WidgetProvider()
|
||||||
private val provider = BitmapProvider(context)
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
playbackManager.addListener(this)
|
playbackManager.addListener(this)
|
||||||
|
@ -74,7 +74,7 @@ constructor(
|
||||||
val repeatMode = playbackManager.repeatMode
|
val repeatMode = playbackManager.repeatMode
|
||||||
val isShuffled = playbackManager.queue.isShuffled
|
val isShuffled = playbackManager.queue.isShuffled
|
||||||
|
|
||||||
provider.load(
|
bitmapProvider.load(
|
||||||
song,
|
song,
|
||||||
object : BitmapProvider.Target {
|
object : BitmapProvider.Target {
|
||||||
override fun onConfigRequest(builder: ImageRequest.Builder): ImageRequest.Builder {
|
override fun onConfigRequest(builder: ImageRequest.Builder): ImageRequest.Builder {
|
||||||
|
@ -112,7 +112,7 @@ constructor(
|
||||||
|
|
||||||
/** Release this instance, preventing any further events from updating the widget instances. */
|
/** Release this instance, preventing any further events from updating the widget instances. */
|
||||||
fun release() {
|
fun release() {
|
||||||
provider.release()
|
bitmapProvider.release()
|
||||||
imageSettings.unregisterListener(this)
|
imageSettings.unregisterListener(this)
|
||||||
playbackManager.removeListener(this)
|
playbackManager.removeListener(this)
|
||||||
uiSettings.unregisterListener(this)
|
uiSettings.unregisterListener(this)
|
||||||
|
|
Loading…
Reference in a new issue