Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Support LibraryのDownloadable FontsやEmojiCompatに対応したアプリを作ろう

takahirom
February 09, 2018
2.2k

Support LibraryのDownloadable FontsやEmojiCompatに対応したアプリを作ろう

DroidKaigi 登壇資料

takahirom

February 09, 2018
Tweet

Transcript

  1. ˖ UBLBIJSPN !OFX@SVOOBCMF  ˖ 劤せכ嬁「䃨峔 ׭׿ׄױ׋ַמ׹  ˖ "OESPJEָ㥨ֹ

    ˖ "CFNB57ך"OESPJE،فٔ׾⡲׏גְתׅկ ˖ %SPJE,BJHJⰕ䒭،فٔךٔ٦ت٦׾׃ת׃׋
 ⟃♳ךفؙٕٔؒأز֮׶ָהֲ׀ְׂת׃׋!
 ؎ٝأز٦ٕ׃גזַ׏׋׵؎ٝأز٦ٕ׃גֻ׌ְׁ 荈ⴓחאְג
  2. ˖ %PXOMPBEBCMF'POU ˖ %PXOMPBEBCMF'POUך稱➜ ˖ 'POU3FTPVSDFך稱➜ ˖ %PXOMPBEBCMF'POU׾⢪׏ג׫״ֲ ˖ %PXOMPBEBCMF'POUך➬穈׫

    ˖ וֲװ׏ג%PXOMPBEBCMF'POU׾⢪׏גְַֻ ˖ &NPKJ$PNQBU ˖ &NPKJ$PNQBUך稱➜ ˖ &NPKJ$PNQBUך➬穈׫ ˖ וֲװ׏ג&NPKJ$PNQBU׾⢪׏גְַֻ ˖ ذأز倯岀 ˖ תה׭ 湡如
  3. ˖ فؚٗٓئַا٦أַ׵《䖤ׅ׷ ˖ (PPHMF1MBZ4FSWJDFTח'POU1SPWJEFSָ֮׶
 ׉ַֿ׵《䖤ׅ׷ ˖ ،فٔחؿؓٝز׾㙵׭鴥תזְ ˖ ،فٔחؿؓٝز׾湫䱸تؐٝٗ٦س׃זְ ˖

    (PPHMF1MBZ4FSWJDFTָؿؓٝز׾
 ٍؗحءُ׃גְֶגֻ׸׷ IUUQTEFWFMPQFSBOESPJEDPNHVJEFUPQJDTVJMPPL BOEGFFMEPXOMPBEBCMFGPOUTIUNM״׶ %PXOMPBEBCMF'POUהכ
  4. ˖ ؕأةيؿؓٝز׾黝䘔דֹ׷ ˖ وذٔ،ٕرؠ؎ٝך黝䘔 ˖ 畭劣㔿剣ךؿؓٝز׾⢪׻זֻ⳿勻׷ ˖ "1,؟؎ؤ׾幾׵ׇ׷ ˖ ׉׸ח״׶،فٔך؎ٝأز٦ٕ䧭⸆桦ָ㟓װׇ׷

    ˖ 'POU׾➭ך،فٔהءؑ،דֹ׷ ˖ 鸐⥋ꆀך⵴幾 ˖ ًٌٔך⢪欽ꆀך⵴幾 ˖ ر؍أؙأل٦أך⵴幾 %PXOMPBEBCMF'POUךًٔحز
  5. 'POU3FTPVSDF #VOEMFE'POUGPOUGBNJMZ YNM  <?xml version="1.0" encoding="utf-8"?> <font-family xmlns:app=“../apk/res-auto”> <font

    app:font="@font/orbitron_regular" app:fontStyle="normal" /> <font app:font="@font/orbitron_bold" app:fontStyle="normal" app:fontWeight="700" /> </font-family> UFYU4UZMFח״׏ג⢪ֲؿؓٝز׾㢌ִ׷ <TextView android:fontFamily="@font/orbitron" android:textStyle="bold"
  6. "QQ$PNQBU5FYU7JFXַ׵ 《䖤׃חְֻ switch (name) { case "TextView": view = new

    AppCompatTextView(context, attrs); break; case "ImageView": view = new AppCompatImageView(context, attrs); break; "QQ$PNQBU7JFX*OBUFSKBWB ٖ؎،ؐزךˑ5FYU7JFX˒ך ➿׻׶ח"QQ$PNQBU5FYU7JFXָ ⡲׵׸׷
  7. ResourcesCompat.FontCallback replyCallback = new ResourcesCompat.FontCallback() { @Override public void onFontRetrieved(@NonNull

    Typeface typeface) onAsyncTypefaceReceived(textViewWeak, typeface); } @Override public void onFontRetrievalFailed(int reason) { // Do nothing. } }; try { // Note the callback will be triggered on the UI thread. mFontTypeface = a.getFont(fontFamilyId, mStyle, replyCallback); // If this call gave us an immediate result, ignore any "QQ$PNQBU5FYU)FMQFS $BMM#BDL׾⡲䧭
  8. @Override public void onFontRetrievalFailed(int reason) { // Do nothing. }

    }; try { // Note the callback will be triggered on the UI thread mFontTypeface = a.getFont(fontFamilyId, mStyle, replyCallback); // If this call gave us an immediate result, ignore any pending callbacks. mAsyncFontPending = mFontTypeface == null; } catch (UnsupportedOperationException | Resources.NotFoundException e) { // Expected if it is not a font resource. } private void onAsyncTypefaceReceived(WeakReference<TextView textViewWeak, Typeface typeface) { if (mAsyncFontPending) { mFontTypeface = typeface; final TextView textView = textViewWeak.get(); 5JOU5ZQFE"SSBZHFU'POU ד《䖤ׅ׷
  9. private void onAsyncTypefaceReceived(WeakReference<TextView> textViewWeak, Typeface typeface) { if (mAsyncFontPending) {

    mFontTypeface = typeface; final TextView textView = textViewWeak.get(); if (textView != null) { textView.setTypeface(typeface, mStyle); } } } ꬊず劍דⳢ椚׃ג《䖤דֹ׋׵ UFYU7JFXTFU5ZQFGBDF ד إحزׅ׷
  10. 1BDLBHF.BOBHFS׾⢪׏ג ػح؛٦آך縭せثؑحؙ List<byte[]> signatures; PackageInfo packageInfo = packageManager.getPackageInfo(info.packageName, PackageManager.GET_SIGNATURES); signatures

    = convertToByteArrayList(packageInfo.signatures); … List<List<byte[]>> requestCertificatesList = request.getCertificates(); for (int i = 0; i < requestCertificatesList.size(); ++i) { ػح؛٦آوط٦آַٍ׵ (PPHMF1MBZ4FSWJDFTך،فٔך ءؚطثٍ׾《䖤׃גغ؎ز⴨חׅ׷ ˑDPNHPPHMFBOESPJEHNT (PPHMF1MBZ4FSWJDFT 'POUT$POUSBDU$PNQBU
  11. List<List<byte[]>> requestCertificatesList = request.getCertificates(); for (int i = 0; i

    < requestCertificatesList.size(); ++i) { List<byte[]> requestSignatures = new ArrayList<>(requestCertificatesList.get(i)); … if (equalsByteArrayList(signatures, requestSignatures)) { return info; } } return null; غ؎ز⴨׾嫰鯰׃ג♧筰ז׵䞔㜠׾鵤ׅ
  12. val uri = Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority("com.google.android.gms.fonts") .build() var cursor =

    contentResolver.query( uri, arrayOf( FontsContractCompat.Columns._ID, FontsContractCompat.Columns.FILE_ID, FontsContractCompat.Columns.TTC_INDEX, … ), "query = ?", arrayOf("Days One"), null ) $POUFOU3FTPMWFS׾⢪׏ג《䖤ׅ׷ 'POUせ
  13. ➬穈׫ך鍑铡 'POUT$POUSBDU$PNQBU ⯓玎ך$POUFOU1SPWJEFSך倯岀חכ縭せثؑحָؙⰅ׏גֶ ׵׆ծ㸜Ⰻדכזְדׅկ 'POUT$POUSBDU׾⢪ֲהػح؛٦آךثؑحؙ׮׃ג㸜Ⰻח 䭯׏גֿ׸תׅկ val request = FontRequest(

    "com.google.android.gms.fonts", "com.google.android.gms", "Days One", R.array.com_google_…_fonts_certs_prod ) FontsContractCompat.requestFont( this, request, object : FontsContractCompat.FontRequestCallback() {
  14. val request = FontRequest( "com.google.android.gms.fonts", "com.google.android.gms", "Days One", R.array.com_google_…_fonts_certs_prod )

    FontsContractCompat.requestFont( this, request, object : FontsContractCompat.FontRequestCallback() override fun onTypefaceRetrieved(typeface: Type textView.typeface = typeface } override fun onTypefaceRequestFailed(reason: In super.onTypefaceRequestFailed(reason) } }, Handler() 'POU3FRVFTU׾⡲䧭 $POUFOU3FTPMWFSח䗳銲ז"VUIPSJUZせװ ؿؓٝزせծ縭せָⰅ׏גְ׷ٔا٦أ׾ 䭷㹀ׅ׷
  15. quest = FontRequest( m.google.android.gms.fonts", m.google.android.gms", ys One", rray.com_google_…_fonts_certs_prod ontractCompat.requestFont( this,

    request, object : FontsContractCompat.FontRequestCallback() { override fun onTypefaceRetrieved(typeface: Typeface?) { textView.typeface = typeface } override fun onTypefaceRequestFailed(reason: Int) { super.onTypefaceRequestFailed(reason) } }, Handler() 《׏גֿ׸׋؝٦ٕغحؙד 5FYU7JFXךUZQFGBDFחإحزׅ׷
  16. 3FTPVSDF$PNQBU׾⢪׏ג 'POU3FTPVSDF׾《׶⳿ׅ 'POUT$POUSBDU$PNQBUכ$POUFOU1SPWJEFS״׶嚂דָׅծ
 (PPHMF1MBZ4FSWJDFTךػح؛٦آせ䭷㹀׃׋׶הַ㣐㢌דׅ ⯓玎铡僇׃גְ׋'POU3FTPVSDF׾⢪׏ג׫ת׃׳ֲ 'POU3FTPVSDFד⯓玎剅ְגְ׋ػح؛٦آせזוָ鎸鯹ׁ׸גְ תׅ <?xml version="1.0" encoding="utf-8"?>

    <font-family xmlns:app="http://schemas.android.com/apk/res-auto" app:fontProviderAuthority="com.google.android.gms.fonts" app:fontProviderPackage="com.google.android.gms" app:fontProviderQuery="Days One" app:fontProviderCerts="@array/com_google_android_gms_fonts_certs"> </font-family>
  17. 3FTPVSDFT$PNQBU ResourcesCompat.getFont( this, R.font.orbitron, object : ResourcesCompat.FontCallback() { override fun

    onFontRetrieved(typeface: Typeface) { fontText.typeface = typeface } override fun onFontRetrievalFailed(reason: Int) { } }, Handler()) ꬊず劍ד《䖤׮דֹתׅկ 4VQQPSU-JCַ׵鷄⸇ׁ׸׋ًاحس
  18. %PXOMPBEBCMF'POUח 傈劤铂ָזְ⟝ Get Started with the Google Fonts for Android

    ͱ͍͏ϖʔδʹҎԼͷΑ͏ʹ͋Γ·͢ fonts.google.comʹ͋ΔϑΥϯτ͕࢖͑Δʂ
  19. 'POU3FTPVSDF椚䟝涸ז㼪Ⰵ <style name=“TextAppearance.App.Title" parent="TextAppearance.AppCompat.Title"> <item name="android:textSize">21sp</item> <item name="android:fontFamily">@font/notosans_medium</item> </style> <style

    name="TextAppearance.App.Subhead" parent="TextAppearance.AppCompat.Subhead"> <item name="android:textSize">17sp</item> <item name="android:fontFamily">@font/notosans_regular</item> </style> ׉׸׊׸ך5FYU"QQFBSBODF欽䠐׃גGPOU'BNJMZה㣐ֹׁ׾䭷㹀 ֿֿדGPOUOPUPTBOT@NFEJVNPUGזוד ؿؓٝزؿ؋؎ٕ،فٔחⰅ׸׷ %SPJE,BJHJ״׶
  20. رؿٕؓزךؿؓٝزך鏣㹀 ذ٦وהַדז׿הַדֹזְַ <style name=“AppTheme" parent="Theme.AppCompat.Light"> <item name=“android:textAppearance">@style/TextAppearance.hogehoge</item> <item name=“android:textAppearanceSmall”>@style/TextAppearance..</item> <item

    name="android:textAppearanceButton">@style/TextAppearance…</item> <item name=“android:textAppearanceMediumInverse”>@style/TextAppearance…</i ɻɻɻɻ ❌דֹ׷ֽו׋ֻׁ׿֮׷կ㣐㢌
  21. رؿٕؓزךؿؓٝزך鏣㹀 DISJTKFOY$BMMJHSBQIZ׾⢪ֲ $BMMJHSBQIZהכ"OESPJEךأةٝت٦سזٓ؎ـٓٔד رؿٕؓزךؿؓٝز׾鏣㹀דֹ׷׮ך @Override public void onCreate() { super.onCreate();

    CalligraphyConfig.initDefault(new CalligraphyConfig.Builder() .setDefaultFontPath("fonts/Roboto-RobotoRegular.ttf") .setFontAttrId(R.attr.fontPath) .build() ); ❌׋׌׃$BMMJHSBQIZכ僓ַ׵֮׷ٓ؎ـٓٔד 倜׃ְ'POU3FTPVSDFכ䭷㹀דֹ׆ծ✼䳔䚍׮זְ
  22. &NPKJ$PNQBUך㛇劤涸ז⢪ְ倯 val fontRequest = FontRequest( "com.google.android.gms.fonts", "com.google.android.gms", "Noto Color EmojiCompat",

    R.array.com_google_android_gms_fonts_certs) val config = FontRequestEmojiCompatConfig(context, fontRequest) EmojiCompat.init(config) "QQMJDBUJPOPO$SFBUF
  23. &NPKJ$PNQBUך㛇劤涸ז⢪ְ倯 val fontRequest = FontRequest( "com.google.android.gms.fonts", "com.google.android.gms", "Noto Color EmojiCompat",

    R.array.com_google_android_gms_fonts_certs) val config = FontRequestEmojiCompatConfig(context, fontRequest) EmojiCompat.init(config) ⯓玎ך'POU3FRVFTU׾⢪׏ג &NPKJ$PNQBUJOJU דⴱ劍⻉ "QQMJDBUJPOPO$SFBUF
  24. &NPKJ$PNQBUך㛇劤涸ז⢪ְ倯 val fontRequest = FontRequest( "com.google.android.gms.fonts", "com.google.android.gms", "Noto Color EmojiCompat",

    R.array.com_google_android_gms_fonts_certs) val config = FontRequestEmojiCompatConfig(context, fontRequest) EmojiCompat.init(config) ⯓玎ך'POU3FRVFTU׾⢪׏ג &NPKJ$PNQBUJOJU דⴱ劍⻉ "QQMJDBUJPOPO$SFBUF implementation “com.android.support:support-emoji:27.0.2"
  25. ➬穈׫חⰅ׷⵸ח窩俑㶵חאְג // String -> code point val face = "\ud83d\udc68"

    val codePoint: Int = Character.toCodePoint(face[0], face[1]) println(Integer.toHexString(codePoint)) // code point -> String val charArray: CharArray = Character.toChars(codePoint) val string = String(charArray) println(string) ؝٦سه؎ٝز׾《䖤׃׋׶ծ 俑㶵⴨ח湫ֿׅהָדֹ׷ $IBSBDUFSUP$PEF1PJOUחDIBS׾א床ׅ׌ֽ
  26. ➬穈׫חⰅ׷⵸ח窩俑㶵חאְג // String -> code point val face = "\ud83d\udc68"

    val codePoint: Int = Character.toCodePoint(face[0], face[1]) println(Integer.toHexString(codePoint)) // code point -> String val charArray: CharArray = Character.toChars(codePoint) val string = String(charArray) println(string) $IBSBDUFSUP$IBST ח؝٦سه؎ٝز床ׅ׌ֽ
  27. ➬穈׫חⰅ׷⵸ח窩俑㶵חאְג val face = Character.toChars(0x1f468) val lightSkin = Character.toChars(0x1f3fb) val

    zwj = Character.toChars(0x200d) val computer = Character.toChars(0x1f4bb) val programmer = face + lightSkin + zwj + computer programmer.forEach { println(Integer.toHexString(it.toInt())) } 6 '6 ''#6 %6 '## ;8+ ֿך؝٦سه؎ٝز׾؝٦سד邌植ׅ׷חכ ֿ׸דת׆כDIBSךBSSBZָ《䖤דֹ׋ 如ךأٓ؎سד˒QSPHSBNNFS˒׾⢪ֲ
  28. ➬穈׫חⰅ׷⵸ח窩俑㶵חאְג textView.typeface = ResourcesCompat.getFont(this, R.font.noto_color_emoji) textView.text = String(programmer) // or

    textView.text = "\ud83d\udc68\ud83c\udffb\u200d\ud83d\udcbb" &NPKJ$PNQBUָזֻג׮窩俑㶵כ ⹛ָֻկկ
  29. public final class TypefaceEmojiSpan extends EmojiSpan { … @Override public

    void draw(@NonNull final Canvas canvas, …, @NonNull final Paint paint) { getMetadata().draw(canvas, x, y, paint); } … } QBJOUחTFU5ZQFGBDF׃ג DBOWTESBX5FYU ׅ׷׌ֽ &NPKJ.FUBEBUBKBWB public void draw(@NonNull final Canvas canvas, final float x,… @NonNull final Paint paint) { final Typeface typeface = mMetadataRepo.getTypeface(); … paint.setTypeface(typeface); … canvas.drawText(…, paint); }
  30. 窩俑㶵׾ⴻ㹀ׅ׷➬穈׫ ֿך加׾⢪׏גծ俑㶵⴨ַ׵א׆א؝٦سه؎ٝز׾ 铣׿דծ加ח֮׷EBUB׾黝䘔׃גְֻ int currentOffset = start; int codePoint =

    Character.codePointAt(charSequence, currentOffset); while (currentOffset < end && addedCount < maxEmojiCount) { final int action = sm.check(codePoint); switch (action) { case ACTION_ADVANCE_BOTH: start += Character.charCount(Character.codePointAt(charSequence, start)); currentOffset = start; if (currentOffset < end) { codePoint = Character.codePointAt(charSequence, currentOffset); } …
  31. ذأز倯岀 ˖ 㛇劤涸ח縧ֹ䳔׻׏גְ׷ַוֲַד然钠ָדֹ ׷ ˖ ؿؓٝزך麩ְכ然钠ׅ׷ךָ㔭ꨇזךדծ NVSPPLBHPEJJNBHFזוד然钠ׅ׷ה葺ַ׏ ׋ ˖ ֲתֻ⹛ַזְהֹכ4VQQPSU-JCSBSZזךדծ兛

    鸐חرغحؚד鋅׸׷׃؝٦س׮铣׭׷ ˖ 4VQQPSU-JCSBSZך刿倜㾶娖׾׫גְ׋׌ֽ׷הغ ؚؿ؍حؙأ׾걼籕ח׃גְ׷״ֲזךדծ岣鋔 ׃׋קֲָ葺ׁ׉ֲ
  32. ⿫罋俑柃 䒷欽 ˖ "OESPJE%FWFMPQFSTIUUQTEFWFMPQFSBOESPJEDPNJOEFYIUNM ˖ "OESPJE0QFO4PVSDF1SPKFDUIUUQTTPVSDFBOESPJEDPN ˖ &NPKJ$IBSUTIUUQVOJDPEFPSHFNPKJDIBSUTGVMMFNPKJMJTUIUNM ˖ &YQMPSJOHUIF"OESPJE&NPKJ$PNQBU-JCSBSZIUUQTNFEJVNDPNFYQMPSJOHBOESPJE

    FYQMPSJOHUIFBOESPJEFNPKJDPNQBUJCJMJUZMJCSBSZCGCCBB ˖ (PPHMF*0IUUQTXXXZPVUVCFDPNXBUDI W/,WF+'6 ˖ QPUBUPUJQTד"OESPJEך'POUワ׶ך倜堣腉חאְג涪邌׃ת׃׋կIUUQT RJJUBDPNUTVZPTIJUFNTCFED ˖ +BWBח״׷6OJDPEF؟ٗ؜٦زفؚٗٓىؚٝIUUQTXXXJCNDPN EFWFMPQFSXPSLTKQZTMMJCSBSZKBWBKVOJDPEF@TVSSPHBUFJOEFYIUNM ˖ "OESPJEָד窩俑㶵ח׍ׯ׿ה㼎䘔׃׋IUUQTNTUTTLCMPHTQPUKQ FNPKJIUNM