Skip to content

Commit

Permalink
Merge pull request #174 from qdsfdhvh/feature/android_context_in_opti…
Browse files Browse the repository at this point in the history
…ons_extra

support androidContext in options extra
  • Loading branch information
qdsfdhvh authored Jun 3, 2023
2 parents cf8615e + b532cf1 commit 0a57809
Show file tree
Hide file tree
Showing 24 changed files with 213 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.seiko.imageloader.component.setupDefaultComponents
import com.seiko.imageloader.demo.util.LocalResLoader
import com.seiko.imageloader.demo.util.ResLoader
import com.seiko.imageloader.demo.util.commonConfig
import com.seiko.imageloader.option.androidContext
import okio.Path.Companion.toOkioPath

class MainActivity : ComponentActivity() {
Expand All @@ -29,8 +30,11 @@ class MainActivity : ComponentActivity() {
private fun generateImageLoader(): ImageLoader {
return ImageLoader {
commonConfig()
options {
androidContext(applicationContext)
}
components {
setupDefaultComponents(applicationContext)
setupDefaultComponents()
}
interceptor {
memoryCacheConfig {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
package com.seiko.imageloader.intercept

import com.seiko.imageloader.Bitmap
import com.seiko.imageloader.model.BlurEffects
import com.seiko.imageloader.model.ImageResult
import com.seiko.imageloader.model.KEY_BLUR_RADIUS
import com.seiko.imageloader.model.blurEffects

// blur only support Bitmap
class BlurInterceptor : Interceptor {
override suspend fun intercept(chain: Interceptor.Chain): ImageResult {
val request = chain.request
val result = chain.proceed(request)
if (result is ImageResult.Bitmap) {
val blurEffects = request.extra[KEY_BLUR_RADIUS] as? BlurEffects
?: return result
val blurEffects = request.blurEffects ?: return result
return result.copy(bitmap = blur(result.bitmap, blurEffects.radius))
}
return result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ fun ImageRequestBuilder.blur(radius: Int) {
}
}

internal val ImageRequest.blurEffects: BlurEffects?
get() = extra[KEY_BLUR_RADIUS] as? BlurEffects

internal data class BlurEffects(
val radius: Int,
)

internal const val KEY_BLUR_RADIUS = "KEY_BLUR_RADIUS"
private const val KEY_BLUR_RADIUS = "KEY_BLUR_RADIUS"
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import com.seiko.imageloader.component.mapper.ResourceIntMapper
import com.seiko.imageloader.component.mapper.ResourceUriMapper

fun ComponentRegistryBuilder.setupAndroidComponents(
context: Context,
density: Density = Density(context),
maxImageSize: Int = 4096,
context: Context? = null,
density: Density? = context?.let { Density(it) },
maxImageSize: Int = BitmapFactoryDecoder.DEFAULT_MAX_PARALLELISM,
) {
// Mappers
add(ResourceUriMapper(context))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Build.VERSION.SDK_INT
import com.seiko.imageloader.option.Options
import com.seiko.imageloader.option.androidContext
import com.seiko.imageloader.util.DecodeUtils
import com.seiko.imageloader.util.ExifData
import com.seiko.imageloader.util.ExifUtils
Expand Down Expand Up @@ -83,7 +84,7 @@ class BitmapFactoryDecoder private constructor(

/** Compute and set [BitmapFactory.Options.inPreferredConfig]. */
private fun BitmapFactory.Options.configureConfig(exifData: ExifData) {
var config = options.config.toBitmapConfig()
var config = options.imageConfig.toBitmapConfig()

// Disable hardware bitmaps if we need to perform EXIF transformations.
if (exifData.isFlipped || exifData.isRotated) {
Expand Down Expand Up @@ -164,16 +165,22 @@ class BitmapFactoryDecoder private constructor(
}
}

class Factory constructor(
private val context: Context,
private val maxImageSize: Int,
class Factory(
private val context: Context? = null,
private val maxImageSize: Int = DEFAULT_MAX_IMAGE_SIZE,
maxParallelism: Int = DEFAULT_MAX_PARALLELISM,
) : Decoder.Factory {

private val parallelismLock = Semaphore(maxParallelism)

override suspend fun create(source: DecodeSource, options: Options): Decoder {
return BitmapFactoryDecoder(context, source, options, maxImageSize, parallelismLock)
return BitmapFactoryDecoder(
context = context ?: options.androidContext,
source = source,
options = options,
maxImageSize = maxImageSize,
parallelismLock = parallelismLock,
)
}
}

Expand All @@ -195,5 +202,6 @@ class BitmapFactoryDecoder private constructor(

internal companion object {
internal const val DEFAULT_MAX_PARALLELISM = 4
internal const val DEFAULT_MAX_IMAGE_SIZE = 4096
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class GifDecoder private constructor(
val movie: Movie? = bufferedSource.use { Movie.decodeStream(it.inputStream()) }
check(movie != null && movie.width() > 0 && movie.height() > 0) { "Failed to decode GIF." }

val config = options.config.toBitmapConfig()
val config = options.imageConfig.toBitmapConfig()
val movieConfig = when {
// movie.isOpaque && options.allowRgb565 -> Bitmap.Config.RGB_565
config.isHardware -> Bitmap.Config.ARGB_8888
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.seiko.imageloader.component.fetcher.ContentUriFetcher
import com.seiko.imageloader.component.fetcher.ResourceUriFetcher
import com.seiko.imageloader.model.metadata
import com.seiko.imageloader.option.Options
import com.seiko.imageloader.option.androidContext
import com.seiko.imageloader.toImage
import com.seiko.imageloader.util.FrameDelayRewritingSource
import com.seiko.imageloader.util.ScaleDrawable
Expand Down Expand Up @@ -103,7 +104,7 @@ class ImageDecoderDecoder private constructor(
}

private fun ImageDecoder.configureImageDecoderProperties() {
val config = options.config.toBitmapConfig()
val config = options.imageConfig.toBitmapConfig()
allocator = if (config.isHardware) {
ImageDecoder.ALLOCATOR_HARDWARE
} else {
Expand Down Expand Up @@ -143,15 +144,20 @@ class ImageDecoderDecoder private constructor(
return ScaleDrawable(baseDrawable, options.scale)
}

class Factory @JvmOverloads constructor(
private val context: Context,
class Factory(
private val context: Context? = null,
private val enforceMinimumFrameDelay: Boolean = true,
) : Decoder.Factory {

override suspend fun create(source: DecodeSource, options: Options): Decoder? {
if (!options.playAnimate) return null
if (!isApplicable(source.source)) return null
return ImageDecoderDecoder(context, source, options, enforceMinimumFrameDelay)
return ImageDecoderDecoder(
context = context ?: options.androidContext,
source = source,
options = options,
enforceMinimumFrameDelay = enforceMinimumFrameDelay,
)
}

private fun isApplicable(source: BufferedSource): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.compose.ui.unit.Density
import com.caverock.androidsvg.SVG
import com.seiko.imageloader.model.mimeType
import com.seiko.imageloader.option.Options
import com.seiko.imageloader.option.androidContext
import com.seiko.imageloader.util.SVGPainter
import com.seiko.imageloader.util.isSvg

Expand All @@ -27,15 +28,15 @@ class SvgDecoder private constructor(
)
}

class Factory constructor(
private val density: Density,
class Factory(
private val density: Density? = null,
) : Decoder.Factory {

override suspend fun create(source: DecodeSource, options: Options): Decoder? {
if (!isApplicable(source)) return null
return SvgDecoder(
source = source,
density = density,
density = density ?: Density(options.androidContext),
options = options,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.seiko.imageloader.model.extraData
import com.seiko.imageloader.model.metadata
import com.seiko.imageloader.model.mimeType
import com.seiko.imageloader.option.Options
import com.seiko.imageloader.option.androidContext
import com.seiko.imageloader.util.getMimeTypeFromUrl
import com.seiko.imageloader.util.isAssetUri
import okio.buffer
Expand All @@ -28,11 +29,16 @@ class AssetUriFetcher private constructor(
)
}

class Factory(private val context: Context) : Fetcher.Factory {
class Factory(
private val context: Context? = null,
) : Fetcher.Factory {
override fun create(data: Any, options: Options): Fetcher? {
if (data !is Uri) return null
if (!isAssetUri(data)) return null
return AssetUriFetcher(context, data)
return AssetUriFetcher(
context = context ?: options.androidContext,
data = data,
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import com.seiko.imageloader.model.extraData
import com.seiko.imageloader.model.metadata
import com.seiko.imageloader.model.mimeType
import com.seiko.imageloader.option.Options
import com.seiko.imageloader.option.androidContext
import okio.buffer
import okio.source
import android.net.Uri as AndroidUri
Expand Down Expand Up @@ -78,13 +79,16 @@ class ContentUriFetcher private constructor(
}

class Factory(
private val context: Context,
private val context: Context? = null,
) : Fetcher.Factory {

override fun create(data: Any, options: Options): Fetcher? {
if (data !is Uri) return null
if (!isApplicable(data)) return null
return ContentUriFetcher(context, data)
return ContentUriFetcher(
context = context ?: options.androidContext,
data = data,
)
}

private fun isApplicable(data: Uri) = data.scheme == SCHEME_CONTENT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.seiko.imageloader.model.extraData
import com.seiko.imageloader.model.metadata
import com.seiko.imageloader.model.mimeType
import com.seiko.imageloader.option.Options
import com.seiko.imageloader.option.androidContext
import com.seiko.imageloader.toImage
import com.seiko.imageloader.util.DrawableUtils
import com.seiko.imageloader.util.getMimeTypeFromUrl
Expand Down Expand Up @@ -64,7 +65,7 @@ class ResourceUriFetcher private constructor(
FetchResult.Bitmap(
bitmap = DrawableUtils.convertToBitmap(
drawable = drawable,
config = options.config.toBitmapConfig(),
config = options.imageConfig.toBitmapConfig(),
scale = options.scale,
allowInexactSize = options.allowInexactSize,
),
Expand Down Expand Up @@ -98,11 +99,17 @@ class ResourceUriFetcher private constructor(
throw IllegalStateException("Invalid $SCHEME_ANDROID_RESOURCE URI: $data")
}

class Factory(private val context: Context) : Fetcher.Factory {
class Factory(
private val context: Context? = null,
) : Fetcher.Factory {
override fun create(data: Any, options: Options): Fetcher? {
if (data !is Uri) return null
if (!isApplicable(data)) return null
return ResourceUriFetcher(context, data, options)
return ResourceUriFetcher(
context = context ?: options.androidContext,
data = data,
options = options,
)
}

private fun isApplicable(data: Uri): Boolean {
Expand Down Expand Up @@ -162,6 +169,7 @@ private fun Context.getXmlDrawableCompat(resources: Resources, @XmlRes resId: In
theme,
)
}

"animated-vector" -> {
return AnimatedVectorDrawableCompat.createFromXmlInner(
this,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import android.content.Context
import android.content.res.Configuration
import com.eygraber.uri.Uri
import com.seiko.imageloader.option.Options
import com.seiko.imageloader.option.androidContext

class UriKeyer(private val context: Context) : Keyer {
class UriKeyer(private val context: Context? = null) : Keyer {
override fun key(data: Any, options: Options, type: Keyer.Type): String? {
if (data !is Uri) return null

Expand All @@ -15,11 +16,12 @@ class UriKeyer(private val context: Context) : Keyer {
// android uri is mapper to android.resource://example.package.name/12345678,
// but resId is changeable, here is convert resId to entryName.
val resId = data.pathSegments.lastOrNull()?.toIntOrNull() ?: return data.toString()
val entryName = context.resources.getResourceEntryName(resId)
val androidContext = context ?: options.androidContext
val entryName = androidContext.resources.getResourceEntryName(resId)
val newUri = data.buildUpon().path(entryName).build()

// 'android.resource' uris can change if night mode is enabled/disabled.
return "$newUri-${context.resources.configuration.nightMode}"
return "$newUri-${androidContext.resources.configuration.nightMode}"
}

private val Configuration.nightMode: Int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import android.content.res.Resources
import androidx.annotation.DrawableRes
import com.eygraber.uri.Uri
import com.seiko.imageloader.option.Options
import com.seiko.imageloader.option.androidContext

class ResourceIntMapper(private val context: Context) : Mapper<Uri> {
class ResourceIntMapper(private val context: Context? = null) : Mapper<Uri> {

override fun map(data: Any, options: Options): Uri? {
if (data !is Int) return null
if (!isApplicable(data, context)) return null
return Uri.parse("$SCHEME_ANDROID_RESOURCE://${context.packageName}/$data")
val androidContext = context ?: options.androidContext
if (!isApplicable(data, androidContext)) return null
return Uri.parse("$SCHEME_ANDROID_RESOURCE://${androidContext.packageName}/$data")
}

private fun isApplicable(@DrawableRes data: Int, context: Context): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,24 @@ import android.content.ContentResolver.SCHEME_ANDROID_RESOURCE
import android.content.Context
import com.eygraber.uri.Uri
import com.seiko.imageloader.option.Options
import com.seiko.imageloader.option.androidContext

/**
* Maps android.resource uris with resource names to uris containing their resources ID. i.e.:
*
* android.resource://example.package.name/drawable/image -> android.resource://example.package.name/12345678
*/
class ResourceUriMapper(private val context: Context) : Mapper<Uri> {
class ResourceUriMapper(
private val context: Context? = null,
) : Mapper<Uri> {

override fun map(data: Any, options: Options): Uri? {
if (data !is Uri) return null
if (!isApplicable(data)) return null

val packageName = data.authority.orEmpty()
val resources = context.packageManager.getResourcesForApplication(packageName)
val androidContext = context ?: options.androidContext
val resources = androidContext.packageManager.getResourcesForApplication(packageName)
val (type, name) = data.pathSegments
val id = resources.getIdentifier(name, type, packageName)
check(id != 0) { "Invalid $SCHEME_ANDROID_RESOURCE URI: $data" }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.seiko.imageloader.option

import android.content.Context

fun OptionsBuilder.androidContext(context: Context) {
extra {
set(KEY_ANDROID_CONTEXT, context)
}
}

val Options.androidContext: Context
get() = requireNotNull(extra[KEY_ANDROID_CONTEXT] as? Context) {
"not androidContext in options extra"
}

private const val KEY_ANDROID_CONTEXT = "KEY_ANDROID_CONTEXT"
Loading

0 comments on commit 0a57809

Please sign in to comment.