Tofus (□) are representations used when a specific character (like an Emoji) cannot be displayed. You have seen them and so your users.

Thanks to EmojiCompat now Android developers have a way to provide Emoji compatibility for older devices but does it solve all the issues developers have with Emoji?

Have you wondered why Twitter counts characters differently depending on the Emoji? Or how gender and skin tone Emoji modifiers work? How can I have a similar functionality as Slack on my app with custom Emoji? Do all your users see the same Emoji?

On this talk you will learn about the different pitfalls developers face when working with Emoji like code points the Fitzpatrick modifiers and short-names you will learn how to use EmojiCompat and I will show you alternatives to it. In this talk you will learn:

- How Emoji was created
- How Emoji works
- What are the problems developers face with Emoji
- How to use EmojiCompat including dynamic font downloading
- What is EmojiCompat not solving and what can you do
- What alternatives are there to EmojiCompat and how to implement them

After this talk you will be an Emoji expert no longer afraid of showing □ (tofu) to your users!


Miguel Beltran

June 27, 2018


  1. @Miqubel No More □ (tofu)    Mastering Emoji on Android

  2. Why Emoji matters? What problems do Android developers face? What

    solutions do we have? Is EmojiCompat the greatest thing ever?
  3. Emoji: A Brief Story Source:

  4. Emoji & Android Source:

  5. How to be up to date: DIY

  6. None
  7. None
  8. None
  9. Emotions Source: People interpret the same emoji in completely different

  10. How Emoji Works

  11. Extra-short-intro to Unicode Hello U+0048 U+0065 U+006C U+006C U+006F Unicode:

    Single character set that contains ALL characters of the Universe Each character maps to a Codepoint A Codepoint may be represented in memory differently! Codepoint UTF-8 UTF-16 H U+0048 48 0048 U+1F602 F0 9F 98 82 D83D DE02
  12. How Emoji works Emoji: Graphic representation of one or more

    Unicode chars (Codepoints) "Face With Tears of Joy” U+1F602 val emoji = "\uD83D\uDE02"
  13. How Emoji works “Woman Technologist: Medium-Dark Skin Tone” val emoji

    = “\uD83D\uDC69\uD83C\uDFFE\u200D\uD83D\uDCBB" U+1F469 U+1F3FE U+200D U+1F4BB
  14. How long is an Emoji? Challenge: How many “Woman Technologist:

    Medium-Dark Skin Tone” Emojis can you fit on a Tweet? A: 280 B: 140 C: 40 Solution: 40 280 / 7 = 40
  15. How long is an Emoji? —> Improvement emoji.length = 7

    emoji.codePointCount(0, emoji.length) = 4 val emoji = “\uD83D\uDC69\uD83C\uDFFE\u200D\uD83D\uDCBB"
  16. How long is an Emoji? —> Improvement val it =

    BreakIterator.getCharacterInstance() it.setText(emoji) var count = 0 while ( != BreakIterator.DONE) { count++ } count = 3 on API 23 count = 1 on API 25
  17. Challenges so far Missing Emoji (tofu) Different Emoji per platform

    Length of strings with Emoji
  18. Let’s look at three solutions Custom solution: Images and Spans

    Custom solution: Font file Using EmojiCompat
  19. Images and Spans Strategy: 1. Find an Emoji on a

    String 2. Replace with an ImageSpan 3. Repeat for the whole String
  20. Finding Emoji on Strings Get a nice dictionary: EmojiOne “emoji.json”:

    1.38 MB! Fast JSON parsing: com.jsoniter —> but you won’t need to Efficient pattern matching —> Regular Expression
  21. None
  22. Emoji assets Download: emojione/emojione-assets • Free: PNG 32px, 64px, 128px

    • Paid: SVG and larger PNG Example: SLICE OF PIZZA (U+1F355) —> 1f355.png
  23. Matching and displaying val PATTERN = “\\ud83c\\udf55".toRegex() val matches =

    PATTERN.findAll(text) for (match in matches) { spannableString.setSpan( ImageSpan(context, R.drawable.emoji_1f355), match.range.start, match.range.endInclusive + 1, SpannableString.SPAN_INCLUSIVE_INCLUSIVE ) } Solution: DynamicDrawableSpan (and resize the drawable)
  24. Images and Spans Pros: Freedom Cons: Lots of manual work

    needed APK size (4000 PNGs…)
  25. Custom font file Strategy: 1. Include font file with Emoji

    2. Set font file containing Emoji to TextView 3. Set text to TextView
  26. Custom font file: Setup 1. Download Android font 2. Place

    in assets/fonts Download:
  27. Custom font file: Setup val typeface = Typeface.createFromAsset(assets, “fonts/emojione.ttf”) textView.typeface

    = typeface textView.text = string
  28. Custom font file: Results

  29. Custom font file: Hold on… textView.text = "Numbers: 1 2

    3 4" Solution: Parse Emoji and create TypefaceSpans
  30. Custom Font: Better but… Pros: Easy to setup Cons: Manual

    work needed
  31. EmojiCompat Strategy: — Process Strings with EmojiCompat or — Use

  32. EmojiCompat: Setup (bundled) implementation “” implementation “” Adds: 7.4 MB

  33. EmojiCompat: Setup (downloadable) val fontRequest = FontRequest( "", "", "Noto

    Color Emoji Compat", R.array.com_google_android_gms_fonts_certs) val config = FontRequestEmojiCompatConfig(this, fontRequest) EmojiCompat.init(config) Init takes 150 ms! But happens asynchronously
  34. EmojiCompat: Setup (downloadable) Downloadable fonts get updated automatically!

  35. EmojiCompat: Setup (downloadable) Source:

  36. EmojiCompat: Setup (downloadable) Downloadable fonts get updated automatically! Find it

    in data/data/ Noto_Color_Emoji_Compat-400-100_0-0_0.ttf Version 2.003;GOOG;noto-emoji:20171030:a5efe6ca61db
  37. EmojiCompat: Using it textView.text = EmojiCompat.get().process(string)

  38. EmojiCompat: Using it

  39. EmojiCompat: Using it

  40. EmojiCompat: Replace All val config = FontRequestEmojiCompatConfig(…) config.setReplaceAll(true) EmojiCompat.init(config)

  41. EmojiCompat: Handling init delay EmojiCompat.get().registerInitCallback(object : EmojiCompat.InitCallback() { override fun

    onInitialized() { textView.text = EmojiCompat.get().process(string) } override fun onFailed(throwable: Throwable?) { // handle error? textView.text = string } } )
  42. EmojiCompat: Using the Views <> <> <>

  43. EmojiCompat: Easy! Wishlist Customize it Emoji Keyboard Emoji tools

  44. Thank you! Miquel Beltran - @Miqubel