Tag Archives: Android 5.0

Help! My custom font does not work on Android 5.0

Issue description

On Android Lollipop versions before 5.1 some custom fonts loaded using the API Typeface.createFromAsset() do not render using the correct font. This issue was fixed with the release of Android 5.1, but will still affect your users running Android Lollipop 5.0.x.

This should not cause a crash in your app, it will just cause your view to render using the default typeface rather than the custom font you have specified.

What causes the issue?

The cause of this issue is badly formed TTF font files. Minor issues in font files may be ignored or tolerated by most systems rendering the font, however the tolerance level in the early builds of Lollipop was not so high and some fonts fail to render causing the system to fall back to using the default font. It is similar to having a badly written audio file that will play in some media apps but is flagged as corrupted in others.

Is my app affected?

The first thing to check is if your app loads custom fonts. Take a look for any font files found in your Android assets/fonts folder. Then look for the code that is loading the font so you can see where in the apps UI the custom font is used. Loading a custom font from your apps Assets folder and setting it into a View is quite straight forward.

Typeface tf = Typeface.createFromAsset(getAssets(), "fonts/myfont.ttf");
myView.setTypeface(tf);

Having custom fonts doesn’t always mean your app will be affected by this issue however. The quickest way to check is to visually verify those custom fonts are being rendered correctly. Another check suggested by the Android team is to run your font through the Sanitiser for OpenType engine. To determine a potential issue look for this in the output:

WARNING at ots-read-only/src/cmap.cc:160: bad id_range_offset

Solution

The root cause of the problem is badly written font files, so it makes sense that the solution is to re-encode the offending font.

If you only have a couple of font files you would like to re-encode a great place to start is with an online converting tool such as http://www.freefontconverter.com/. Upload your dodgy font file and even if you select the same input and output font format (ie. TTF), the file will still be re-encoded and should now be able to be used in your app. This web service has successfully fixed all the non conforming fonts I have thrown at it.

However if that online tool did not help, or you have a huge repository of files you would like to re-encode you may like to use https://github.com/behdad/fonttools/ to convert your TTF to XML and then back to a TTF.

Be careful running any software that will re-encode your font files. As this process will be changing the file there may be side effects such as loss of quality. Also it is possible the tool you are using to re-encode the file will also produce non-compliant font files that fail in some environments.

Why did my Unit tests not catch this error?

If you have had to fix this error then you may be thinking to yourself, Why did my unit test not alert me to this issue? or How can I write a unit test to stop this issue next time?

Don’t be too hard on yourself; for all intents and purposes the code appears to function correctly without visually verifying the rendering. The typeface is loaded correctly and it can be set into a view without issue or complaint. Even after the view has been invalidated and rendered there is still no exceptions or warnings of an issue raised. The problem only becomes obvious on visual inspection of the View.

References

Android issue tracker for this issue:

https://code.google.com/p/android-developer-preview/issues/detail?id=608

Android Typeface documentation:

http://developer.android.com/reference/android/graphics/Typeface.html

Leave a comment

Filed under Android 5.0, Android SDK, Testing

Vector Graphics in Android: Converting SVG to VectorDrawable

Vector graphics native support has long been on the wishlist of Android developers who have to export their vector graphics as a rasterized PNG file for each different device resolution.

Introducing VectorDrawable

The good news is that the Android SDK now has native support for basic vector graphic definitions. With the release of API 21 (Android 5.0) came VectorDrawable.

android.graphics.drawable.VectorDrawable

The most important things to understand if you want to implement drawables in your app using VectorDrawable are:

  • VectorDrawable is not implemented in the support library, therefore it will only work in Android 5.0+ (VectorDrawable support for older APIs may be coming soon to the support library)
  • VectorDrawable supports paths using the same definition as the ‘d’ attribute in the SVG format
  • VectorDrawable does not read or support the SVG format
  • VectorDrawable does not support gradients and patterns
  • VectorDrawable does not support numbers using scientific E notation (as commonly used in SVG)

While it is important to understand that the SVG format is not supported, it doesn’t necessarily mean you can’t convert your existing simple SVG files. A VectorDrawable is specified through XML, with the format of the path data in the VectorDrawable using the same format as the specification for SVG paths. This allows us to reformat the XML in some SVG files to convert it into a VectorDrawable XML file.

