From 2bcd5114eb614e06f590ec759fde4b6bf28149ce Mon Sep 17 00:00:00 2001 From: Viktoriia Schwartz Date: Wed, 3 Feb 2021 02:19:25 -0800 Subject: [PATCH] Fix #2132: Chapter List Page Issues (#2232) - Blur and lock thumbnails of inactive cards. (#2422) * Lock and blur * Replace color/white with #FFFFFF * Add \n * Move isBlurred to LessonThumbnailImageView. * Remove unused import. * Move dependency on Glide away from from ImageLoader. * Remove unneeded line. * Refactor Transformation-related code, add KDocs. * Renaming. * Remove unused import. * Reformat. * Add Mockk test * Add Mockk test - lint * Add blurring tests. * Mock ImageLoader in StoryActivityTest. * Add delay before image loading. * Nits and improvements. * Remove unused import. * Remove unused import. * Set image resources in LessonThumbnailImageView in addition to calling GlideImageLoader. * BlurTransformation re-write. * BlurTransformation re-write. * Add TestImageLoaderModule to some tests. * Lint * Add TestImageLoaderModule to more tests. * Add TestImageLoaderModule to more tests. * Update utility/src/main/java/org/oppia/android/util/parser/BlurTransformation.kt Co-authored-by: Ben Henning * Update utility/src/main/java/org/oppia/android/util/parser/BlurTransformation.kt Co-authored-by: Ben Henning * TestGlideImageLoader Refactor. * Reformat. * Reformat. * Reformat. * Nits and comments. * Revert changes in landscape design. * Change BlurTransformation to return a copy instead of 'toTransform'. * Update app/src/sharedTest/java/org/oppia/android/app/story/StoryFragmentTest.kt Co-authored-by: Ben Henning * Refactor TestGlideImageLoader. * Nits Co-authored-by: Ben Henning --- .../customview/LessonThumbnailImageView.kt | 25 ++++- .../main/res/drawable/ic_baseline_lock_24.xml | 10 ++ .../res/layout-sw600dp/story_chapter_view.xml | 23 +++++ .../main/res/layout/story_chapter_view.xml | 23 +++++ .../app/home/RecentlyPlayedFragmentTest.kt | 4 +- .../android/app/story/StoryActivityTest.kt | 4 +- .../android/app/story/StoryFragmentTest.kt | 91 ++++++++++++++++++- .../app/topic/info/TopicInfoFragmentTest.kt | 4 +- .../app/utility/MockitoKotlinHelper.kt | 4 + .../WalkthroughTopicListFragmentTest.kt | 4 +- .../android/testing/TestImageLoaderModule.kt | 13 +++ .../android/util/parser/BlurTransformation.kt | 57 ++++++++++++ .../android/util/parser/GlideImageLoader.kt | 37 +++++++- .../oppia/android/util/parser/ImageLoader.kt | 34 ++++++- .../util/parser/TestGlideImageLoader.kt | 44 +++++++++ 15 files changed, 356 insertions(+), 21 deletions(-) create mode 100644 app/src/main/res/drawable/ic_baseline_lock_24.xml create mode 100644 testing/src/main/java/org/oppia/android/testing/TestImageLoaderModule.kt create mode 100644 utility/src/main/java/org/oppia/android/util/parser/BlurTransformation.kt create mode 100644 utility/src/main/java/org/oppia/android/util/parser/TestGlideImageLoader.kt diff --git a/app/src/main/java/org/oppia/android/app/customview/LessonThumbnailImageView.kt b/app/src/main/java/org/oppia/android/app/customview/LessonThumbnailImageView.kt index bec52e5d1d3..4b0bc74df7c 100644 --- a/app/src/main/java/org/oppia/android/app/customview/LessonThumbnailImageView.kt +++ b/app/src/main/java/org/oppia/android/app/customview/LessonThumbnailImageView.kt @@ -13,6 +13,7 @@ import org.oppia.android.util.gcsresource.DefaultResourceBucketName import org.oppia.android.util.logging.ConsoleLogger import org.oppia.android.util.parser.DefaultGcsPrefix import org.oppia.android.util.parser.ImageLoader +import org.oppia.android.util.parser.ImageTransformation import org.oppia.android.util.parser.ImageViewTarget import org.oppia.android.util.parser.ThumbnailDownloadUrlTemplate import javax.inject.Inject @@ -25,6 +26,7 @@ class LessonThumbnailImageView @JvmOverloads constructor( ) : AppCompatImageView(context, attrs, defStyleAttr) { private val imageView = this + private var isBlurred: Boolean = false private lateinit var lessonThumbnail: LessonThumbnail private lateinit var entityId: String private lateinit var entityType: String @@ -63,6 +65,10 @@ class LessonThumbnailImageView @JvmOverloads constructor( checkIfLoadingIsPossible() } + fun setIsBlurred(isBlurred: Boolean) { + this.isBlurred = isBlurred + } + private fun checkIfLoadingIsPossible() { if (::entityId.isInitialized && ::entityType.isInitialized && @@ -78,16 +84,25 @@ class LessonThumbnailImageView @JvmOverloads constructor( } private fun loadLessonThumbnail() { + var transformations = if (isBlurred) { + listOf(ImageTransformation.BLUR) + } else { + listOf() + } if (lessonThumbnail.thumbnailFilename.isNotEmpty()) { - loadImage(lessonThumbnail.thumbnailFilename) + loadImage(lessonThumbnail.thumbnailFilename, transformations) } else { - imageView.setImageResource(getLessonDrawableResource(lessonThumbnail)) + imageLoader.loadDrawable( + getLessonDrawableResource(lessonThumbnail), + ImageViewTarget(this), + transformations + ) } imageView.setBackgroundColor(lessonThumbnail.backgroundColorRgb) } /** Loads an image using Glide from [filename]. */ - private fun loadImage(filename: String) { + private fun loadImage(filename: String, transformations: List) { val imageName = String.format( thumbnailDownloadUrlTemplate, entityType, @@ -96,9 +111,9 @@ class LessonThumbnailImageView @JvmOverloads constructor( ) val imageUrl = "$gcsPrefix/$resourceBucketName/$imageName" if (imageUrl.endsWith("svg", ignoreCase = true)) { - imageLoader.loadSvg(imageUrl, ImageViewTarget(this)) + imageLoader.loadSvg(imageUrl, ImageViewTarget(this), transformations) } else { - imageLoader.loadBitmap(imageUrl, ImageViewTarget(this)) + imageLoader.loadBitmap(imageUrl, ImageViewTarget(this), transformations) } } diff --git a/app/src/main/res/drawable/ic_baseline_lock_24.xml b/app/src/main/res/drawable/ic_baseline_lock_24.xml new file mode 100644 index 00000000000..f9eac1afffb --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_lock_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout-sw600dp/story_chapter_view.xml b/app/src/main/res/layout-sw600dp/story_chapter_view.xml index 1b10fd0ff44..0248e1a857b 100644 --- a/app/src/main/res/layout-sw600dp/story_chapter_view.xml +++ b/app/src/main/res/layout-sw600dp/story_chapter_view.xml @@ -47,12 +47,35 @@ android:scaleType="centerInside" app:entityId="@{viewModel.storyId}" app:entityType="@{viewModel.entityType}" + app:isBlurred="@{viewModel.chapterSummary.chapterPlayState == ChapterPlayState.NOT_PLAYABLE_MISSING_PREREQUISITES}" app:layout_constraintDimensionRatio="16:9" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:lessonThumbnail="@{viewModel.chapterThumbnail}" /> + + + + + + + + > + @get:Rule var activityTestRule: ActivityTestRule = ActivityTestRule( StoryActivity::class.java, /* initialTouchMode= */ true, /* launchActivity= */ false @@ -304,6 +324,65 @@ class StoryFragmentTest { } } + @Test + fun testStoryFragment_chapterMissingPrerequisiteThumbnailIsBlurred() { + launch(createFractionsStoryActivityIntent()).use { + testCoroutineDispatchers.runCurrent() + onView(allOf(withId(R.id.story_chapter_list))).perform( + scrollToPosition( + 2 + ) + ) + onView( + atPositionOnView( + R.id.story_chapter_list, + 2, + R.id.chapter_thumbnail + ) + ).check { view, noViewFoundException -> + var lessonThumbnailImageView = view.findViewById( + R.id.chapter_thumbnail + ) + verify(lessonThumbnailImageView.imageLoader, atLeastOnce()).loadDrawable( + anyInt(), + anyOrNull(), + capture(listCaptor) + ) + assertThat(listCaptor.value).contains(ImageTransformation.BLUR) + } + } + } + + @Test + fun testStoryFragment_configChange_chapterMissingPrerequisiteThumbnailIsBlurred() { + launch(createFractionsStoryActivityIntent()).use { + testCoroutineDispatchers.runCurrent() + onView(isRoot()).perform(orientationLandscape()) + onView(allOf(withId(R.id.story_chapter_list))).perform( + scrollToPosition( + 2 + ) + ) + onView( + atPositionOnView( + R.id.story_chapter_list, + 2, + R.id.chapter_thumbnail + ) + ).check { view, noViewFoundException -> + var lessonThumbnailImageView = view.findViewById( + R.id.chapter_thumbnail + ) + verify(lessonThumbnailImageView.imageLoader, atLeastOnce()).loadDrawable( + anyInt(), + anyOrNull(), + capture(listCaptor) + ) + assertThat(listCaptor.value).contains(ImageTransformation.BLUR) + } + } + } + @Test fun testStoryFragment_chapterMissingPrerequisiteIsShownCorrectly() { launch(createFractionsStoryActivityIntent()).use { @@ -445,7 +524,7 @@ class StoryFragmentTest { ItemSelectionInputModule::class, MultipleChoiceInputModule::class, NumberWithUnitsRuleModule::class, NumericInputRuleModule::class, TextInputRuleModule::class, DragDropSortInputModule::class, ImageClickInputModule::class, InteractionsModule::class, - GcsResourceModule::class, GlideImageLoaderModule::class, ImageParsingModule::class, + GcsResourceModule::class, TestModule::class, ImageParsingModule::class, HtmlParserEntityTypeModule::class, QuestionModule::class, TestLogReportingModule::class, TestAccessibilityModule::class, LogStorageModule::class, CachingTestModule::class, PrimeTopicAssetsControllerModule::class, ExpirationMetaDataRetrieverModule::class, @@ -479,4 +558,12 @@ class StoryFragmentTest { override fun getApplicationInjector(): ApplicationInjector = component } + + /** Provides test dependencies (including a mock for [ImageLoader] to capture its operations). */ + @Module + class TestModule { + @Provides + @Singleton + fun provideMockImageLoader() = mock(ImageLoader::class.java) + } } diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/info/TopicInfoFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/info/TopicInfoFragmentTest.kt index a540ed51d44..d7ac3872e5a 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/topic/info/TopicInfoFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/topic/info/TopicInfoFragmentTest.kt @@ -68,13 +68,13 @@ import org.oppia.android.testing.RunOn import org.oppia.android.testing.TestAccessibilityModule import org.oppia.android.testing.TestCoroutineDispatchers import org.oppia.android.testing.TestDispatcherModule +import org.oppia.android.testing.TestImageLoaderModule import org.oppia.android.testing.TestLogReportingModule import org.oppia.android.testing.TestPlatform import org.oppia.android.util.caching.testing.CachingTestModule import org.oppia.android.util.gcsresource.GcsResourceModule import org.oppia.android.util.logging.LoggerModule import org.oppia.android.util.logging.firebase.FirebaseLogUploaderModule -import org.oppia.android.util.parser.GlideImageLoaderModule import org.oppia.android.util.parser.HtmlParserEntityTypeModule import org.oppia.android.util.parser.ImageParsingModule import org.robolectric.annotation.Config @@ -350,7 +350,7 @@ class TopicInfoFragmentTest { ItemSelectionInputModule::class, MultipleChoiceInputModule::class, NumberWithUnitsRuleModule::class, NumericInputRuleModule::class, TextInputRuleModule::class, DragDropSortInputModule::class, ImageClickInputModule::class, InteractionsModule::class, - GcsResourceModule::class, GlideImageLoaderModule::class, ImageParsingModule::class, + GcsResourceModule::class, TestImageLoaderModule::class, ImageParsingModule::class, HtmlParserEntityTypeModule::class, QuestionModule::class, TestLogReportingModule::class, TestAccessibilityModule::class, LogStorageModule::class, CachingTestModule::class, PrimeTopicAssetsControllerModule::class, ExpirationMetaDataRetrieverModule::class, diff --git a/app/src/sharedTest/java/org/oppia/android/app/utility/MockitoKotlinHelper.kt b/app/src/sharedTest/java/org/oppia/android/app/utility/MockitoKotlinHelper.kt index fb8163791b8..7937666e05e 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/utility/MockitoKotlinHelper.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/utility/MockitoKotlinHelper.kt @@ -1,9 +1,13 @@ package org.oppia.android.app.utility import org.mockito.ArgumentCaptor +import org.mockito.Mockito.any /** * Returns ArgumentCaptor.capture() as nullable type to avoid java.lang.IllegalStateException * when null is returned. */ fun capture(argumentCaptor: ArgumentCaptor): T = argumentCaptor.capture() + +/** Matches anything, including nulls. */ +fun anyOrNull(): T = any() diff --git a/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughTopicListFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughTopicListFragmentTest.kt index 852229ff5bc..96891d9b6cf 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughTopicListFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughTopicListFragmentTest.kt @@ -57,12 +57,12 @@ import org.oppia.android.testing.RobolectricModule import org.oppia.android.testing.TestAccessibilityModule import org.oppia.android.testing.TestCoroutineDispatchers import org.oppia.android.testing.TestDispatcherModule +import org.oppia.android.testing.TestImageLoaderModule import org.oppia.android.testing.TestLogReportingModule import org.oppia.android.util.caching.testing.CachingTestModule import org.oppia.android.util.gcsresource.GcsResourceModule import org.oppia.android.util.logging.LoggerModule import org.oppia.android.util.logging.firebase.FirebaseLogUploaderModule -import org.oppia.android.util.parser.GlideImageLoaderModule import org.oppia.android.util.parser.HtmlParserEntityTypeModule import org.oppia.android.util.parser.ImageParsingModule import org.robolectric.annotation.Config @@ -220,7 +220,7 @@ class WalkthroughTopicListFragmentTest { ItemSelectionInputModule::class, MultipleChoiceInputModule::class, NumberWithUnitsRuleModule::class, NumericInputRuleModule::class, TextInputRuleModule::class, DragDropSortInputModule::class, ImageClickInputModule::class, InteractionsModule::class, - GcsResourceModule::class, GlideImageLoaderModule::class, ImageParsingModule::class, + GcsResourceModule::class, TestImageLoaderModule::class, ImageParsingModule::class, HtmlParserEntityTypeModule::class, QuestionModule::class, TestLogReportingModule::class, TestAccessibilityModule::class, LogStorageModule::class, CachingTestModule::class, PrimeTopicAssetsControllerModule::class, ExpirationMetaDataRetrieverModule::class, diff --git a/testing/src/main/java/org/oppia/android/testing/TestImageLoaderModule.kt b/testing/src/main/java/org/oppia/android/testing/TestImageLoaderModule.kt new file mode 100644 index 00000000000..ff06faf525f --- /dev/null +++ b/testing/src/main/java/org/oppia/android/testing/TestImageLoaderModule.kt @@ -0,0 +1,13 @@ +package org.oppia.android.testing + +import dagger.Binds +import dagger.Module +import org.oppia.android.util.parser.ImageLoader +import org.oppia.android.util.parser.TestGlideImageLoader + +/** Provides image loading dependencies for unit tests. */ +@Module +abstract class TestImageLoaderModule { + @Binds + abstract fun provideFakeImageLoader(impl: TestGlideImageLoader): ImageLoader +} diff --git a/utility/src/main/java/org/oppia/android/util/parser/BlurTransformation.kt b/utility/src/main/java/org/oppia/android/util/parser/BlurTransformation.kt new file mode 100644 index 00000000000..910c80bd0dc --- /dev/null +++ b/utility/src/main/java/org/oppia/android/util/parser/BlurTransformation.kt @@ -0,0 +1,57 @@ +package org.oppia.android.util.parser + +import android.content.Context +import android.graphics.Bitmap +import android.renderscript.Allocation +import android.renderscript.Element +import android.renderscript.RenderScript +import android.renderscript.ScriptIntrinsicBlur +import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool +import com.bumptech.glide.load.resource.bitmap.BitmapTransformation +import java.security.MessageDigest + +// The radius of the blur, a float between 0 and 25. It defines the value of the standard +// deviation to the Gaussian function. +private const val BLUR_RADIUS = 20f + +/** + * [BlurTransformation] is a bitmap transformation that blurs an image using RenderScript. + * + * The following [tutorial](https://futurestud.io/tutorials/glide-custom-transformation) + * was used as a reference, as well as this [article](https://stackoverflow.com/a/23119957). + */ +class BlurTransformation(private val context: Context) : BitmapTransformation() { + + private val renderScript by lazy { RenderScript.create(context) } + + override fun transform( + pool: BitmapPool, + toTransform: Bitmap, + outWidth: Int, + outHeight: Int + ): Bitmap { + val blurredBitmap = toTransform.copy(Bitmap.Config.ARGB_8888, true) + // Create a RenderScript allocation pointing to a copy. + val inputAllocation = Allocation.createFromBitmap( + renderScript, + blurredBitmap, + Allocation.MipmapControl.MIPMAP_FULL, + Allocation.USAGE_SHARED + ) + // Create a new RenderScript allocation to receive the output from the blur operation. + val outputAllocation = Allocation.createTyped(renderScript, inputAllocation.type) + + // Create a new Gaussian blur script with 4 expect 8-bit color channels (e.g. ARGB). + val blurScript = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript)) + blurScript.setInput(inputAllocation) + blurScript.setRadius(BLUR_RADIUS) + blurScript.forEach(outputAllocation) + + outputAllocation.copyTo(blurredBitmap) + return blurredBitmap + } + + override fun updateDiskCacheKey(messageDigest: MessageDigest) { + messageDigest.update("blur transformation".toByteArray()) + } +} diff --git a/utility/src/main/java/org/oppia/android/util/parser/GlideImageLoader.kt b/utility/src/main/java/org/oppia/android/util/parser/GlideImageLoader.kt index 2519ee43f6c..e6dd1ec9ca9 100644 --- a/utility/src/main/java/org/oppia/android/util/parser/GlideImageLoader.kt +++ b/utility/src/main/java/org/oppia/android/util/parser/GlideImageLoader.kt @@ -2,9 +2,11 @@ package org.oppia.android.util.parser import android.content.Context import android.graphics.Bitmap +import android.graphics.drawable.Drawable import android.graphics.drawable.PictureDrawable import com.bumptech.glide.Glide import com.bumptech.glide.RequestBuilder +import com.bumptech.glide.load.Transformation import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.request.RequestOptions import org.oppia.android.util.caching.AssetRepository @@ -18,7 +20,11 @@ class GlideImageLoader @Inject constructor( private val assetRepository: AssetRepository ) : ImageLoader { - override fun loadBitmap(imageUrl: String, target: ImageTarget) { + override fun loadBitmap( + imageUrl: String, + target: ImageTarget, + transformations: List + ) { val model: Any = if (cacheAssetsLocally) { object : ImageAssetFetcher { override fun fetchImage(): ByteArray = assetRepository.loadRemoteBinaryAsset(imageUrl)() @@ -26,13 +32,19 @@ class GlideImageLoader @Inject constructor( override fun getImageIdentifier(): String = imageUrl } } else imageUrl + Glide.with(context) .asBitmap() .load(model) + .transform(*transformations.toGlideTransformations()) .intoTarget(target) } - override fun loadSvg(imageUrl: String, target: ImageTarget) { + override fun loadSvg( + imageUrl: String, + target: ImageTarget, + transformations: List + ) { val model: Any = if (cacheAssetsLocally) { object : ImageAssetFetcher { override fun fetchImage(): ByteArray = assetRepository.loadRemoteBinaryAsset(imageUrl)() @@ -47,6 +59,19 @@ class GlideImageLoader @Inject constructor( .fitCenter() .apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.NONE)) .load(model) + .transform(*transformations.toGlideTransformations()) + .intoTarget(target) + } + + override fun loadDrawable( + imageDrawableResId: Int, + target: ImageTarget, + transformations: List + ) { + Glide.with(context) + .asDrawable() + .load(imageDrawableResId) + .transform(*transformations.toGlideTransformations()) .intoTarget(target) } @@ -56,4 +81,12 @@ class GlideImageLoader @Inject constructor( is ImageViewTarget -> into(target.imageView) } } + + private fun List.toGlideTransformations(): Array> { + return map { + when (it) { + ImageTransformation.BLUR -> BlurTransformation(context) + } + }.toTypedArray() + } } diff --git a/utility/src/main/java/org/oppia/android/util/parser/ImageLoader.kt b/utility/src/main/java/org/oppia/android/util/parser/ImageLoader.kt index 33f22933711..096f29414ef 100644 --- a/utility/src/main/java/org/oppia/android/util/parser/ImageLoader.kt +++ b/utility/src/main/java/org/oppia/android/util/parser/ImageLoader.kt @@ -1,21 +1,47 @@ package org.oppia.android.util.parser import android.graphics.Bitmap +import android.graphics.drawable.Drawable import android.graphics.drawable.PictureDrawable +import androidx.annotation.DrawableRes + +/** Represents transformations for images loaded using [ImageLoader]. */ +enum class ImageTransformation { + /** Represents Blur Transformation on an [ImageTarget]. */ + BLUR +} /** Loads an image from the provided URL into the specified target, optionally caching it. */ interface ImageLoader { /** * Loads a bitmap at the specified [imageUrl] into the specified [target]. Note that this is an * asynchronous operation, and may take a while if the image needs to be downloaded from the - * internet. + * internet. Optional [transformations] may be applied to the image. */ - fun loadBitmap(imageUrl: String, target: ImageTarget) + fun loadBitmap( + imageUrl: String, + target: ImageTarget, + transformations: List = listOf() + ) /** * Loads a vector drawable at the specified [imageUrl] into the specified [target]. Note that this * is an asynchronous operation, and may take a while if the image needs to be downloaded from the - * internet. + * internet. Optional [transformations] may be applied to the image. + */ + fun loadSvg( + imageUrl: String, + target: ImageTarget, + transformations: List = listOf() + ) + + /** + * Loads the specified [imageDrawable] resource into the specified [target]. + * Optional [transformations] may be applied to the image. */ - fun loadSvg(imageUrl: String, target: ImageTarget) + fun loadDrawable( + @DrawableRes imageDrawableResId: Int, + target: ImageTarget, + transformations: List = listOf() + ) } diff --git a/utility/src/main/java/org/oppia/android/util/parser/TestGlideImageLoader.kt b/utility/src/main/java/org/oppia/android/util/parser/TestGlideImageLoader.kt new file mode 100644 index 00000000000..7d66bb65c12 --- /dev/null +++ b/utility/src/main/java/org/oppia/android/util/parser/TestGlideImageLoader.kt @@ -0,0 +1,44 @@ +package org.oppia.android.util.parser + +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.graphics.drawable.PictureDrawable +import javax.inject.Inject + +/** + * [TestGlideImageLoader] is designed to be used in tests. It uses real [GlideImageLoader] + * except for [loadDrawable]. [loadDrawable] function is overridden to work with drawable matchers + * in unit tests. + */ +class TestGlideImageLoader @Inject constructor( + private val glideImageLoader: GlideImageLoader +) : ImageLoader { + + override fun loadBitmap( + imageUrl: String, + target: ImageTarget, + transformations: List + ) = glideImageLoader.loadBitmap(imageUrl, target, transformations) + + override fun loadSvg( + imageUrl: String, + target: ImageTarget, + transformations: List + ) = glideImageLoader.loadSvg(imageUrl, target, transformations) + + /** + * [loadDrawable] can be used in tests to match drawable ids: + * `matches(withDrawable([imageDrawableResId]))`. + * + * Real [loadDrawable] in [GlideImageLoader] cannot be tested using such drawable matchers. + */ + override fun loadDrawable( + imageDrawableResId: Int, + target: ImageTarget, + transformations: List + ) { + if (target is ImageViewTarget) { + target.imageView.setImageResource(imageDrawableResId) + } + } +}