+++
template = "article.html"
title = "Compat libraries incompatibilities"
date = 2017-05-06T19:52:26+02:00
description = "Navigating the challenges of Android compat libraries, particularly with vector drawables and their unexpected limitations."
[taxonomies]
tags = ["android"]
+++
Compat libraries are great. They allow us to work with the newest Android APIs,
without thinking (much) about your minimum API level. Instead of thousands
of devices, you can reach billions. With nearly no changes in your code.
But sometimes, they're not so great…
Support for vector drawables (mainly SVGs files) has been added in API 21. They
come in two kinds: still and animated. They're great for many reasons: they
scale properly, you don't have to keep multiple densities of the same image
anymore, you can reference colors and dimensions from resources, you can do path
morphing… well, almost.
In order to use vector drawables on pre-21 APIs, Google released a set of two
support libraries, `com.android.support:vector-drawable` and
`com.android.support:animated-vector-drawable`. The first one works starting
with API 7, the latest with API 11. Everything is explained
[here](https://android-developers.googleblog.com/2016/02/android-support-library-232.html).
Still drawables should work exactly the same regarding the API level you're
running. On the animated version, however, there's a catch: you can't animate
all the properties you can on 21+. From the
[documentation](https://developer.android.com/reference/android/support/graphics/drawable/AnimatedVectorDrawableCompat.html):
> Note that the animation in AnimatedVectorDrawableCompat has to be valid and
> functional based on the SDK version the app will be running on. Before SDK
> version 21, the animation system didn't support the following features:
>
> * Path Morphing (PathType evaluator). This is used for morphing one path into
> another.
> * Path Interpolation. This is used to define a flexible interpolator
> (represented as a path) instead of the system defined ones like
> LinearInterpolator.
> * Animating 2 values in one ObjectAnimator according to one path's X
> value and Y value. One usage is moving one object in both X and Y
> dimensions along a path.
Let's say you want an animation using path morphing. Place your vector drawable
in the `drawable-v21` folder and add a fallback with a rotation or whatever in
the `drawable` one, and you're good to go, right? *Right?*
The same page of the documentation also mentions this:
> For API 24 and above, this class is delegating to the framework's
> AnimatedVectorDrawable. For older API version, this class uses ObjectAnimator
> and AnimatorSet to animate the properties of a VectorDrawableCompat to create an
> animated drawable.
And here come troubles. The reasoning behind this is that SDK's implementation
on APIs 21 to 23 [contains some bugs](https://issuetracker.google.com/issues/37116940#comment3).
However, using an `AnimatedVectorDrawableCompat` on an API 21 device comes with
the same limitations as running on an API 19 device: you're using the SDK's
`ObjectAnimator` and `AnimatorSet`, and they do *not* know the support libraries
symbols. More specifically, they don't know how to animate any
`android.support.graphics.drawable.PathParser$PathDataNode` (what they do know
about is the `android.util.PathParser$PathDataNode` class).
If you're looking for more technical bits, I wrote some more notes on this
[StackOverflow question](http://stackoverflow.com/q/43654496/775894) as I
stumbled upon this issue.
As a result, on APIs 21 to 23, any path-morphing animation fails silently when
using the support libraries. There's a work-around, though. You can use the
SDK's implementation to load your vector drawable:
```java
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// Casting the drawable to an AnimatedVectorDrawable is useless here
// It's just to show that you don't get an AnimatedVectorDrawableCompat
AnimatedVectorDrawable drawable = (AnimatedVectorDrawable) getDrawable(R.drawable.ic_animated_drawable_32dp);
mBinding.imageView.setImageDrawable(drawable);
} else {
mBinding.imageView.setImageResource(R.drawable.ic_animated_drawable_32dp);
}
// Starting the animation works whatever the implementation we're using now
final Drawable animation = mBinding.imageView.getDrawable();
if (animation instanceof Animatable) {
((Animatable) animation).start();
}
```
However, using the SDK's implementation obviously means *not* making use of
the support library's bug-fixes. It will probably work for simple drawables, but
may fail on more complex ones.
As a final note: if you're using animated vector drawables, remember to test not
only on whatever your minimum SDK is and your latest shiny device, but also on
APIs 21 to 23. You might be surprised.