Skip to content
Permalink
Browse files

Update to Coil v0.9.2

Allows us to use the new Transition API
  • Loading branch information
chrisbanes committed Nov 13, 2019
1 parent 764e34f commit 8a897bc6ce86ea6d6222fee138d6b28e8293575d
@@ -62,6 +62,7 @@ allprojects {
mavenCentral()
jcenter()
maven { url 'https://jitpack.io' }
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
mavenLocal()
}
}
@@ -84,6 +85,8 @@ subprojects {
// Enable experimental coroutines APIs, including Flow
freeCompilerArgs += "-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi"
freeCompilerArgs += "-Xuse-experimental=kotlinx.coroutines.FlowPreview"
freeCompilerArgs += "-Xuse-experimental=kotlinx.coroutines.FlowPreview"
freeCompilerArgs += "-Xuse-experimental=kotlin.Experimental"

// Set JVM target to 1.8
jvmTarget = "1.8"
@@ -184,7 +184,7 @@ object Libs {
}

object Coil {
private const val version = "0.8.0"
private const val version = "0.9.2"
const val coil = "io.coil-kt:coil:$version"
}

@@ -21,6 +21,7 @@ import androidx.databinding.BindingAdapter
import app.tivi.data.entities.ImageType
import app.tivi.data.entities.ShowTmdbImage
import app.tivi.data.entities.TmdbImageEntity
import coil.annotation.ExperimentalCoil
import coil.api.loadAny
import coil.transform.RoundedCornersTransformation
import coil.transform.Transformation
@@ -77,6 +78,7 @@ fun ImageView.loadLogo(
"imageCornerRadius",
requireAll = false
)
@UseExperimental(ExperimentalCoil::class)
fun ImageView.loadImage(
oldImage: TmdbImageEntity?,
oldSaturateOnLoad: Boolean?,
@@ -90,18 +92,14 @@ fun ImageView.loadImage(
oldCornerRadius == cornerRadius) return

loadAny(image) {
if (saturateOnLoad == null || saturateOnLoad == true) {
val saturatingTarget = SaturatingImageViewTarget(this@loadImage)
target(saturatingTarget)
listener(saturatingTarget)
}
transition(SaturatingTransformation())

val transformations = ArrayList<Transformation>()
if (cornerRadius > 0) {
transformations += RoundedCornersTransformation(cornerRadius)
}
if (image?.type == ImageType.LOGO) {
transformations += TrimTransparentEdgesTransformation()
transformations += TrimTransparentEdgesTransformation
}
transformations(transformations)
}

This file was deleted.

@@ -0,0 +1,65 @@
/*
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package app.tivi.common.imageloading

import androidx.core.animation.doOnEnd
import app.tivi.ui.animations.SATURATION_ANIMATION_DURATION
import app.tivi.ui.animations.saturateDrawableAnimator
import coil.annotation.ExperimentalCoil
import coil.transition.Transition
import coil.transition.TransitionResult
import coil.transition.TransitionTarget
import kotlinx.coroutines.suspendCancellableCoroutine

/** A [Transition] that saturates and fades in the new drawable on load */
@ExperimentalCoil
class SaturatingTransformation(
private val durationMillis: Long = SATURATION_ANIMATION_DURATION
) : Transition {
init {
require(durationMillis > 0) { "durationMillis must be > 0." }
}

override suspend fun transition(
target: TransitionTarget<*>,
result: TransitionResult
) {
// Don't animate if the request was fulfilled by the memory cache.
if (result is TransitionResult.Success && result.isMemoryCache) {
target.onSuccess(result.drawable)
return
}

// Animate the drawable and suspend until the animation is completes.
suspendCancellableCoroutine<Unit> { continuation ->
when (result) {
is TransitionResult.Success -> {
val animator = saturateDrawableAnimator(result.drawable,
durationMillis, target.view)
animator.doOnEnd {
continuation.resume(Unit) { animator.cancel() }
}
animator.start()

continuation.invokeOnCancellation { animator.cancel() }
target.onSuccess(result.drawable)
}
is TransitionResult.Error -> target.onError(result.drawable)
}
}
}
}
@@ -21,15 +21,16 @@ import android.graphics.Canvas
import android.graphics.Rect
import androidx.core.graphics.alpha
import coil.bitmappool.BitmapPool
import coil.size.Size
import coil.transform.Transformation

/**
* A [Transformation] that trims transparent edges from an image.
*/
class TrimTransparentEdgesTransformation : Transformation {
object TrimTransparentEdgesTransformation : Transformation {
override fun key(): String = TrimTransparentEdgesTransformation::class.java.name

override suspend fun transform(pool: BitmapPool, input: Bitmap): Bitmap {
override suspend fun transform(pool: BitmapPool, input: Bitmap, size: Size): Bitmap {
val inputWidth = input.width
val inputHeight = input.height

@@ -85,13 +86,17 @@ class TrimTransparentEdgesTransformation : Transformation {
return input
}

val bitmap = pool.get(1 + lastX - firstX, 1 + lastY - firstY, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
val output = pool.get(1 + lastX - firstX, 1 + lastY - firstY, Bitmap.Config.ARGB_8888)
val canvas = Canvas(output)

val src = Rect(firstX, firstY, firstX + bitmap.width, firstY + bitmap.height)
val dst = Rect(0, 0, bitmap.width, bitmap.height)
val src = Rect(firstX, firstY, firstX + output.width, firstY + output.height)
val dst = Rect(0, 0, output.width, output.height)

canvas.drawBitmap(input, src, dst, null)
return bitmap

// Put the original bitmap in the pool
pool.put(input)

return output
}
}
@@ -29,31 +29,36 @@ import kotlin.math.roundToLong

private val fastOutSlowInInterpolator = FastOutSlowInInterpolator()

fun saturateDrawableAnimator(current: Drawable, view: View): Animator {
fun saturateDrawableAnimator(
current: Drawable,
duration: Long = SATURATION_ANIMATION_DURATION,
view: View? = null
): Animator {
current.mutate()
view.setHasTransientState(true)
view?.setHasTransientState(true)

val cm = ImageLoadingColorMatrix()

val satAnim = ObjectAnimator.ofFloat(cm, ImageLoadingColorMatrix.PROP_SATURATION, 0f, 1f)
satAnim.duration = SATURATION_ANIMATION_DURATION
satAnim.duration = duration
satAnim.addUpdateListener {
current.colorFilter = ColorMatrixColorFilter(cm)
}

val alphaAnim = ObjectAnimator.ofFloat(cm, ImageLoadingColorMatrix.PROP_ALPHA, 0f, 1f)
alphaAnim.duration = SATURATION_ANIMATION_DURATION / 2
alphaAnim.duration = duration / 2

val darkenAnim = ObjectAnimator.ofFloat(cm, ImageLoadingColorMatrix.PROP_BRIGHTNESS, 0.8f, 1f)
darkenAnim.duration = (SATURATION_ANIMATION_DURATION * 0.75f).roundToLong()
darkenAnim.duration = (duration * 0.75f).roundToLong()

return AnimatorSet().apply {
playTogether(satAnim, alphaAnim, darkenAnim)
interpolator = fastOutSlowInInterpolator
doOnEnd {
current.clearColorFilter()
view.setHasTransientState(false)
view?.setHasTransientState(false)
}
}
}

private const val SATURATION_ANIMATION_DURATION = 1000L
const val SATURATION_ANIMATION_DURATION = 1000L

0 comments on commit 8a897bc

Please sign in to comment.
You can’t perform that action at this time.