If you are looking to use SVG files directly in your Android app take a look at this comparison of 3rd party solutions:

https://androidbycode.wordpress.com/2015/02/27/vector-graphics-in-android-part-1-svg/

Case Study: Converting SVG to VectorDrawable

The penguin in the image below (Attribution: Larry Ewing) has been rendered from a SVG image.  SVG images such as this can not be converted into a VectorDrawable as it uses LinearGradient in the original source.NewTux

Below is a rendered image of the flag of Ghana, it is also rendered from an SVG however as this image doesn’t have a gradient and only includes simple shapes, it is most suitable for conversion to a VectorDrawable. We will use this image as to illustrate how to convert an SVG into a VectorDrawable.

Flag of Ghana

SVG Images are described using the XML format. The above image contains 4 elements; three stripes and a star. The SVG XML source may appear as follows:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:svg="http://www.w3.org/2000/svg"
  width="450"
  height="300"
  id="svg2"
  version="1.1">
<rect
  width="450"
  height="300"
  fill="#006b3f"
  id="rect4" />
<rect
  width="450"
  height="200"
  fill="#fcd116"
  id="rect6" />
<rect
  width="450"
  height="100"
  fill="#ce1126"
  id="rect8" />
<path
  d="M225,100 L257.492,200 172.427,138.197H277.573L192.508,200z"
  fill="#000"
  id="path10" />
</svg>

If we want to create this same image as an Android VectorDrawable the first thing we need to do is ensure all items are described using paths. Remember only paths are supported in VectorDrawable.  To do this we convert each rect definition in the SVG image to use a path definition. The following XML shows the new path objects:


<path
  style="fill:#006b3f"
  d="m 0,0 450,0 0,300 -450,0 z" />
<path
  style="fill:#fcd116"
  d="m 0,0 450,0 0,200 -450,0 z" />
<path
  style="fill:#ce1126"
  d="m 0,0 450,0 0,100 -450,0 z" />

The next step is to convert the XML into Android’s new VectorDrawable format.  To do this we need to keep the path and color definitions but replace the formatting.  The XML should now look like this:


<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="450dp"
android:height="300dp"
android:viewportWidth="450"
android:viewportHeight="300">

<group>
  <path
    android:fillColor="#006b3f"
    android:pathData="M 0,0 l 450,0 0,300 -450,0 z" />
  <path
    android:fillColor="#fcd116"
    android:pathData="M 0,0 l 450,0 0,200 -450,0 z" />
  <path
    android:fillColor="#ce1126"
    android:pathData="M 0,0 l 450,0 0,100 -450,0 z" />
  <path
    android:fillColor="#000"
    android:pathData="M225,100 L257.492,200 172.427,138.197H277.573L192.508,200z" />
</group>
</vector>

This VectorDrawable file should be named with an .xml extension and placed in your drawables folder so it can be accessed in your Java source code or layout definition as you would with any other drawable.

The good news is you don’t need to step through this process by hand each time, there are tools to help you out. I have documented this process as it is important to know what and how to hand code the xml format as the conversion tools will not work all of the time so you may need to tweak the code from time to time.

To convert your SVG file to use only paths you can use Inkscape (The awesome open source SVG tool).  Open the SVG file in Inkscape and select all items you want to convert to a path.  Then from the Path menu select Object to path. If you use Adobe Illustrator then you need to convert the shapes into compound paths before saving the SVG.

To automate the conversion of an SVG file to the VectorDrawable format you can use the on-line open source svg2android tool.

VectorDrawable support pre Android 5.0

While the VectorDrawable class is only available on 5.0+ at this stage, an unofficial project has released support for older versions of Android.

Visit Mr. Vector on GitHub for the source code and usage instructions. You should note that the Vector XML used in this project is slightly different to the standard VectorDrawable specification. You can convert your simple SVG files to the Mr. Vector format using Svg2MrVector.

UPDATE MAY 2015: VectorDrawableCompat is in the process of being developed and may be added to the official support library. Once released there is no need to use the Mr. Vector project anymore, so keep an eye on the official Android Developers Blog for updates to the support library.

References:

Scalable Vector Graphics http://en.wikipedia.org/wiki/Scalable_Vector_Graphics

SVG Path Format http://www.w3.org/TR/SVG11/paths.html#PathData

