No More Tofu - Mastering Emoji on Android (Chicago Roboto)

No More Tofu - Mastering Emoji on Android (Chicago Roboto)

72ffb135de71bef2c4a11961634edc6a?s=128

Miguel Beltran

April 13, 2018
Tweet

Transcript

  1. 2.

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

    solutions do we have? Is EmojiCompat the greatest thing ever?
  2. 6.
  3. 7.
  4. 8.
  5. 9.

    Emotions Source: People interpret the same emoji in completely different

    ways https://www.theverge.com/2016/4/11/11406944/emoji-meaning-difference-ios-android
  6. 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
  7. 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"
  8. 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
  9. 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
  10. 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"
  11. 16.

    How long is an Emoji? —> Improvement val it =

    BreakIterator.getCharacterInstance() it.setText(emoji) var count = 0 while (it.next() != BreakIterator.DONE) { count++ } count = 3 on API 23 count = 1 on API 25
  12. 18.

    Let’s look at three solutions Custom solution: Images and Spans

    Custom solution: Font file Using EmojiCompat
  13. 19.

    Images and Spans Strategy: 1. Find an Emoji on a

    String 2. Replace with an ImageSpan 3. Repeat for the whole String
  14. 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
  15. 21.
  16. 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
  17. 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)
  18. 25.

    Custom font file Strategy: 1. Include font file with Emoji

    2. Set font file containing Emoji to TextView 3. Set text to TextView
  19. 26.

    Custom font file: Setup 1. Download Android font 2. Place

    in assets/fonts Download: https://www.emojione.com/download
  20. 29.

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

    3 4" Solution: Parse Emoji and create TypefaceSpans
  21. 33.

    EmojiCompat: Setup (downloadable) val fontRequest = FontRequest( "com.google.android.gms.fonts", "com.google.android.gms", "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
  22. 36.

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

    in data/data/com.google.android.gms/files/fonts/opentype Noto_Color_Emoji_Compat-400-100_0-0_0.ttf Version 2.003;GOOG;noto-emoji:20171030:a5efe6ca61db
  23. 40.

    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 } } )