feat: provide glide support for photo

This commit is contained in:
cgspine
2022-03-14 16:26:10 +08:00
parent a897b526a8
commit ef076a20b2
19 changed files with 609 additions and 78 deletions
@@ -1,11 +1,7 @@
package com.qmuiteam.photo.coil
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.ColorFilter
import android.graphics.PixelFormat
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import androidx.compose.ui.unit.IntSize
import coil.ImageLoader
import coil.decode.BitmapFactoryDecoder
@@ -17,7 +13,7 @@ import coil.request.Options
import coil.request.get
import coil.size.Scale
import coil.size.pxOrElse
import com.qmuiteam.photo.data.QMUIBitmapRegion
import com.qmuiteam.photo.data.QMUIBitmapRegionHolderDrawable
import com.qmuiteam.photo.data.loadLongImage
import com.qmuiteam.photo.data.loadLongImageThumbnail
import kotlinx.coroutines.runInterruptible
@@ -77,36 +73,9 @@ class QMUICoilLongImageDecoder(
preloadCount = 2
)
return DecodeResult(
drawable = QMUICoilLongImageDrawableHolder(bitmapRegion),
drawable = QMUIBitmapRegionHolderDrawable(bitmapRegion),
isSampled = bmOptions.inSampleSize > 1 || bmOptions.inScaled
)
}
}
}
class QMUICoilLongImageDrawableHolder(val bitmapRegion: QMUIBitmapRegion) : Drawable() {
override fun getIntrinsicHeight(): Int {
return bitmapRegion.height
}
override fun getIntrinsicWidth(): Int {
return bitmapRegion.width
}
override fun draw(canvas: Canvas) {
}
override fun setAlpha(alpha: Int) {
}
override fun setColorFilter(colorFilter: ColorFilter?) {
}
override fun getOpacity(): Int {
return PixelFormat.OPAQUE
}
}
@@ -8,7 +8,6 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.*
@@ -20,7 +19,6 @@ import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.core.graphics.drawable.toBitmap
import coil.compose.AsyncImage
import coil.compose.AsyncImageContent
@@ -30,6 +28,7 @@ import coil.request.ErrorResult
import coil.request.ImageRequest
import coil.request.SuccessResult
import coil.size.Scale
import com.qmuiteam.photo.compose.QMUIBitmapRegionItem
import com.qmuiteam.photo.data.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@@ -77,9 +76,11 @@ open class QMUICoilThumbPhoto(
) { state ->
if (state == AsyncImagePainter.State.Empty || state is AsyncImagePainter.State.Loading) {
if (isContainerDimenExactly) {
Box(modifier = Modifier
.fillMaxSize()
.background(blankColor))
Box(
modifier = Modifier
.fillMaxSize()
.background(blankColor)
)
}
} else {
AsyncImageContent()
@@ -144,8 +145,10 @@ open class QMUICoilThumbPhoto(
alignment = Alignment.TopCenter,
modifier = Modifier.fillMaxSize()
)
}else{
Box(modifier = Modifier.fillMaxSize().background(blankColor))
} else {
Box(modifier = Modifier
.fillMaxSize()
.background(blankColor))
}
}
@@ -216,7 +219,7 @@ class QMUICoilPhoto(
context.imageLoader.execute(request)
}
if (result is SuccessResult) {
(result.drawable as? QMUICoilLongImageDrawableHolder)?.bitmapRegion?.let {
(result.drawable as? QMUIBitmapRegionHolderDrawable)?.bitmapRegion?.let {
images = it.list
}
onSuccess?.invoke(PhotoResult(uri, result.drawable))
@@ -233,7 +236,7 @@ class QMUICoilPhoto(
val heightDp = with(LocalDensity.current) {
height.toDp()
}
QMUILongImageItem(image, maxWidth, heightDp)
QMUIBitmapRegionItem(image, maxWidth, heightDp)
}
}
}
@@ -242,29 +245,6 @@ class QMUICoilPhoto(
}
@Composable
fun QMUILongImageItem(bmRegion: QMUIBitmapRegionProvider, w: Dp, h: Dp) {
var bitmap by remember {
mutableStateOf<Bitmap?>(null)
}
LaunchedEffect(key1 = bmRegion) {
withContext(Dispatchers.IO) {
bitmap = bmRegion.loader.load()
}
}
Box(modifier = Modifier.size(w, h)) {
val bm = bitmap
if (bm != null) {
Image(
painter = BitmapPainter(bm.asImageBitmap()),
contentDescription = "",
contentScale = ContentScale.FillWidth,
modifier = Modifier.fillMaxSize()
)
}
}
}
open class QMUICoilPhotoProvider(val uri: Uri, val ratio: Float) : QMUIPhotoProvider {
companion object {
+1
View File
@@ -0,0 +1 @@
/build
+53
View File
@@ -0,0 +1,53 @@
import com.qmuiteam.plugin.Dep
plugins {
id("com.android.library")
kotlin("android")
kotlin("kapt")
`maven-publish`
signing
id("qmui-publish")
}
version = Dep.QMUI.photoVer
android {
compileSdk = Dep.compileSdk
defaultConfig {
minSdk = Dep.minSdk
targetSdk = Dep.targetSdk
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = Dep.Compose.version
}
buildTypes {
getByName("release"){
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
}
}
compileOptions {
sourceCompatibility = Dep.javaVersion
targetCompatibility = Dep.javaVersion
}
kotlinOptions {
jvmTarget = Dep.kotlinJvmTarget
freeCompilerArgs += "-Xjvm-default=all"
}
}
dependencies {
api(project(":compose-core"))
compileOnly(project(":photo"))
api(Dep.Glide.glide)
kapt(Dep.Glide.compiler)
}
View File
+21
View File
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -0,0 +1,24 @@
package com.qmuiteam
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.qmuiteam.test", appContext.packageName)
}
}
+5
View File
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.qmuiteam.photo.glide">
</manifest>
@@ -0,0 +1,102 @@
package com.qmuiteam.photo.glide
import android.content.Context
import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import androidx.compose.ui.unit.IntSize
import com.bumptech.glide.Glide
import com.bumptech.glide.Registry
import com.bumptech.glide.annotation.GlideModule
import com.bumptech.glide.load.Option
import com.bumptech.glide.load.Options
import com.bumptech.glide.load.ResourceDecoder
import com.bumptech.glide.load.engine.Resource
import com.bumptech.glide.load.resource.SimpleResource
import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy
import com.bumptech.glide.module.LibraryGlideModule
import com.qmuiteam.photo.data.QMUIBitmapRegionHolderDrawable
import com.qmuiteam.photo.data.loadLongImage
import com.qmuiteam.photo.data.loadLongImageThumbnail
import java.io.IOException
import java.io.InputStream
import java.nio.ByteBuffer
val QMUI_PHOTO_IMG_IS_THUMB = Option.memory("com.qmuiteam.photo.isThumb", false)
class QMUILongGlidePhotoData(
val drawable: Drawable
)
@GlideModule
class QMUIGlideModule : LibraryGlideModule() {
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
registry.prepend(
Registry.BUCKET_BITMAP,
InputStream::class.java,
QMUILongGlidePhotoData::class.java,
object : ResourceDecoder<InputStream, QMUILongGlidePhotoData> {
override fun handles(source: InputStream, options: Options): Boolean {
return true
}
override fun decode(source: InputStream, width: Int, height: Int, options: Options): Resource<QMUILongGlidePhotoData> {
return doDecode(context, source, width, height, options)
}
})
}
}
private fun doDecode(
context: Context,
source: InputStream,
width: Int,
height: Int,
options: Options
): Resource<QMUILongGlidePhotoData> {
val isThumb = options.get(QMUI_PHOTO_IMG_IS_THUMB) == true
val bmOptions = BitmapFactory.Options()
if (isThumb) {
val bm = loadLongImageThumbnail(
source,
IntSize(width, height),
bmOptions,
options.get(DownsampleStrategy.OPTION) == DownsampleStrategy.CENTER_INSIDE
)
val drawable = BitmapDrawable(context.resources, bm)
return SimpleResource(QMUILongGlidePhotoData(drawable))
} else {
val bitmapRegion = loadLongImage(
source,
IntSize(width, height),
bmOptions,
options.get(DownsampleStrategy.OPTION) == DownsampleStrategy.CENTER_INSIDE,
preloadCount = 2
)
val drawable = QMUIBitmapRegionHolderDrawable(bitmapRegion)
return SimpleResource(QMUILongGlidePhotoData(drawable))
}
}
private class ByteBufferInputStream(val buf: ByteBuffer) : InputStream() {
@Throws(IOException::class)
override fun read(): Int {
return if (!buf.hasRemaining()) {
-1
} else buf.get().toInt() and 0xFF
}
@Throws(IOException::class)
override fun read(bytes: ByteArray, off: Int, len: Int): Int {
if (!buf.hasRemaining()) {
return -1
}
val toRead = len.coerceAtMost(buf.remaining())
buf.get(bytes, off, toRead)
return toRead
}
}
@@ -0,0 +1,266 @@
package com.qmuiteam.photo.glide
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Bundle
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.core.graphics.drawable.toBitmap
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import com.qmuiteam.photo.compose.QMUIBitmapRegionItem
import com.qmuiteam.photo.data.*
@Composable
private fun GlideImage(
uri: Uri,
isLongImage: Boolean,
isThumbImage: Boolean,
isContainerDimenExactly: Boolean,
onSuccess: ((PhotoResult) -> Unit)?,
onError: (() -> Unit)?,
contentDescription: String = "",
contentScale: ContentScale = ContentScale.Fit,
blankColor: Color = Color.Transparent,
) {
BoxWithConstraints(modifier = if (isContainerDimenExactly) Modifier.fillMaxSize() else Modifier) {
val state = remember(uri) {
mutableStateOf<Drawable?>(null)
}
val context = LocalContext.current
DisposableEffect(uri, isContainerDimenExactly, isLongImage, isThumbImage, contentScale) {
val request = if (isLongImage) {
Glide.with(context).`as`(QMUILongGlidePhotoData::class.java).load(uri)
.downsample(DownsampleStrategy.CENTER_OUTSIDE)
.dontTransform()
.set(QMUI_PHOTO_IMG_IS_THUMB, isThumbImage)
.into(object : CustomTarget<QMUILongGlidePhotoData>(
constraints.maxWidth,
constraints.maxHeight
) {
override fun onResourceReady(resource: QMUILongGlidePhotoData, transition: Transition<in QMUILongGlidePhotoData>?) {
state.value = resource.drawable
onSuccess?.invoke(PhotoResult(uri, resource.drawable))
}
override fun onLoadStarted(placeholder: Drawable?) {
if (placeholder != null) {
state.value = placeholder
}
}
override fun onLoadCleared(placeholder: Drawable?) {
if (placeholder != null) {
state.value = placeholder
}
}
override fun onLoadFailed(errorDrawable: Drawable?) {
onError?.invoke()
}
})
.request
} else {
Glide.with(context).load(uri)
.downsample(DownsampleStrategy.CENTER_OUTSIDE)
.dontTransform()
.into(object : CustomTarget<Drawable>(
constraints.maxWidth,
constraints.maxHeight
) {
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
state.value = resource
onSuccess?.invoke(PhotoResult(uri, resource))
}
override fun onLoadStarted(placeholder: Drawable?) {
if (placeholder != null) {
state.value = placeholder
}
}
override fun onLoadCleared(placeholder: Drawable?) {
if (placeholder != null) {
state.value = placeholder
}
}
override fun onLoadFailed(errorDrawable: Drawable?) {
onError?.invoke()
}
})
.request
}
onDispose {
request?.clear()
}
}
val currentDrawable = state.value
if (currentDrawable != null) {
if (currentDrawable is QMUIBitmapRegionHolderDrawable) {
LongImageContent(currentDrawable)
} else {
Image(
modifier = if (isContainerDimenExactly) {
Modifier.fillMaxSize()
} else Modifier,
contentDescription = contentDescription,
painter = BitmapPainter(currentDrawable.toBitmap().asImageBitmap()),
contentScale = contentScale,
)
}
} else if (isContainerDimenExactly && blankColor != Color.Transparent) {
Box(
modifier = Modifier
.fillMaxSize()
.background(blankColor)
)
}
}
}
@Composable
private fun LongImageContent(drawable: QMUIBitmapRegionHolderDrawable) {
val images by remember(drawable) {
mutableStateOf(drawable.bitmapRegion.list)
}
if (images.isNotEmpty()) {
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(images) { image ->
BoxWithConstraints() {
val width = constraints.maxWidth
val height = width * image.height / image.width
val heightDp = with(LocalDensity.current) {
height.toDp()
}
QMUIBitmapRegionItem(image, maxWidth, heightDp)
}
}
}
}
}
open class QMUIGlideThumbPhoto(
val uri: Uri,
val isLongImage: Boolean,
val blankColor: Color = Color.LightGray,
) : QMUIPhoto {
@Composable
override fun Compose(
contentScale: ContentScale,
isContainerDimenExactly: Boolean,
onSuccess: ((PhotoResult) -> Unit)?,
onError: ((Throwable) -> Unit)?
) {
GlideImage(
uri,
isLongImage,
true,
isContainerDimenExactly,
onSuccess,
onError = {
onError?.invoke(RuntimeException("glide failed to load thumb image."))
},
contentScale = contentScale,
blankColor = blankColor
)
}
}
class QMUICoilPhoto(
val uri: Uri,
val isLongImage: Boolean
) : QMUIPhoto {
@Composable
override fun Compose(
contentScale: ContentScale,
isContainerDimenExactly: Boolean,
onSuccess: ((PhotoResult) -> Unit)?,
onError: ((Throwable) -> Unit)?
) {
GlideImage(
uri,
isLongImage,
false,
isContainerDimenExactly,
onSuccess,
onError = {
onError?.invoke(RuntimeException("glide failed to load thumb image."))
},
contentScale = contentScale
)
}
}
open class QMUIGlidePhotoProvider(val uri: Uri, val ratio: Float) : QMUIPhotoProvider {
companion object {
const val META_URI_KEY = "meta_uri"
const val META_RATIO_KEY = "meta_ratio"
}
override fun thumbnail(): QMUIPhoto? {
return QMUIGlideThumbPhoto(uri, isLongImage())
}
override fun photo(): QMUIPhoto? {
return QMUICoilPhoto(uri, isLongImage())
}
override fun ratio(): Float {
return ratio
}
override fun isLongImage(): Boolean {
return ratio > 0 && ratio < 0.2f
}
override fun meta(): Bundle? {
return Bundle().apply {
putParcelable(META_URI_KEY, uri)
putFloat(META_RATIO_KEY, ratio)
}
}
override fun recoverCls(): Class<out PhotoTransitionProviderRecover>? {
return QMUICoilPhotoTransitionProviderRecover::class.java
}
}
class QMUICoilPhotoTransitionProviderRecover : PhotoTransitionProviderRecover {
override fun recover(bundle: Bundle): QMUIPhotoTransitionInfo? {
val uri = bundle.getParcelable<Uri>(QMUIGlidePhotoProvider.META_URI_KEY) ?: return null
val ratio = bundle.getFloat(QMUIGlidePhotoProvider.META_RATIO_KEY)
return QMUIPhotoTransitionInfo(
QMUIGlidePhotoProvider(uri, ratio),
null,
null,
null
)
}
}
@@ -0,0 +1,15 @@
package com.qmuiteam.photo.glide
import com.qmuiteam.photo.data.QMUIMediaModel
import com.qmuiteam.photo.data.QMUIMediaPhotoProviderFactory
import com.qmuiteam.photo.data.QMUIPhotoProvider
class QMUIMediaGlidePhotoProviderFactory : QMUIMediaPhotoProviderFactory {
override fun factory(model: QMUIMediaModel): QMUIPhotoProvider {
return QMUIGlidePhotoProvider(
model.uri,
if (model.height > 0 && model.width > 0) model.width.toFloat() / model.height else 0f
)
}
}
@@ -0,0 +1,17 @@
package com.qmuiteam
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
@@ -0,0 +1,39 @@
package com.qmuiteam.photo.compose
import android.graphics.Bitmap
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.Dp
import com.qmuiteam.photo.data.QMUIBitmapRegionProvider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
fun QMUIBitmapRegionItem(bmRegion: QMUIBitmapRegionProvider, w: Dp, h: Dp) {
var bitmap by remember {
mutableStateOf<Bitmap?>(null)
}
LaunchedEffect(key1 = bmRegion) {
withContext(Dispatchers.IO) {
bitmap = bmRegion.loader.load()
}
}
Box(modifier = Modifier.size(w, h)) {
val bm = bitmap
if (bm != null) {
Image(
painter = BitmapPainter(bm.asImageBitmap()),
contentDescription = "",
contentScale = ContentScale.FillWidth,
modifier = Modifier.fillMaxSize()
)
}
}
}
@@ -1,9 +1,7 @@
package com.qmuiteam.photo.data
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.BitmapRegionDecoder
import android.graphics.Rect
import android.graphics.*
import android.graphics.drawable.Drawable
import android.os.Build
import android.util.LruCache
import androidx.compose.ui.unit.IntSize
@@ -11,7 +9,6 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.io.InputStream
import java.util.LinkedHashMap
import kotlin.math.max
import kotlin.math.min
@@ -216,22 +213,22 @@ private class QMUIBitmapRegionCacheStatistic(
) {
private val scope = CoroutineScope(Dispatchers.IO)
private val cacheJobs = object: LruCache<QMUICacheBitmapRegionLoader, Job>(cacheCountForLazyLoad){
private val cacheJobs = object : LruCache<QMUICacheBitmapRegionLoader, Job>(cacheCountForLazyLoad) {
override fun entryRemoved(evicted: Boolean, key: QMUICacheBitmapRegionLoader?, oldValue: Job?, newValue: Job?) {
super.entryRemoved(evicted, key, oldValue, newValue)
if(newValue == null){
if (newValue == null) {
key?.let {
scope.launch {
it.releaseCache()
}
}
}else{
} else {
oldValue?.cancel()
}
}
}
fun doWhenLoaded(loader: QMUICacheBitmapRegionLoader){
fun doWhenLoaded(loader: QMUICacheBitmapRegionLoader) {
val job = scope.launch {
delay(cacheTimeoutForLazyLoad)
cacheJobs.remove(loader)
@@ -242,4 +239,32 @@ private class QMUIBitmapRegionCacheStatistic(
fun canCache(): Boolean {
return cacheTimeoutForLazyLoad > 0 && cacheCountForLazyLoad > 0
}
}
class QMUIBitmapRegionHolderDrawable(val bitmapRegion: QMUIBitmapRegion) : Drawable() {
override fun getIntrinsicHeight(): Int {
return bitmapRegion.height
}
override fun getIntrinsicWidth(): Int {
return bitmapRegion.width
}
override fun draw(canvas: Canvas) {
}
override fun setAlpha(alpha: Int) {
}
override fun setColorFilter(colorFilter: ColorFilter?) {
}
override fun getOpacity(): Int {
return PixelFormat.OPAQUE
}
}
@@ -74,4 +74,10 @@ object Dep {
object Coil {
const val compose = "io.coil-kt:coil-compose:2.0.0-alpha06"
}
object Glide {
private const val ver = "4.13.0"
const val glide = "com.github.bumptech.glide:glide:$ver"
const val compiler = "com.github.bumptech.glide:compiler:$ver"
}
}
@@ -741,6 +741,7 @@ public class QMUIViewHelper {
m.reset();
}
m.preTranslate(-group.getScrollX(), -group.getScrollY());
offsetDescendantMatrix(group, child, m);
RectF rectF = sRectF.get();
@@ -755,10 +756,6 @@ public class QMUIViewHelper {
}
static void offsetDescendantMatrix(ViewParent target, View view, Matrix m) {
if(target instanceof View){
final View vt = (View) target;
m.preTranslate(-vt.getScrollX(), -vt.getScrollY());
}
final ViewParent parent = view.getParent();
if (parent instanceof View && parent != target) {
final View vp = (View) parent;
+2
View File
@@ -90,10 +90,12 @@ dependencies {
implementation(project(":compose"))
implementation(project(":photo"))
implementation(project(":photo-coil"))
implementation(project(":photo-glide"))
implementation(Dep.Flipper.soLoader)
implementation(Dep.Flipper.flipper)
kapt(project(":compiler"))
kapt(project(":arch-compiler"))
kapt(Dep.Glide.compiler)
implementation("com.iqiyi.xcrash:xcrash-android-lib:3.1.0")
}
@@ -0,0 +1,8 @@
package com.qmuiteam.qmuidemo.manager
import com.bumptech.glide.annotation.GlideModule
import com.bumptech.glide.module.AppGlideModule
@GlideModule
class QDAppGlideModule: AppGlideModule() {
}
+1
View File
@@ -18,5 +18,6 @@ include(":compose-core")
include(":compose")
include(":photo")
include(":photo-coil")
include(":photo-glide")