VectorDrawable https://developer.android.com/reference/android/graphics/drawable/VectorDrawable.html

MrVector https://github.com/telly/MrVector

Inkscape https://inkscape.org/

“NewTux” by Larry Ewing. Licensed under Attribution via Wikimedia Commons – http://commons.wikimedia.org/wiki/File:NewTux.svg#/media/File:NewTux.svg

“Flag of Ghana”. Licensed under Public Domain via Wikimedia Commons – http://commons.wikimedia.org/wiki/File:Flag_of_Ghana.svg

2 Comments

Filed under Android SDK, Image

Nexus Lollipop soundpool.load() not so sweet

When I got my first taste of the Android L pre-release I noticed something rather disturbing when running some games.  I was using a Nexus 5 and every now and again a long pause where an app would become non-responsive. Sometimes it was a couple of seconds and other times it was 10, 20 or more seconds without response, then it would finally burst back to life. After doing a little digging and I tracked it to an issue with the Nexus family using the SoundPool.load() function.  This issue lives on with the latest Android 5.0.2 build.

Issue Description

The Android SoundPool class allows developers to pre-load several short sound clips so the may be played at any time without the lag of decompressing on the fly.  This is perfect for game developers where sounds may be played many times. On the Nexus family of devices, the execution of the SoundPool.load() function may take several seconds to load a very small audio file, instead of the few milliseconds as has been the case with the same files on previous Android versions, or with other devices running Lollipop.  The issue relates to the ‘AwesomePlayer’ media engine. There is a way to confirm if this is an issue in your code.  Go to the media section of the developer options and check the ‘Use NuPlayer (experimental)’ checkbox.  Run your app again and you should see none of the unresponsiveness that has lead you to this post. If the delay remains it is likely not being caused by the SoundPool.load() issue.

Issue Status

As of Android 5.0.2 this is an unresolved issue in the Android Open Source Project (AOSP) issue tracker. It’s can be reproduced on most of the Nexus devices that have support for 5.0, from the Nexus 4 through to 5, 7 (2012 & 2013) and 9.  I have not been lucky enough to get my hands on a Nexus 6 to confirm if it suffers the same affliction.

Poor app design exposed

The Android media engine requiring seconds to load a small sound file is clearly not acceptable, but what it does is exposes apps that are executing the sound pool loading code from the UI thread. For a few small audio files that were taking a few milliseconds each, this was never noticed. But best practice dictates load functions like this should be happening on a background thread. Structure you app so the thread has maximum time to load all it needs to load without needing to hold up the user.  Once this change is made chances are this issue will no longer be a problem in your app and you won’t be waiting for the AOSP fix.

Changes to SoundPool in API 21

While on the subject of the SoundPool class, Android 5.0 (API 21) also brings some recommended usage changes.  So read on if you have not changed the way you create your SoundPool on Lollipop devices. If you take a look at the SoundPool interface the constructor public SoundPool(int maxStreams, int streamType, int srcQuality) is now deprecated. The recommended usage is to create and configure the SoundPool instance using the SoundPool builder class (SoundPool.Builder) The SoundPool Builder is only available on API 21+, so you will need to create your SoundPool differently based on the SDK version of the device your app is running on. When your code is executed on devices running 5.0+ create the SoundPool using the builder, as follows:

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
protected void createSoundPoolWithBuilder(){
    AudioAttributes attributes = new AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_GAME)
        .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
        .build();

    mSoundPool = new SoundPool.Builder().setAudioAttributes(attributes).setMaxStreams(6).build();
}

For devices running 4.4 and older use the SoundPool constructor:

@SuppressWarnings("deprecation")
protected void createSoundPoolWithConstructor(){
    mSoundPool = new SoundPool(6, AudioManager.STREAM_MUSIC, 0);
}

Check the device version to call the appropriate function to create the SoundPool:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    createSoundPoolWithBuilder();
} else{
    createSoundPoolWithConstructor();
}

Hopefully this has sorted your Android 5.0 SoundPool woes.

Update March 2015: This issue was still present for my Nexus devices after initially updating to Android 5.1, although it seems on a clean install of 5.1 the default media engine is now NuPlayer which does not have exhibit the SoundPool issue.

lollipop

3 Comments

Filed under Android 5.0, Android SDK, Audio, SoundPool