From 90205caaef9b206d72057593255d79acc4af6314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toma=CC=81s=CC=8C=20Valenta?= Date: Sun, 20 Dec 2020 15:53:41 +0100 Subject: [PATCH 1/5] Add base dynamic-image module. Add dynamic-image Coil implementation. --- build.gradle | 3 + .../twocoders/dynamic/color/DynamicColor.kt | 4 +- dynamic/color/src/main/res/values/strings.xml | 3 - dynamic/image-coil/.gitignore | 1 + dynamic/image-coil/build.gradle | 55 +++++++ dynamic/image-coil/consumer-rules.pro | 0 .../image-coil/src/main/AndroidManifest.xml | 1 + .../dynamic/image/coil/DynamicImage.kt | 136 ++++++++++++++++++ .../image/coil/DynamicImageBindingAdapter.kt | 8 ++ dynamic/image/.gitignore | 1 + dynamic/image/build.gradle | 29 ++++ dynamic/image/consumer-rules.pro | 0 dynamic/image/src/main/AndroidManifest.xml | 1 + .../dynamic/image/BaseDynamicImage.kt | 96 +++++++++++++ .../dynamic/image/component/UriComponent.kt | 33 +++++ .../com/twocoders/dynamic/text/Quantity.kt | 2 +- dynamic/text/src/main/res/values/strings.xml | 3 - settings.gradle | 4 +- 18 files changed, 369 insertions(+), 11 deletions(-) delete mode 100644 dynamic/color/src/main/res/values/strings.xml create mode 100644 dynamic/image-coil/.gitignore create mode 100644 dynamic/image-coil/build.gradle create mode 100644 dynamic/image-coil/consumer-rules.pro create mode 100644 dynamic/image-coil/src/main/AndroidManifest.xml create mode 100644 dynamic/image-coil/src/main/java/com/twocoders/dynamic/image/coil/DynamicImage.kt create mode 100644 dynamic/image-coil/src/main/java/com/twocoders/dynamic/image/coil/DynamicImageBindingAdapter.kt create mode 100644 dynamic/image/.gitignore create mode 100644 dynamic/image/build.gradle create mode 100644 dynamic/image/consumer-rules.pro create mode 100644 dynamic/image/src/main/AndroidManifest.xml create mode 100644 dynamic/image/src/main/java/com/twocoders/dynamic/image/BaseDynamicImage.kt create mode 100755 dynamic/image/src/main/java/com/twocoders/dynamic/image/component/UriComponent.kt delete mode 100644 dynamic/text/src/main/res/values/strings.xml diff --git a/build.gradle b/build.gradle index ee4dceb..6872edd 100644 --- a/build.gradle +++ b/build.gradle @@ -11,12 +11,15 @@ buildscript { androidTargerSdkVersion = androidCompileSdkVersion libDynamicColorVersion = '1.0.0' + libDynamicImageVersion = '1.0.0' + libDynamicImageCoilVersion = '1.0.0' libDynamicTextVersion = '3.0.0' kotlinVersion = '1.4.21' androidXCoreVersion = '1.3.2' annotationVersion = '1.1.0' commonExtensionsVersion = '1.0.0' + coilVersion = '1.1.0' junitVersion = '4.13.1' androidTestRunnerVersion = '1.3.0' diff --git a/dynamic/color/src/main/java/com/twocoders/dynamic/color/DynamicColor.kt b/dynamic/color/src/main/java/com/twocoders/dynamic/color/DynamicColor.kt index 26af110..e3c4014 100644 --- a/dynamic/color/src/main/java/com/twocoders/dynamic/color/DynamicColor.kt +++ b/dynamic/color/src/main/java/com/twocoders/dynamic/color/DynamicColor.kt @@ -91,9 +91,7 @@ open class DynamicColor : Parcelable { parcel.writeInt(attrRes ?: NO_ID) } - override fun describeContents(): Int { - return 0 - } + override fun describeContents() = 0 override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/dynamic/color/src/main/res/values/strings.xml b/dynamic/color/src/main/res/values/strings.xml deleted file mode 100644 index 3b233f0..0000000 --- a/dynamic/color/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - Dynamic Color - \ No newline at end of file diff --git a/dynamic/image-coil/.gitignore b/dynamic/image-coil/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/dynamic/image-coil/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/dynamic/image-coil/build.gradle b/dynamic/image-coil/build.gradle new file mode 100644 index 0000000..47c4303 --- /dev/null +++ b/dynamic/image-coil/build.gradle @@ -0,0 +1,55 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-kapt' + +ext.bintrayPublishVersion = libDynamicImageCoilVersion +apply from: '../../bintray-publish-config.gradle' + +android { + compileSdkVersion androidCompileSdkVersion + + defaultConfig { + minSdkVersion androidMinSdkVersion + targetSdkVersion androidTargerSdkVersion + versionName libDynamicImageCoilVersion + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles 'consumer-rules.pro' + } + + buildTypes { + release { + minifyEnabled false + } + } + + buildFeatures { + dataBinding = true + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8.toString() + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + api project(':dynamic:image') + + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" + implementation "androidx.annotation:annotation:$annotationVersion" + implementation "androidx.core:core-ktx:$androidXCoreVersion" + implementation "com.twocoders.extensions:common:$commonExtensionsVersion" + implementation "io.coil-kt:coil:$coilVersion" + + androidTestImplementation "androidx.test:runner:$androidTestRunnerVersion" + androidTestImplementation "androidx.test.ext:junit:$androidJunitVersion" + + testImplementation "junit:junit:$junitVersion" +} diff --git a/dynamic/image-coil/consumer-rules.pro b/dynamic/image-coil/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/dynamic/image-coil/src/main/AndroidManifest.xml b/dynamic/image-coil/src/main/AndroidManifest.xml new file mode 100644 index 0000000..ea98b05 --- /dev/null +++ b/dynamic/image-coil/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dynamic/image-coil/src/main/java/com/twocoders/dynamic/image/coil/DynamicImage.kt b/dynamic/image-coil/src/main/java/com/twocoders/dynamic/image/coil/DynamicImage.kt new file mode 100644 index 0000000..ba4bc47 --- /dev/null +++ b/dynamic/image-coil/src/main/java/com/twocoders/dynamic/image/coil/DynamicImage.kt @@ -0,0 +1,136 @@ +package com.twocoders.dynamic.image.coil + +import android.content.Context +import android.graphics.drawable.Drawable +import android.graphics.drawable.VectorDrawable +import android.os.Parcel +import android.os.Parcelable +import android.widget.ImageView +import androidx.annotation.ColorInt +import androidx.annotation.DrawableRes +import coil.ImageLoader +import coil.load +import coil.request.ImageRequest +import com.twocoders.dynamic.image.BaseDynamicImage +import com.twocoders.dynamic.image.component.UriComponent +import com.twocoders.extensions.common.getDrawable + +/** + * + * Handy class which can be used to bind image data to views using them. + * Data can be in [DrawableRes], [Drawable], or [UriComponent] format. + * + * Use one of [DynamicImage.from] creator methods to create the data + * and [DynamicImage.getDrawable] to obtain the final [Drawable] output. Or you can + * provide target [ImageView] class into the [DynamicImage.loadDrawableInto] method. + * + * This [DynamicImage] implementation uses [Coil], for other implementations please visit our GitHub. + * + */ +@Suppress("unused", "MemberVisibilityCanBePrivate") +open class DynamicImage : BaseDynamicImage { + + protected constructor( + @ColorInt imageRes: Int? = null, + imageDrawable: Drawable? = null, + imageUri: UriComponent? = null + ) : super(imageRes, imageDrawable, imageUri) + + protected constructor(parcel: Parcel) : super(parcel) + + companion object { + + val EMPTY = DynamicImage() + + @JvmStatic + fun from(@DrawableRes imageRes: Int) = DynamicImage(imageRes = imageRes) + + @JvmStatic + fun from(imageDrawable: Drawable) = DynamicImage(imageDrawable = imageDrawable) + + @JvmStatic + fun from(imageUri: UriComponent) = DynamicImage(imageUri = imageUri) + + @JvmField + val CREATOR = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel) = DynamicImage(parcel) + override fun newArray(size: Int): Array = arrayOfNulls(size) + } + } + + override suspend fun getDrawable(context: Context): Drawable? { + imageUri?.let { uri -> + val request = ImageRequest.Builder(context) + .data(uri) + .build() + return ImageLoader(context).execute(request).drawable + } + + imageDrawable?.let { drawable -> + return drawable + } + + imageRes?.let { res -> + return context.getDrawable(drawableResId = res) + } + + return null + } + + override fun getDrawable(context: Context, callback: (drawable: Drawable) -> Unit) { + imageUri?.let { imageUriComponent -> + val request = ImageRequest.Builder(context) + .data(imageUriComponent.image) + .target { callback(it) } + .build() + ImageLoader(context).enqueue(request) + } + + imageDrawable?.let { drawable -> + callback(drawable) + } + + imageRes?.let { res -> + context.getDrawable(drawableResId = res)?.let { callback(it) } + } + } + + override fun loadDrawableInto( + imageView: ImageView, + withCrossFade: Boolean + ) { + imageUri?.let { imageUriComponent -> + imageView.load(imageUriComponent.image) { + crossfade(withCrossFade) + imageUriComponent.placeholderImage?.let { placeholder(it) } + imageUriComponent.errorImage?.let { error(it) } + } + } + + imageDrawable?.let { + // Note: Coil has currently issue with loading vectors + if (it is VectorDrawable) { + imageView.setImageDrawable(it) + } else { + imageView.load(it) { + crossfade(withCrossFade) + } + } + } + + imageRes?.let { + // We need to get drawable before setting it to imageView through Coil, + // there is a problem with background tint from theme attribute + getDrawable(imageView.context) { drawable -> + // Note: Coil has currently issue with loading vectors + if (drawable is VectorDrawable) { + imageView.setImageDrawable(drawable) + } else { + imageView.load(drawable) { + crossfade(withCrossFade) + } + } + } + } + } +} \ No newline at end of file diff --git a/dynamic/image-coil/src/main/java/com/twocoders/dynamic/image/coil/DynamicImageBindingAdapter.kt b/dynamic/image-coil/src/main/java/com/twocoders/dynamic/image/coil/DynamicImageBindingAdapter.kt new file mode 100644 index 0000000..501f221 --- /dev/null +++ b/dynamic/image-coil/src/main/java/com/twocoders/dynamic/image/coil/DynamicImageBindingAdapter.kt @@ -0,0 +1,8 @@ +package com.twocoders.dynamic.image.coil + +import android.widget.ImageView +import androidx.databinding.BindingAdapter + +@BindingAdapter(value = ["android:src", "loadWithCrossFade"], requireAll = false) +fun ImageView.loadDynamicImage(image: DynamicImage, withCrossFade: Boolean = false) = + image.loadDrawableInto(this, withCrossFade) \ No newline at end of file diff --git a/dynamic/image/.gitignore b/dynamic/image/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/dynamic/image/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/dynamic/image/build.gradle b/dynamic/image/build.gradle new file mode 100644 index 0000000..6449be8 --- /dev/null +++ b/dynamic/image/build.gradle @@ -0,0 +1,29 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-kapt' + +android { + compileSdkVersion androidCompileSdkVersion + + defaultConfig { + minSdkVersion androidMinSdkVersion + targetSdkVersion androidTargerSdkVersion + versionName libDynamicImageVersion + + consumerProguardFiles 'consumer-rules.pro' + } + + buildTypes { + release { + minifyEnabled false + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" + implementation "androidx.annotation:annotation:$annotationVersion" + implementation "com.twocoders.extensions:common:$commonExtensionsVersion" +} diff --git a/dynamic/image/consumer-rules.pro b/dynamic/image/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/dynamic/image/src/main/AndroidManifest.xml b/dynamic/image/src/main/AndroidManifest.xml new file mode 100644 index 0000000..e447999 --- /dev/null +++ b/dynamic/image/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dynamic/image/src/main/java/com/twocoders/dynamic/image/BaseDynamicImage.kt b/dynamic/image/src/main/java/com/twocoders/dynamic/image/BaseDynamicImage.kt new file mode 100644 index 0000000..9e448b7 --- /dev/null +++ b/dynamic/image/src/main/java/com/twocoders/dynamic/image/BaseDynamicImage.kt @@ -0,0 +1,96 @@ +package com.twocoders.dynamic.image + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable +import android.os.Parcel +import android.os.Parcelable +import android.widget.ImageView +import androidx.annotation.ColorInt +import androidx.annotation.DrawableRes +import com.twocoders.dynamic.image.component.UriComponent +import com.twocoders.extensions.common.NO_ID +import com.twocoders.extensions.common.logd + +/** + * + * Handy class which can be used to bind image data to views using them. + * Data can be in [DrawableRes], [Drawable], or [UriComponent] format. + */ +@Suppress("unused", "MemberVisibilityCanBePrivate") +abstract class BaseDynamicImage : Parcelable { + + @DrawableRes protected val imageRes: Int? + protected val imageDrawable: Drawable? + protected val imageUri: UriComponent? + + protected constructor( + @ColorInt imageRes: Int? = null, + imageDrawable: Drawable? = null, + imageUri: UriComponent? = null + ) { + this.imageRes = imageRes + this.imageDrawable = imageDrawable + this.imageUri = imageUri + } + + protected constructor(parcel: Parcel) : this( + parcel.readInt(), + parcel.readDrawable(), + parcel.readParcelable(UriComponent::class.java.classLoader) + ) + + abstract suspend fun getDrawable(context: Context): Drawable? + + abstract fun getDrawable(context: Context, callback: (drawable: Drawable) -> Unit) + + abstract fun loadDrawableInto(imageView: ImageView, withCrossFade: Boolean = false) + + fun isEmpty() = imageRes == null && imageDrawable == null && imageUri == null + + fun isNotEmpty() = !isEmpty() + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeInt(imageRes ?: NO_ID) + parcel.writeParcelable(imageDrawable.getParcelable(), flags) + parcel.writeParcelable(imageUri, flags) + } + + override fun describeContents() = 0 + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as BaseDynamicImage + + if (imageRes != other.imageRes) return false + if (imageDrawable != other.imageDrawable) return false + if (imageUri != other.imageUri) return false + + return true + } + + override fun hashCode(): Int { + var result = imageRes ?: NO_ID + result = 31 * result + imageDrawable.hashCode() + result = 31 * result + imageUri.hashCode() + return result + } +} + +private fun Parcel.readDrawable(): Drawable? = + when (val parcelDrawable = readParcelable(BaseDynamicImage::class.java.classLoader)) { + is Bitmap -> @Suppress("DEPRECATION") BitmapDrawable(parcelDrawable) + else -> null + } + +private fun Drawable?.getParcelable(): Parcelable? = when (this) { + is BitmapDrawable -> bitmap + is Drawable -> { + logd("Unsupported $this in imageDrawable for parcel, only BitmapDrawable is supported now.") + null + } + else -> null +} \ No newline at end of file diff --git a/dynamic/image/src/main/java/com/twocoders/dynamic/image/component/UriComponent.kt b/dynamic/image/src/main/java/com/twocoders/dynamic/image/component/UriComponent.kt new file mode 100755 index 0000000..513c89b --- /dev/null +++ b/dynamic/image/src/main/java/com/twocoders/dynamic/image/component/UriComponent.kt @@ -0,0 +1,33 @@ +package com.twocoders.dynamic.image.component + +import android.net.Uri +import android.os.Parcel +import android.os.Parcelable +import androidx.annotation.DrawableRes +import com.twocoders.extensions.common.NO_ID + +data class UriComponent( + val image: Uri, + @DrawableRes val errorImage: Int? = null, + @DrawableRes val placeholderImage: Int? = null +) : Parcelable { + + constructor(parcel: Parcel) : this( + parcel.readParcelable(Uri::class.java.classLoader)!!, + parcel.readInt(), + parcel.readInt() + ) + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel) = UriComponent(parcel) + override fun newArray(size: Int): Array = arrayOfNulls(size) + } + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeParcelable(image, flags) + parcel.writeInt(errorImage ?: NO_ID) + parcel.writeInt(placeholderImage ?: NO_ID) + } + + override fun describeContents() = 0 +} \ No newline at end of file diff --git a/dynamic/text/src/main/java/com/twocoders/dynamic/text/Quantity.kt b/dynamic/text/src/main/java/com/twocoders/dynamic/text/Quantity.kt index 5484166..e68c9ee 100644 --- a/dynamic/text/src/main/java/com/twocoders/dynamic/text/Quantity.kt +++ b/dynamic/text/src/main/java/com/twocoders/dynamic/text/Quantity.kt @@ -37,7 +37,7 @@ data class Quantity(val number: Int, val useInText: Boolean = false) : Parcelabl ParcelCompat.writeBoolean(dest, useInText) } - override fun describeContents(): Int = 0 + override fun describeContents() = 0 companion object { @JvmField diff --git a/dynamic/text/src/main/res/values/strings.xml b/dynamic/text/src/main/res/values/strings.xml deleted file mode 100644 index 9cacc32..0000000 --- a/dynamic/text/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - Dynamic Text - diff --git a/settings.gradle b/settings.gradle index 996b560..d497743 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,4 @@ include ':dynamic:text', - ':dynamic:color' + ':dynamic:color', + ':dynamic:image', + ':dynamic:image-coil' From 8ce6c1048d1bc8757dd8dc28d27bad75dff16c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toma=CC=81s=CC=8C=20Valenta?= Date: Sun, 20 Dec 2020 17:12:50 +0100 Subject: [PATCH 2/5] Add initial dynamic-image Glide and Picasso implementation. --- build.gradle | 4 + dynamic/image-coil/build.gradle | 1 + .../dynamic/image/coil/DynamicImage.kt | 20 +-- dynamic/image-glide/.gitignore | 1 + dynamic/image-glide/build.gradle | 56 ++++++++ dynamic/image-glide/consumer-rules.pro | 0 .../image-glide/src/main/AndroidManifest.xml | 1 + .../dynamic/image/glide/DynamicImage.kt | 126 ++++++++++++++++++ .../image/glide/DynamicImageBindingAdapter.kt | 8 ++ dynamic/image-picasso/.gitignore | 1 + dynamic/image-picasso/build.gradle | 59 ++++++++ dynamic/image-picasso/consumer-rules.pro | 0 .../src/main/AndroidManifest.xml | 1 + .../dynamic/image/picasso/DynamicImage.kt | 111 +++++++++++++++ .../picasso/DynamicImageBindingAdapter.kt | 8 ++ .../dynamic/image/BaseDynamicImage.kt | 26 +++- settings.gradle | 4 +- 17 files changed, 407 insertions(+), 20 deletions(-) create mode 100644 dynamic/image-glide/.gitignore create mode 100644 dynamic/image-glide/build.gradle create mode 100644 dynamic/image-glide/consumer-rules.pro create mode 100644 dynamic/image-glide/src/main/AndroidManifest.xml create mode 100644 dynamic/image-glide/src/main/java/com/twocoders/dynamic/image/glide/DynamicImage.kt create mode 100644 dynamic/image-glide/src/main/java/com/twocoders/dynamic/image/glide/DynamicImageBindingAdapter.kt create mode 100644 dynamic/image-picasso/.gitignore create mode 100644 dynamic/image-picasso/build.gradle create mode 100644 dynamic/image-picasso/consumer-rules.pro create mode 100644 dynamic/image-picasso/src/main/AndroidManifest.xml create mode 100644 dynamic/image-picasso/src/main/java/com/twocoders/dynamic/image/picasso/DynamicImage.kt create mode 100644 dynamic/image-picasso/src/main/java/com/twocoders/dynamic/image/picasso/DynamicImageBindingAdapter.kt diff --git a/build.gradle b/build.gradle index 6872edd..9b3f974 100644 --- a/build.gradle +++ b/build.gradle @@ -13,6 +13,8 @@ buildscript { libDynamicColorVersion = '1.0.0' libDynamicImageVersion = '1.0.0' libDynamicImageCoilVersion = '1.0.0' + libDynamicImageGlideVersion = '1.0.0' + libDynamicImagePicassoVersion = '1.0.0' libDynamicTextVersion = '3.0.0' kotlinVersion = '1.4.21' @@ -20,6 +22,8 @@ buildscript { annotationVersion = '1.1.0' commonExtensionsVersion = '1.0.0' coilVersion = '1.1.0' + glideVersion = '4.11.0' + picassoVersion = '2.71828' junitVersion = '4.13.1' androidTestRunnerVersion = '1.3.0' diff --git a/dynamic/image-coil/build.gradle b/dynamic/image-coil/build.gradle index 47c4303..bf116e4 100644 --- a/dynamic/image-coil/build.gradle +++ b/dynamic/image-coil/build.gradle @@ -46,6 +46,7 @@ dependencies { implementation "androidx.annotation:annotation:$annotationVersion" implementation "androidx.core:core-ktx:$androidXCoreVersion" implementation "com.twocoders.extensions:common:$commonExtensionsVersion" + implementation "io.coil-kt:coil:$coilVersion" androidTestImplementation "androidx.test:runner:$androidTestRunnerVersion" diff --git a/dynamic/image-coil/src/main/java/com/twocoders/dynamic/image/coil/DynamicImage.kt b/dynamic/image-coil/src/main/java/com/twocoders/dynamic/image/coil/DynamicImage.kt index ba4bc47..619ebfe 100644 --- a/dynamic/image-coil/src/main/java/com/twocoders/dynamic/image/coil/DynamicImage.kt +++ b/dynamic/image-coil/src/main/java/com/twocoders/dynamic/image/coil/DynamicImage.kt @@ -8,12 +8,12 @@ import android.os.Parcelable import android.widget.ImageView import androidx.annotation.ColorInt import androidx.annotation.DrawableRes +import coil.Coil import coil.ImageLoader import coil.load import coil.request.ImageRequest import com.twocoders.dynamic.image.BaseDynamicImage import com.twocoders.dynamic.image.component.UriComponent -import com.twocoders.extensions.common.getDrawable /** * @@ -66,15 +66,7 @@ open class DynamicImage : BaseDynamicImage { return ImageLoader(context).execute(request).drawable } - imageDrawable?.let { drawable -> - return drawable - } - - imageRes?.let { res -> - return context.getDrawable(drawableResId = res) - } - - return null + return super.getDrawable(context) } override fun getDrawable(context: Context, callback: (drawable: Drawable) -> Unit) { @@ -86,13 +78,7 @@ open class DynamicImage : BaseDynamicImage { ImageLoader(context).enqueue(request) } - imageDrawable?.let { drawable -> - callback(drawable) - } - - imageRes?.let { res -> - context.getDrawable(drawableResId = res)?.let { callback(it) } - } + super.getDrawable(context, callback) } override fun loadDrawableInto( diff --git a/dynamic/image-glide/.gitignore b/dynamic/image-glide/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/dynamic/image-glide/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/dynamic/image-glide/build.gradle b/dynamic/image-glide/build.gradle new file mode 100644 index 0000000..1a89a9c --- /dev/null +++ b/dynamic/image-glide/build.gradle @@ -0,0 +1,56 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-kapt' + +ext.bintrayPublishVersion = libDynamicImageGlideVersion +apply from: '../../bintray-publish-config.gradle' + +android { + compileSdkVersion androidCompileSdkVersion + + defaultConfig { + minSdkVersion androidMinSdkVersion + targetSdkVersion androidTargerSdkVersion + versionName libDynamicImageGlideVersion + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles 'consumer-rules.pro' + } + + buildTypes { + release { + minifyEnabled false + } + } + + buildFeatures { + dataBinding = true + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8.toString() + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + api project(':dynamic:image') + + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" + implementation "androidx.annotation:annotation:$annotationVersion" + implementation "androidx.core:core-ktx:$androidXCoreVersion" + implementation "com.twocoders.extensions:common:$commonExtensionsVersion" + + implementation "com.github.bumptech.glide:glide:$glideVersion" + + androidTestImplementation "androidx.test:runner:$androidTestRunnerVersion" + androidTestImplementation "androidx.test.ext:junit:$androidJunitVersion" + + testImplementation "junit:junit:$junitVersion" +} diff --git a/dynamic/image-glide/consumer-rules.pro b/dynamic/image-glide/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/dynamic/image-glide/src/main/AndroidManifest.xml b/dynamic/image-glide/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8f373ac --- /dev/null +++ b/dynamic/image-glide/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dynamic/image-glide/src/main/java/com/twocoders/dynamic/image/glide/DynamicImage.kt b/dynamic/image-glide/src/main/java/com/twocoders/dynamic/image/glide/DynamicImage.kt new file mode 100644 index 0000000..5c4e461 --- /dev/null +++ b/dynamic/image-glide/src/main/java/com/twocoders/dynamic/image/glide/DynamicImage.kt @@ -0,0 +1,126 @@ +package com.twocoders.dynamic.image.glide + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable +import android.os.Parcel +import android.os.Parcelable +import android.widget.ImageView +import androidx.annotation.ColorInt +import androidx.annotation.DrawableRes +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import com.twocoders.dynamic.image.BaseDynamicImage +import com.twocoders.dynamic.image.component.UriComponent + +/** + * + * Handy class which can be used to bind image data to views using them. + * Data can be in [DrawableRes], [Drawable], or [UriComponent] format. + * + * Use one of [DynamicImage.from] creator methods to create the data + * and [DynamicImage.getDrawable] to obtain the final [Drawable] output. Or you can + * provide target [ImageView] class into the [DynamicImage.loadDrawableInto] method. + * + * This [DynamicImage] implementation uses [Glide], for other implementations please visit our GitHub. + * + */ +@Suppress("unused", "MemberVisibilityCanBePrivate") +open class DynamicImage : BaseDynamicImage { + + protected constructor( + @ColorInt imageRes: Int? = null, + imageDrawable: Drawable? = null, + imageUri: UriComponent? = null + ) : super(imageRes, imageDrawable, imageUri) + + protected constructor(parcel: Parcel) : super(parcel) + + companion object { + + val EMPTY = DynamicImage() + + @JvmStatic + fun from(@DrawableRes imageRes: Int) = DynamicImage(imageRes = imageRes) + + @JvmStatic + fun from(imageDrawable: Drawable) = DynamicImage(imageDrawable = imageDrawable) + + @JvmStatic + fun from(imageUri: UriComponent) = DynamicImage(imageUri = imageUri) + + @JvmField + val CREATOR = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel) = DynamicImage(parcel) + override fun newArray(size: Int): Array = arrayOfNulls(size) + } + } + + override suspend fun getDrawable(context: Context): Drawable? { + imageUri?.let { uri -> + return BitmapDrawable( + context.resources, + Glide.with(context) + .asBitmap() + .load(uri.image) + .submit() + .get() + ) + } + + return super.getDrawable(context) + } + + override fun getDrawable(context: Context, callback: (drawable: Drawable) -> Unit) { + imageUri?.let { imageUriComponent -> + Glide.with(context) + .asBitmap() + .load(imageUriComponent.image) + .into(object : CustomTarget(){ + override fun onResourceReady(resource: Bitmap, transition: Transition?) { + callback(BitmapDrawable(context.resources, resource)) + } + override fun onLoadCleared(placeholder: Drawable?) {} + }) + } + + super.getDrawable(context, callback) + } + + override fun loadDrawableInto( + imageView: ImageView, + withCrossFade: Boolean + ) { + imageUri?.let { imageUriComponent -> + Glide.with(imageView.context) + .load(imageUriComponent.image) + .apply { + if (withCrossFade) transition(DrawableTransitionOptions.withCrossFade()) + imageUriComponent.placeholderImage?.let { placeholder(it) } + imageUriComponent.errorImage?.let { error(it) } + } + .into(imageView) + } + + imageDrawable?.let { + Glide.with(imageView.context) + .load(it) + .apply { + if (withCrossFade) transition(DrawableTransitionOptions.withCrossFade()) + } + .into(imageView) + } + + imageRes?.let { + Glide.with(imageView.context) + .load(it) + .apply { + if (withCrossFade) transition(DrawableTransitionOptions.withCrossFade()) + } + .into(imageView) + } + } +} \ No newline at end of file diff --git a/dynamic/image-glide/src/main/java/com/twocoders/dynamic/image/glide/DynamicImageBindingAdapter.kt b/dynamic/image-glide/src/main/java/com/twocoders/dynamic/image/glide/DynamicImageBindingAdapter.kt new file mode 100644 index 0000000..54cc7ff --- /dev/null +++ b/dynamic/image-glide/src/main/java/com/twocoders/dynamic/image/glide/DynamicImageBindingAdapter.kt @@ -0,0 +1,8 @@ +package com.twocoders.dynamic.image.glide + +import android.widget.ImageView +import androidx.databinding.BindingAdapter + +@BindingAdapter(value = ["android:src", "loadWithCrossFade"], requireAll = false) +fun ImageView.loadDynamicImage(image: DynamicImage, withCrossFade: Boolean = false) = + image.loadDrawableInto(this, withCrossFade) \ No newline at end of file diff --git a/dynamic/image-picasso/.gitignore b/dynamic/image-picasso/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/dynamic/image-picasso/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/dynamic/image-picasso/build.gradle b/dynamic/image-picasso/build.gradle new file mode 100644 index 0000000..0455f87 --- /dev/null +++ b/dynamic/image-picasso/build.gradle @@ -0,0 +1,59 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-kapt' + +ext.bintrayPublishVersion = libDynamicImagePicassoVersion +apply from: '../../bintray-publish-config.gradle' + +android { + compileSdkVersion androidCompileSdkVersion + + defaultConfig { + minSdkVersion androidMinSdkVersion + targetSdkVersion androidTargerSdkVersion + versionName libDynamicImagePicassoVersion + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles 'consumer-rules.pro' + } + + buildTypes { + release { + minifyEnabled false + } + } + + buildFeatures { + dataBinding = true + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8.toString() + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + api project(':dynamic:image') + + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" + implementation "androidx.annotation:annotation:$annotationVersion" + implementation "androidx.core:core-ktx:$androidXCoreVersion" + implementation "com.twocoders.extensions:common:$commonExtensionsVersion" + + implementation ("com.squareup.picasso:picasso:$picassoVersion") { + exclude group: 'com.android.support' + exclude module: ['exifinterface', 'support-annotations'] + } + + androidTestImplementation "androidx.test:runner:$androidTestRunnerVersion" + androidTestImplementation "androidx.test.ext:junit:$androidJunitVersion" + + testImplementation "junit:junit:$junitVersion" +} diff --git a/dynamic/image-picasso/consumer-rules.pro b/dynamic/image-picasso/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/dynamic/image-picasso/src/main/AndroidManifest.xml b/dynamic/image-picasso/src/main/AndroidManifest.xml new file mode 100644 index 0000000..08f73e3 --- /dev/null +++ b/dynamic/image-picasso/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dynamic/image-picasso/src/main/java/com/twocoders/dynamic/image/picasso/DynamicImage.kt b/dynamic/image-picasso/src/main/java/com/twocoders/dynamic/image/picasso/DynamicImage.kt new file mode 100644 index 0000000..9ada7b9 --- /dev/null +++ b/dynamic/image-picasso/src/main/java/com/twocoders/dynamic/image/picasso/DynamicImage.kt @@ -0,0 +1,111 @@ +package com.twocoders.dynamic.image.picasso + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable +import android.os.Parcel +import android.os.Parcelable +import android.widget.ImageView +import androidx.annotation.ColorInt +import androidx.annotation.DrawableRes +import com.squareup.picasso.Picasso +import com.squareup.picasso.Target +import com.twocoders.dynamic.image.BaseDynamicImage +import com.twocoders.dynamic.image.component.UriComponent + +/** + * + * Handy class which can be used to bind image data to views using them. + * Data can be in [DrawableRes], [Drawable], or [UriComponent] format. + * + * Use one of [DynamicImage.from] creator methods to create the data + * and [DynamicImage.getDrawable] to obtain the final [Drawable] output. Or you can + * provide target [ImageView] class into the [DynamicImage.loadDrawableInto] method. + * + * This [DynamicImage] implementation uses [Picasso], for other implementations please visit our GitHub. + * + */ +@Suppress("unused", "MemberVisibilityCanBePrivate") +open class DynamicImage : BaseDynamicImage { + + protected constructor( + @ColorInt imageRes: Int? = null, + imageDrawable: Drawable? = null, + imageUri: UriComponent? = null + ) : super(imageRes, imageDrawable, imageUri) + + protected constructor(parcel: Parcel) : super(parcel) + + companion object { + + val EMPTY = DynamicImage() + + @JvmStatic + fun from(@DrawableRes imageRes: Int) = DynamicImage(imageRes = imageRes) + + @JvmStatic + fun from(imageDrawable: Drawable) = DynamicImage(imageDrawable = imageDrawable) + + @JvmStatic + fun from(imageUri: UriComponent) = DynamicImage(imageUri = imageUri) + + @JvmField + val CREATOR = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel) = DynamicImage(parcel) + override fun newArray(size: Int): Array = arrayOfNulls(size) + } + } + + override suspend fun getDrawable(context: Context): Drawable? { + imageUri?.let { uri -> + return BitmapDrawable( + context.resources, + Picasso.get() + .load(uri.image) + .get() + ) + } + + return super.getDrawable(context) + } + + override fun getDrawable(context: Context, callback: (drawable: Drawable) -> Unit) { + imageUri?.let { imageUriComponent -> + Picasso.get() + .load(imageUriComponent.image) + .into(object : Target { + override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) { + callback(BitmapDrawable(context.resources, bitmap)) + } + override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) {} + override fun onPrepareLoad(placeHolderDrawable: Drawable?) {} + }) + } + + super.getDrawable(context, callback) + } + + override fun loadDrawableInto( + imageView: ImageView, + withCrossFade: Boolean + ) { + imageUri?.let { imageUriComponent -> + Picasso.get() + .load(imageUriComponent.image) + .apply { + imageUriComponent.placeholderImage?.let { placeholder(it) } + imageUriComponent.errorImage?.let { error(it) } + } + .into(imageView) + } + + imageDrawable?.let { + imageView.setImageDrawable(it) + } + + imageRes?.let { + Picasso.get().load(it).into(imageView) + } + } +} \ No newline at end of file diff --git a/dynamic/image-picasso/src/main/java/com/twocoders/dynamic/image/picasso/DynamicImageBindingAdapter.kt b/dynamic/image-picasso/src/main/java/com/twocoders/dynamic/image/picasso/DynamicImageBindingAdapter.kt new file mode 100644 index 0000000..ff850ff --- /dev/null +++ b/dynamic/image-picasso/src/main/java/com/twocoders/dynamic/image/picasso/DynamicImageBindingAdapter.kt @@ -0,0 +1,8 @@ +package com.twocoders.dynamic.image.picasso + +import android.widget.ImageView +import androidx.databinding.BindingAdapter + +@BindingAdapter(value = ["android:src", "loadWithCrossFade"], requireAll = false) +fun ImageView.loadDynamicImage(image: DynamicImage, withCrossFade: Boolean = false) = + image.loadDrawableInto(this, withCrossFade) \ No newline at end of file diff --git a/dynamic/image/src/main/java/com/twocoders/dynamic/image/BaseDynamicImage.kt b/dynamic/image/src/main/java/com/twocoders/dynamic/image/BaseDynamicImage.kt index 9e448b7..2dfa156 100644 --- a/dynamic/image/src/main/java/com/twocoders/dynamic/image/BaseDynamicImage.kt +++ b/dynamic/image/src/main/java/com/twocoders/dynamic/image/BaseDynamicImage.kt @@ -7,10 +7,12 @@ import android.graphics.drawable.Drawable import android.os.Parcel import android.os.Parcelable import android.widget.ImageView +import androidx.annotation.CallSuper import androidx.annotation.ColorInt import androidx.annotation.DrawableRes import com.twocoders.dynamic.image.component.UriComponent import com.twocoders.extensions.common.NO_ID +import com.twocoders.extensions.common.getDrawable import com.twocoders.extensions.common.logd /** @@ -41,9 +43,29 @@ abstract class BaseDynamicImage : Parcelable { parcel.readParcelable(UriComponent::class.java.classLoader) ) - abstract suspend fun getDrawable(context: Context): Drawable? + @CallSuper + open suspend fun getDrawable(context: Context): Drawable? { + imageDrawable?.let { drawable -> + return drawable + } - abstract fun getDrawable(context: Context, callback: (drawable: Drawable) -> Unit) + imageRes?.let { res -> + return context.getDrawable(drawableResId = res) + } + + return null + } + + @CallSuper + open fun getDrawable(context: Context, callback: (drawable: Drawable) -> Unit) { + imageDrawable?.let { drawable -> + callback(drawable) + } + + imageRes?.let { res -> + context.getDrawable(drawableResId = res)?.let { callback(it) } + } + } abstract fun loadDrawableInto(imageView: ImageView, withCrossFade: Boolean = false) diff --git a/settings.gradle b/settings.gradle index d497743..adb7023 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,6 @@ include ':dynamic:text', ':dynamic:color', ':dynamic:image', - ':dynamic:image-coil' + ':dynamic:image-coil', + ':dynamic:image-glide', + ':dynamic:image-picasso' From 089fb90e013a66967740ad7e49ca0c47e457d483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toma=CC=81s=CC=8C=20Valenta?= Date: Tue, 5 Jan 2021 16:32:51 +0100 Subject: [PATCH 3/5] Rename dynamic-image to dynamic-image-base. Improve obtaining uri image in BaseDynamicImage and it's implementations. Fix project url. Improve DynamicImage class docs. --- build.gradle | 3 +- dynamic/{image => image-base}/.gitignore | 0 dynamic/{image => image-base}/build.gradle | 5 +- .../{image => image-base}/consumer-rules.pro | 0 .../image-base/src/main/AndroidManifest.xml | 1 + .../dynamic/image/base}/BaseDynamicImage.kt | 28 ++++++-- .../image/base}/DynamicImageBindingAdapter.kt | 4 +- .../image/base}/component/UriComponent.kt | 2 +- dynamic/image-coil/build.gradle | 2 +- .../dynamic/image/coil/DynamicImage.kt | 41 ++++++------ dynamic/image-glide/build.gradle | 2 +- .../dynamic/image/glide/DynamicImage.kt | 64 +++++++++---------- .../image/glide/DynamicImageBindingAdapter.kt | 8 --- dynamic/image-picasso/build.gradle | 2 +- .../dynamic/image/picasso/DynamicImage.kt | 60 +++++++++-------- .../picasso/DynamicImageBindingAdapter.kt | 8 --- dynamic/image/src/main/AndroidManifest.xml | 1 - settings.gradle | 2 +- 18 files changed, 116 insertions(+), 117 deletions(-) rename dynamic/{image => image-base}/.gitignore (100%) rename dynamic/{image => image-base}/build.gradle (92%) rename dynamic/{image => image-base}/consumer-rules.pro (100%) create mode 100644 dynamic/image-base/src/main/AndroidManifest.xml rename dynamic/{image/src/main/java/com/twocoders/dynamic/image => image-base/src/main/java/com/twocoders/dynamic/image/base}/BaseDynamicImage.kt (82%) rename dynamic/{image-coil/src/main/java/com/twocoders/dynamic/image/coil => image-base/src/main/java/com/twocoders/dynamic/image/base}/DynamicImageBindingAdapter.kt (61%) rename dynamic/{image/src/main/java/com/twocoders/dynamic/image => image-base/src/main/java/com/twocoders/dynamic/image/base}/component/UriComponent.kt (94%) delete mode 100644 dynamic/image-glide/src/main/java/com/twocoders/dynamic/image/glide/DynamicImageBindingAdapter.kt delete mode 100644 dynamic/image-picasso/src/main/java/com/twocoders/dynamic/image/picasso/DynamicImageBindingAdapter.kt delete mode 100644 dynamic/image/src/main/AndroidManifest.xml diff --git a/build.gradle b/build.gradle index 9b3f974..e679601 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ buildscript { ext { organization = 'twocoders' - projectWebsite = 'https://github.com/Two-Coders/android-dynamic-componets/' + projectWebsite = 'https://github.com/Two-Coders/android-dynamic-components/' projectDescription = 'Project containing useful Dynamic components.' projectLicences = ['MIT'] @@ -11,7 +11,6 @@ buildscript { androidTargerSdkVersion = androidCompileSdkVersion libDynamicColorVersion = '1.0.0' - libDynamicImageVersion = '1.0.0' libDynamicImageCoilVersion = '1.0.0' libDynamicImageGlideVersion = '1.0.0' libDynamicImagePicassoVersion = '1.0.0' diff --git a/dynamic/image/.gitignore b/dynamic/image-base/.gitignore similarity index 100% rename from dynamic/image/.gitignore rename to dynamic/image-base/.gitignore diff --git a/dynamic/image/build.gradle b/dynamic/image-base/build.gradle similarity index 92% rename from dynamic/image/build.gradle rename to dynamic/image-base/build.gradle index 6449be8..82306de 100644 --- a/dynamic/image/build.gradle +++ b/dynamic/image-base/build.gradle @@ -8,11 +8,14 @@ android { defaultConfig { minSdkVersion androidMinSdkVersion targetSdkVersion androidTargerSdkVersion - versionName libDynamicImageVersion consumerProguardFiles 'consumer-rules.pro' } + buildFeatures { + dataBinding = true + } + buildTypes { release { minifyEnabled false diff --git a/dynamic/image/consumer-rules.pro b/dynamic/image-base/consumer-rules.pro similarity index 100% rename from dynamic/image/consumer-rules.pro rename to dynamic/image-base/consumer-rules.pro diff --git a/dynamic/image-base/src/main/AndroidManifest.xml b/dynamic/image-base/src/main/AndroidManifest.xml new file mode 100644 index 0000000..ae67ed8 --- /dev/null +++ b/dynamic/image-base/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dynamic/image/src/main/java/com/twocoders/dynamic/image/BaseDynamicImage.kt b/dynamic/image-base/src/main/java/com/twocoders/dynamic/image/base/BaseDynamicImage.kt similarity index 82% rename from dynamic/image/src/main/java/com/twocoders/dynamic/image/BaseDynamicImage.kt rename to dynamic/image-base/src/main/java/com/twocoders/dynamic/image/base/BaseDynamicImage.kt index 2dfa156..9af417b 100644 --- a/dynamic/image/src/main/java/com/twocoders/dynamic/image/BaseDynamicImage.kt +++ b/dynamic/image-base/src/main/java/com/twocoders/dynamic/image/base/BaseDynamicImage.kt @@ -1,4 +1,4 @@ -package com.twocoders.dynamic.image +package com.twocoders.dynamic.image.base import android.content.Context import android.graphics.Bitmap @@ -7,17 +7,16 @@ import android.graphics.drawable.Drawable import android.os.Parcel import android.os.Parcelable import android.widget.ImageView -import androidx.annotation.CallSuper import androidx.annotation.ColorInt import androidx.annotation.DrawableRes -import com.twocoders.dynamic.image.component.UriComponent +import com.twocoders.dynamic.image.base.component.UriComponent import com.twocoders.extensions.common.NO_ID import com.twocoders.extensions.common.getDrawable import com.twocoders.extensions.common.logd /** * - * Handy class which can be used to bind image data to views using them. + * Handy class which can be used to bind image data to views. * Data can be in [DrawableRes], [Drawable], or [UriComponent] format. */ @Suppress("unused", "MemberVisibilityCanBePrivate") @@ -43,8 +42,22 @@ abstract class BaseDynamicImage : Parcelable { parcel.readParcelable(UriComponent::class.java.classLoader) ) - @CallSuper + protected abstract suspend fun getDrawableFromUri( + context: Context, + imageUriComponent: UriComponent + ): Drawable? + + protected abstract fun getDrawableFromUri( + context: Context, + imageUriComponent: UriComponent, + callback: (drawable: Drawable) -> Unit + ) + open suspend fun getDrawable(context: Context): Drawable? { + imageUri?.let { imageUriComponent -> + return getDrawableFromUri(context, imageUriComponent) + } + imageDrawable?.let { drawable -> return drawable } @@ -56,8 +69,11 @@ abstract class BaseDynamicImage : Parcelable { return null } - @CallSuper open fun getDrawable(context: Context, callback: (drawable: Drawable) -> Unit) { + imageUri?.let { imageUriComponent -> + getDrawableFromUri(context, imageUriComponent, callback) + } + imageDrawable?.let { drawable -> callback(drawable) } diff --git a/dynamic/image-coil/src/main/java/com/twocoders/dynamic/image/coil/DynamicImageBindingAdapter.kt b/dynamic/image-base/src/main/java/com/twocoders/dynamic/image/base/DynamicImageBindingAdapter.kt similarity index 61% rename from dynamic/image-coil/src/main/java/com/twocoders/dynamic/image/coil/DynamicImageBindingAdapter.kt rename to dynamic/image-base/src/main/java/com/twocoders/dynamic/image/base/DynamicImageBindingAdapter.kt index 501f221..a9bce11 100644 --- a/dynamic/image-coil/src/main/java/com/twocoders/dynamic/image/coil/DynamicImageBindingAdapter.kt +++ b/dynamic/image-base/src/main/java/com/twocoders/dynamic/image/base/DynamicImageBindingAdapter.kt @@ -1,8 +1,8 @@ -package com.twocoders.dynamic.image.coil +package com.twocoders.dynamic.image.base import android.widget.ImageView import androidx.databinding.BindingAdapter @BindingAdapter(value = ["android:src", "loadWithCrossFade"], requireAll = false) -fun ImageView.loadDynamicImage(image: DynamicImage, withCrossFade: Boolean = false) = +fun ImageView.loadDynamicImage(image: BaseDynamicImage, withCrossFade: Boolean = false) = image.loadDrawableInto(this, withCrossFade) \ No newline at end of file diff --git a/dynamic/image/src/main/java/com/twocoders/dynamic/image/component/UriComponent.kt b/dynamic/image-base/src/main/java/com/twocoders/dynamic/image/base/component/UriComponent.kt similarity index 94% rename from dynamic/image/src/main/java/com/twocoders/dynamic/image/component/UriComponent.kt rename to dynamic/image-base/src/main/java/com/twocoders/dynamic/image/base/component/UriComponent.kt index 513c89b..00a68a7 100755 --- a/dynamic/image/src/main/java/com/twocoders/dynamic/image/component/UriComponent.kt +++ b/dynamic/image-base/src/main/java/com/twocoders/dynamic/image/base/component/UriComponent.kt @@ -1,4 +1,4 @@ -package com.twocoders.dynamic.image.component +package com.twocoders.dynamic.image.base.component import android.net.Uri import android.os.Parcel diff --git a/dynamic/image-coil/build.gradle b/dynamic/image-coil/build.gradle index bf116e4..e6b0906 100644 --- a/dynamic/image-coil/build.gradle +++ b/dynamic/image-coil/build.gradle @@ -40,7 +40,7 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - api project(':dynamic:image') + api project(':dynamic:image-base') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" implementation "androidx.annotation:annotation:$annotationVersion" diff --git a/dynamic/image-coil/src/main/java/com/twocoders/dynamic/image/coil/DynamicImage.kt b/dynamic/image-coil/src/main/java/com/twocoders/dynamic/image/coil/DynamicImage.kt index 619ebfe..cbff9e2 100644 --- a/dynamic/image-coil/src/main/java/com/twocoders/dynamic/image/coil/DynamicImage.kt +++ b/dynamic/image-coil/src/main/java/com/twocoders/dynamic/image/coil/DynamicImage.kt @@ -12,19 +12,20 @@ import coil.Coil import coil.ImageLoader import coil.load import coil.request.ImageRequest -import com.twocoders.dynamic.image.BaseDynamicImage -import com.twocoders.dynamic.image.component.UriComponent +import com.twocoders.dynamic.image.base.BaseDynamicImage +import com.twocoders.dynamic.image.base.component.UriComponent /** * - * Handy class which can be used to bind image data to views using them. + * Handy class which can be used to bind image data to views. * Data can be in [DrawableRes], [Drawable], or [UriComponent] format. * * Use one of [DynamicImage.from] creator methods to create the data * and [DynamicImage.getDrawable] to obtain the final [Drawable] output. Or you can * provide target [ImageView] class into the [DynamicImage.loadDrawableInto] method. * - * This [DynamicImage] implementation uses [Coil], for other implementations please visit our GitHub. + * This [DynamicImage] implementation uses [Coil], for other implementations please visit our GitHub: + * [Android Dynamic Components](https://github.com/Two-Coders/android-dynamic-components) * */ @Suppress("unused", "MemberVisibilityCanBePrivate") @@ -58,27 +59,27 @@ open class DynamicImage : BaseDynamicImage { } } - override suspend fun getDrawable(context: Context): Drawable? { - imageUri?.let { uri -> - val request = ImageRequest.Builder(context) - .data(uri) - .build() - return ImageLoader(context).execute(request).drawable - } - - return super.getDrawable(context) + override suspend fun getDrawableFromUri( + context: Context, + imageUriComponent: UriComponent + ): Drawable? { + val request = ImageRequest.Builder(context) + .data(imageUriComponent.image) + .build() + return ImageLoader(context).execute(request).drawable } - override fun getDrawable(context: Context, callback: (drawable: Drawable) -> Unit) { - imageUri?.let { imageUriComponent -> - val request = ImageRequest.Builder(context) + override fun getDrawableFromUri( + context: Context, + imageUriComponent: UriComponent, + callback: (drawable: Drawable) -> Unit + ) { + ImageLoader(context).enqueue( + ImageRequest.Builder(context) .data(imageUriComponent.image) .target { callback(it) } .build() - ImageLoader(context).enqueue(request) - } - - super.getDrawable(context, callback) + ) } override fun loadDrawableInto( diff --git a/dynamic/image-glide/build.gradle b/dynamic/image-glide/build.gradle index 1a89a9c..d339d9a 100644 --- a/dynamic/image-glide/build.gradle +++ b/dynamic/image-glide/build.gradle @@ -40,7 +40,7 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - api project(':dynamic:image') + api project(':dynamic:image-base') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" implementation "androidx.annotation:annotation:$annotationVersion" diff --git a/dynamic/image-glide/src/main/java/com/twocoders/dynamic/image/glide/DynamicImage.kt b/dynamic/image-glide/src/main/java/com/twocoders/dynamic/image/glide/DynamicImage.kt index 5c4e461..dcad5e1 100644 --- a/dynamic/image-glide/src/main/java/com/twocoders/dynamic/image/glide/DynamicImage.kt +++ b/dynamic/image-glide/src/main/java/com/twocoders/dynamic/image/glide/DynamicImage.kt @@ -13,19 +13,20 @@ import com.bumptech.glide.Glide import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.transition.Transition -import com.twocoders.dynamic.image.BaseDynamicImage -import com.twocoders.dynamic.image.component.UriComponent +import com.twocoders.dynamic.image.base.BaseDynamicImage +import com.twocoders.dynamic.image.base.component.UriComponent /** * - * Handy class which can be used to bind image data to views using them. + * Handy class which can be used to bind image data to views. * Data can be in [DrawableRes], [Drawable], or [UriComponent] format. * * Use one of [DynamicImage.from] creator methods to create the data * and [DynamicImage.getDrawable] to obtain the final [Drawable] output. Or you can * provide target [ImageView] class into the [DynamicImage.loadDrawableInto] method. * - * This [DynamicImage] implementation uses [Glide], for other implementations please visit our GitHub. + * This [DynamicImage] implementation uses [Glide], for other implementations please visit our GitHub: + * [Android Dynamic Components](https://github.com/Two-Coders/android-dynamic-components) * */ @Suppress("unused", "MemberVisibilityCanBePrivate") @@ -59,35 +60,32 @@ open class DynamicImage : BaseDynamicImage { } } - override suspend fun getDrawable(context: Context): Drawable? { - imageUri?.let { uri -> - return BitmapDrawable( - context.resources, - Glide.with(context) - .asBitmap() - .load(uri.image) - .submit() - .get() - ) - } - - return super.getDrawable(context) - } - - override fun getDrawable(context: Context, callback: (drawable: Drawable) -> Unit) { - imageUri?.let { imageUriComponent -> - Glide.with(context) - .asBitmap() - .load(imageUriComponent.image) - .into(object : CustomTarget(){ - override fun onResourceReady(resource: Bitmap, transition: Transition?) { - callback(BitmapDrawable(context.resources, resource)) - } - override fun onLoadCleared(placeholder: Drawable?) {} - }) - } - - super.getDrawable(context, callback) + override suspend fun getDrawableFromUri( + context: Context, + imageUriComponent: UriComponent + ) = BitmapDrawable( + context.resources, + Glide.with(context) + .asBitmap() + .load(imageUriComponent.image) + .submit() + .get() + ) + + override fun getDrawableFromUri( + context: Context, + imageUriComponent: UriComponent, + callback: (drawable: Drawable) -> Unit + ) { + Glide.with(context) + .asBitmap() + .load(imageUriComponent.image) + .into(object : CustomTarget() { + override fun onResourceReady(resource: Bitmap, transition: Transition?) { + callback(BitmapDrawable(context.resources, resource)) + } + override fun onLoadCleared(placeholder: Drawable?) {} + }) } override fun loadDrawableInto( diff --git a/dynamic/image-glide/src/main/java/com/twocoders/dynamic/image/glide/DynamicImageBindingAdapter.kt b/dynamic/image-glide/src/main/java/com/twocoders/dynamic/image/glide/DynamicImageBindingAdapter.kt deleted file mode 100644 index 54cc7ff..0000000 --- a/dynamic/image-glide/src/main/java/com/twocoders/dynamic/image/glide/DynamicImageBindingAdapter.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.twocoders.dynamic.image.glide - -import android.widget.ImageView -import androidx.databinding.BindingAdapter - -@BindingAdapter(value = ["android:src", "loadWithCrossFade"], requireAll = false) -fun ImageView.loadDynamicImage(image: DynamicImage, withCrossFade: Boolean = false) = - image.loadDrawableInto(this, withCrossFade) \ No newline at end of file diff --git a/dynamic/image-picasso/build.gradle b/dynamic/image-picasso/build.gradle index 0455f87..8261923 100644 --- a/dynamic/image-picasso/build.gradle +++ b/dynamic/image-picasso/build.gradle @@ -40,7 +40,7 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - api project(':dynamic:image') + api project(':dynamic:image-base') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" implementation "androidx.annotation:annotation:$annotationVersion" diff --git a/dynamic/image-picasso/src/main/java/com/twocoders/dynamic/image/picasso/DynamicImage.kt b/dynamic/image-picasso/src/main/java/com/twocoders/dynamic/image/picasso/DynamicImage.kt index 9ada7b9..eee0afe 100644 --- a/dynamic/image-picasso/src/main/java/com/twocoders/dynamic/image/picasso/DynamicImage.kt +++ b/dynamic/image-picasso/src/main/java/com/twocoders/dynamic/image/picasso/DynamicImage.kt @@ -11,19 +11,20 @@ import androidx.annotation.ColorInt import androidx.annotation.DrawableRes import com.squareup.picasso.Picasso import com.squareup.picasso.Target -import com.twocoders.dynamic.image.BaseDynamicImage -import com.twocoders.dynamic.image.component.UriComponent +import com.twocoders.dynamic.image.base.BaseDynamicImage +import com.twocoders.dynamic.image.base.component.UriComponent /** * - * Handy class which can be used to bind image data to views using them. + * Handy class which can be used to bind image data to views. * Data can be in [DrawableRes], [Drawable], or [UriComponent] format. * * Use one of [DynamicImage.from] creator methods to create the data * and [DynamicImage.getDrawable] to obtain the final [Drawable] output. Or you can * provide target [ImageView] class into the [DynamicImage.loadDrawableInto] method. * - * This [DynamicImage] implementation uses [Picasso], for other implementations please visit our GitHub. + * This [DynamicImage] implementation uses [Picasso], for other implementations please visit our GitHub: + * [Android Dynamic Components](https://github.com/Two-Coders/android-dynamic-components) * */ @Suppress("unused", "MemberVisibilityCanBePrivate") @@ -57,33 +58,30 @@ open class DynamicImage : BaseDynamicImage { } } - override suspend fun getDrawable(context: Context): Drawable? { - imageUri?.let { uri -> - return BitmapDrawable( - context.resources, - Picasso.get() - .load(uri.image) - .get() - ) - } - - return super.getDrawable(context) - } - - override fun getDrawable(context: Context, callback: (drawable: Drawable) -> Unit) { - imageUri?.let { imageUriComponent -> - Picasso.get() - .load(imageUriComponent.image) - .into(object : Target { - override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) { - callback(BitmapDrawable(context.resources, bitmap)) - } - override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) {} - override fun onPrepareLoad(placeHolderDrawable: Drawable?) {} - }) - } - - super.getDrawable(context, callback) + override suspend fun getDrawableFromUri( + context: Context, + imageUriComponent: UriComponent + ) = BitmapDrawable( + context.resources, + Picasso.get() + .load(imageUriComponent.image) + .get() + ) + + override fun getDrawableFromUri( + context: Context, + imageUriComponent: UriComponent, + callback: (drawable: Drawable) -> Unit + ) { + Picasso.get() + .load(imageUriComponent.image) + .into(object : Target { + override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) { + callback(BitmapDrawable(context.resources, bitmap)) + } + override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) {} + override fun onPrepareLoad(placeHolderDrawable: Drawable?) {} + }) } override fun loadDrawableInto( diff --git a/dynamic/image-picasso/src/main/java/com/twocoders/dynamic/image/picasso/DynamicImageBindingAdapter.kt b/dynamic/image-picasso/src/main/java/com/twocoders/dynamic/image/picasso/DynamicImageBindingAdapter.kt deleted file mode 100644 index ff850ff..0000000 --- a/dynamic/image-picasso/src/main/java/com/twocoders/dynamic/image/picasso/DynamicImageBindingAdapter.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.twocoders.dynamic.image.picasso - -import android.widget.ImageView -import androidx.databinding.BindingAdapter - -@BindingAdapter(value = ["android:src", "loadWithCrossFade"], requireAll = false) -fun ImageView.loadDynamicImage(image: DynamicImage, withCrossFade: Boolean = false) = - image.loadDrawableInto(this, withCrossFade) \ No newline at end of file diff --git a/dynamic/image/src/main/AndroidManifest.xml b/dynamic/image/src/main/AndroidManifest.xml deleted file mode 100644 index e447999..0000000 --- a/dynamic/image/src/main/AndroidManifest.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index adb7023..80fd82c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,6 @@ include ':dynamic:text', ':dynamic:color', - ':dynamic:image', + ':dynamic:image-base', ':dynamic:image-coil', ':dynamic:image-glide', ':dynamic:image-picasso' From f73aaedeec39c4e26c2e6fb2bf0f5615ec8baaa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toma=CC=81s=CC=8C=20Valenta?= Date: Wed, 6 Jan 2021 15:17:06 +0100 Subject: [PATCH 4/5] Remove unused buildType block from Gradle. --- dynamic/color/build.gradle | 6 ------ dynamic/image-base/build.gradle | 6 ------ dynamic/image-coil/build.gradle | 6 ------ dynamic/image-glide/build.gradle | 6 ------ dynamic/image-picasso/build.gradle | 6 ------ dynamic/text/build.gradle | 6 ------ 6 files changed, 36 deletions(-) diff --git a/dynamic/color/build.gradle b/dynamic/color/build.gradle index ca88d4c..b0cb286 100644 --- a/dynamic/color/build.gradle +++ b/dynamic/color/build.gradle @@ -17,12 +17,6 @@ android { consumerProguardFiles 'consumer-rules.pro' } - buildTypes { - release { - minifyEnabled false - } - } - buildFeatures { dataBinding = true } diff --git a/dynamic/image-base/build.gradle b/dynamic/image-base/build.gradle index 82306de..0ee0421 100644 --- a/dynamic/image-base/build.gradle +++ b/dynamic/image-base/build.gradle @@ -15,12 +15,6 @@ android { buildFeatures { dataBinding = true } - - buildTypes { - release { - minifyEnabled false - } - } } dependencies { diff --git a/dynamic/image-coil/build.gradle b/dynamic/image-coil/build.gradle index e6b0906..3660297 100644 --- a/dynamic/image-coil/build.gradle +++ b/dynamic/image-coil/build.gradle @@ -17,12 +17,6 @@ android { consumerProguardFiles 'consumer-rules.pro' } - buildTypes { - release { - minifyEnabled false - } - } - buildFeatures { dataBinding = true } diff --git a/dynamic/image-glide/build.gradle b/dynamic/image-glide/build.gradle index d339d9a..51c9bd4 100644 --- a/dynamic/image-glide/build.gradle +++ b/dynamic/image-glide/build.gradle @@ -17,12 +17,6 @@ android { consumerProguardFiles 'consumer-rules.pro' } - buildTypes { - release { - minifyEnabled false - } - } - buildFeatures { dataBinding = true } diff --git a/dynamic/image-picasso/build.gradle b/dynamic/image-picasso/build.gradle index 8261923..8ed39b7 100644 --- a/dynamic/image-picasso/build.gradle +++ b/dynamic/image-picasso/build.gradle @@ -17,12 +17,6 @@ android { consumerProguardFiles 'consumer-rules.pro' } - buildTypes { - release { - minifyEnabled false - } - } - buildFeatures { dataBinding = true } diff --git a/dynamic/text/build.gradle b/dynamic/text/build.gradle index e2711d8..9d8e019 100644 --- a/dynamic/text/build.gradle +++ b/dynamic/text/build.gradle @@ -17,12 +17,6 @@ android { consumerProguardFiles 'consumer-rules.pro' } - buildTypes { - release { - minifyEnabled false - } - } - buildFeatures { dataBinding = true } From 5a5d5441df5d7faf3e72b85e7f8782734234a36e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toma=CC=81s=CC=8C=20Valenta?= Date: Wed, 13 Jan 2021 12:23:49 +0100 Subject: [PATCH 5/5] Add initial state of DynamicImageCoilTest. --- build.gradle | 1 + .../dynamic/image/base/BaseDynamicImage.kt | 7 ++- dynamic/image-coil/build.gradle | 8 +++ .../image/coil/DynamicImageCoilTest.kt | 58 +++++++++++++++++++ 4 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 dynamic/image-coil/src/androidTest/java/com/twocoders/dynamic/image/coil/DynamicImageCoilTest.kt diff --git a/build.gradle b/build.gradle index e679601..716f076 100644 --- a/build.gradle +++ b/build.gradle @@ -25,6 +25,7 @@ buildscript { picassoVersion = '2.71828' junitVersion = '4.13.1' + mockitoVersion = '3.7.0' androidTestRunnerVersion = '1.3.0' androidJunitVersion = '1.1.2' } diff --git a/dynamic/image-base/src/main/java/com/twocoders/dynamic/image/base/BaseDynamicImage.kt b/dynamic/image-base/src/main/java/com/twocoders/dynamic/image/base/BaseDynamicImage.kt index 9af417b..60f3e8a 100644 --- a/dynamic/image-base/src/main/java/com/twocoders/dynamic/image/base/BaseDynamicImage.kt +++ b/dynamic/image-base/src/main/java/com/twocoders/dynamic/image/base/BaseDynamicImage.kt @@ -69,7 +69,12 @@ abstract class BaseDynamicImage : Parcelable { return null } - open fun getDrawable(context: Context, callback: (drawable: Drawable) -> Unit) { + open fun getDrawable(context: Context, callback: (drawable: Drawable?) -> Unit) { + if (isEmpty()) { + callback(null) + return + } + imageUri?.let { imageUriComponent -> getDrawableFromUri(context, imageUriComponent, callback) } diff --git a/dynamic/image-coil/build.gradle b/dynamic/image-coil/build.gradle index 3660297..509a121 100644 --- a/dynamic/image-coil/build.gradle +++ b/dynamic/image-coil/build.gradle @@ -29,6 +29,13 @@ android { kotlinOptions { jvmTarget = JavaVersion.VERSION_1_8.toString() } + + testOptions { + unitTests { + returnDefaultValues = true + includeAndroidResources = true + } + } } dependencies { @@ -45,6 +52,7 @@ dependencies { androidTestImplementation "androidx.test:runner:$androidTestRunnerVersion" androidTestImplementation "androidx.test.ext:junit:$androidJunitVersion" + androidTestImplementation "org.mockito:mockito-android:$mockitoVersion" testImplementation "junit:junit:$junitVersion" } diff --git a/dynamic/image-coil/src/androidTest/java/com/twocoders/dynamic/image/coil/DynamicImageCoilTest.kt b/dynamic/image-coil/src/androidTest/java/com/twocoders/dynamic/image/coil/DynamicImageCoilTest.kt new file mode 100644 index 0000000..e54d2ef --- /dev/null +++ b/dynamic/image-coil/src/androidTest/java/com/twocoders/dynamic/image/coil/DynamicImageCoilTest.kt @@ -0,0 +1,58 @@ +package com.twocoders.dynamic.image.coil + +import android.content.Context +import android.graphics.drawable.BitmapDrawable +import android.widget.ImageView +import androidx.annotation.DrawableRes +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.twocoders.extensions.common.getDrawable +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.* + +@RunWith(AndroidJUnit4::class) +class DynamicImageCoilTest { + + private lateinit var context: Context + + @Before + fun setUp() { + context = InstrumentationRegistry.getInstrumentation().context + } + + @Test + fun emptyDynamicImageReturnsNullDrawable() { + val emptyDynamicImage = DynamicImage.EMPTY + + emptyDynamicImage.getDrawable(context) { assertNull(it) } + + GlobalScope.launch { assertNull(emptyDynamicImage.getDrawable(context)) } + + val imageViewMock = mock(ImageView::class.java) + emptyDynamicImage.loadDrawableInto(imageViewMock) + verify(imageViewMock, never()).setImageDrawable(any()) + } + + @Test + fun dynamicImageFromDrawableRes() { + @DrawableRes val testedImageRes = android.R.drawable.ic_delete + val testedDynamicImage = DynamicImage.from(testedImageRes) + val expectedDrawable = context.getDrawable(drawableResId = testedImageRes) as BitmapDrawable + val expectedBitmap = expectedDrawable.bitmap + + testedDynamicImage.getDrawable(context) { assertEquals(expectedBitmap, (it as BitmapDrawable).bitmap ) } + + GlobalScope.launch { assertEquals(expectedBitmap, (testedDynamicImage.getDrawable(context) as BitmapDrawable).bitmap) } + + val imageViewMock = mock(ImageView::class.java) + `when`(imageViewMock.context).thenReturn(context) + testedDynamicImage.loadDrawableInto(imageViewMock) + verify(imageViewMock).setImageDrawable(expectedDrawable) + } +} \ No newline at end of file