1484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir/* 2ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * Copyright 2018 The Android Open Source Project 3484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir * 4484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir * Licensed under the Apache License, Version 2.0 (the "License"); 5484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir * you may not use this file except in compliance with the License. 6484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir * You may obtain a copy of the License at 7484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir * 8484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir * http://www.apache.org/licenses/LICENSE-2.0 9484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir * 10484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir * Unless required by applicable law or agreed to in writing, software 11484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir * distributed under the License is distributed on an "AS IS" BASIS, 12484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir * See the License for the specific language governing permissions and 14484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir * limitations under the License. 15484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir */ 16484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir 17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.emoji.text; 18484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir 19484205f290d5cc989074248f876f72d10000eba1Siyamed Sinirimport android.content.Context; 208346a9a10895008f08f50969689a1ce16fbd577cSeigo Nonakaimport android.content.pm.PackageManager.NameNotFoundException; 216436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonakaimport android.database.ContentObserver; 22484205f290d5cc989074248f876f72d10000eba1Siyamed Sinirimport android.graphics.Typeface; 236436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonakaimport android.net.Uri; 246436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonakaimport android.os.Handler; 256436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonakaimport android.os.HandlerThread; 266436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonakaimport android.os.Process; 276436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonakaimport android.os.SystemClock; 2838746a682208c764867ffe4415d0b62fb22b5b9aAurimas Liutikas 29ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.GuardedBy; 30ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.NonNull; 31ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.Nullable; 32ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.RequiresApi; 33ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.RestrictTo; 34ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.core.graphics.TypefaceCompatUtil; 35ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.core.provider.FontRequest; 36ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.core.provider.FontsContractCompat; 37ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.core.provider.FontsContractCompat.FontFamilyResult; 38ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.core.util.Preconditions; 39484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir 40484205f290d5cc989074248f876f72d10000eba1Siyamed Sinirimport java.nio.ByteBuffer; 41484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir 42484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir/** 43484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir * {@link EmojiCompat.Config} implementation that asynchronously fetches the required font and the 44484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir * metadata using a {@link FontRequest}. FontRequest should be constructed to fetch an EmojiCompat 45484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir * compatible emoji font. 46484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir * <p/> 47484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir */ 48484205f290d5cc989074248f876f72d10000eba1Siyamed Sinirpublic class FontRequestEmojiCompatConfig extends EmojiCompat.Config { 496436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka 506436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka /** 516436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * Retry policy used when the font provider is not ready to give the font file. 526436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * 536436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * To control the thread the retries are handled on, see 546436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * {@link FontRequestEmojiCompatConfig#setHandler}. 556436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka */ 566436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka public abstract static class RetryPolicy { 576436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka /** 586436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * Called each time the metadata loading fails. 596436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * 606436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * This is primarily due to a pending download of the font. 616436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * If a value larger than zero is returned, metadata loader will retry after the given 626436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * milliseconds. 636436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * <br /> 646436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * If {@code zero} is returned, metadata loader will retry immediately. 656436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * <br/> 666436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * If a value less than 0 is returned, the metadata loader will stop retrying and 676436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * EmojiCompat will get into {@link EmojiCompat#LOAD_STATE_FAILED} state. 686436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * <p/> 696436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * Note that the retry may happen earlier than you specified if the font provider notifies 706436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * that the download is completed. 716436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * 726436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * @return long milliseconds to wait until next retry 736436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka */ 746436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka public abstract long getRetryDelay(); 756436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 766436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka 776436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka /** 786436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * A retry policy implementation that doubles the amount of time in between retries. 796436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * 806436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * If downloading hasn't finish within given amount of time, this policy give up and the 816436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * EmojiCompat will get into {@link EmojiCompat#LOAD_STATE_FAILED} state. 826436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka */ 836436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka public static class ExponentialBackoffRetryPolicy extends RetryPolicy { 846436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka private final long mTotalMs; 856436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka private long mRetryOrigin; 866436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka 876436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka /** 886436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * @param totalMs A total amount of time to wait in milliseconds. 896436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka */ 906436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka public ExponentialBackoffRetryPolicy(long totalMs) { 916436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mTotalMs = totalMs; 926436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 936436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka 946436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka @Override 956436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka public long getRetryDelay() { 966436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka if (mRetryOrigin == 0) { 976436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mRetryOrigin = SystemClock.uptimeMillis(); 986436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka // Since download may be completed after getting query result and before registering 996436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka // observer, requesting later at the same time. 1006436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka return 0; 1016436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } else { 1026436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka // Retry periodically since we can't trust notify change event. Some font provider 1036436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka // may not notify us. 1046436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka final long elapsedMillis = SystemClock.uptimeMillis() - mRetryOrigin; 1056436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka if (elapsedMillis > mTotalMs) { 1066436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka return -1; // Give up since download hasn't finished in 10 min. 1076436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 1086436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka // Wait until the same amount of the time from the first scheduled time, but adjust 1096436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka // the minimum request interval is 1 sec and never exceeds 10 min in total. 1106436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka return Math.min(Math.max(elapsedMillis, 1000), mTotalMs - elapsedMillis); 1116436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 1126436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 1136436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka }; 1146436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka 115484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir /** 116484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir * @param context Context instance, cannot be {@code null} 117484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir * @param request {@link FontRequest} to fetch the font asynchronously, cannot be {@code null} 118484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir */ 119484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir public FontRequestEmojiCompatConfig(@NonNull Context context, @NonNull FontRequest request) { 1208346a9a10895008f08f50969689a1ce16fbd577cSeigo Nonaka super(new FontRequestMetadataLoader(context, request, DEFAULT_FONTS_CONTRACT)); 121484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir } 122484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir 123484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir /** 124484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir * @hide 125484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir */ 126484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 127484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir public FontRequestEmojiCompatConfig(@NonNull Context context, @NonNull FontRequest request, 1286436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka @NonNull FontProviderHelper fontProviderHelper) { 1296436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka super(new FontRequestMetadataLoader(context, request, fontProviderHelper)); 1306436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 1316436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka 1326436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka /** 1336436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * Sets the custom handler to be used for initialization. 1346436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * 1356436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * Since font fetch take longer time, the metadata loader will fetch the fonts on the background 1366436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * thread. You can pass your own handler for this background fetching. This handler is also used 1376436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * for retrying. 1386436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * 1396436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * @param handler A {@link Handler} to be used for initialization. Can be {@code null}. In case 1406436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * of {@code null}, the metadata loader creates own {@link HandlerThread} for 1416436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * initialization. 1426436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka */ 1436436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka public FontRequestEmojiCompatConfig setHandler(Handler handler) { 1446436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka ((FontRequestMetadataLoader) getMetadataRepoLoader()).setHandler(handler); 1456436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka return this; 146484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir } 147484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir 1486436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka /** 1496436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * Sets the retry policy. 1506436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * 1516436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * {@see RetryPolicy} 1526436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * @param policy The policy to be used when the font provider is not ready to give the font 1536436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * file. Can be {@code null}. In case of {@code null}, the metadata loader never 1546436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka * retries. 1556436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka */ 1566436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka public FontRequestEmojiCompatConfig setRetryPolicy(RetryPolicy policy) { 1576436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka ((FontRequestMetadataLoader) getMetadataRepoLoader()).setRetryPolicy(policy); 1586436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka return this; 1596436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 160484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir 161484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir /** 162c685244d1fa0ac20b32f3b62a301b7d7fbd5a832Siyamed Sinir * MetadataRepoLoader implementation that uses FontsContractCompat and TypefaceCompat to load a 1630b03693667d95d2202dfbb24866665ff061acce1Seigo Nonaka * given FontRequest. 164484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir */ 165c685244d1fa0ac20b32f3b62a301b7d7fbd5a832Siyamed Sinir private static class FontRequestMetadataLoader implements EmojiCompat.MetadataRepoLoader { 166484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir private final Context mContext; 167484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir private final FontRequest mRequest; 1686436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka private final FontProviderHelper mFontProviderHelper; 1696436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka 1706436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka private final Object mLock = new Object(); 1716436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka @GuardedBy("mLock") 1726436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka private Handler mHandler; 1736436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka @GuardedBy("mLock") 1746436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka private HandlerThread mThread; 1756436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka @GuardedBy("mLock") 1766436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka private @Nullable RetryPolicy mRetryPolicy; 1776436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka 1786436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka // Following three variables must be touched only on the thread associated with mHandler. 1796436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka private EmojiCompat.MetadataRepoLoaderCallback mCallback; 1806436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka private ContentObserver mObserver; 1816436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka private Runnable mHandleMetadataCreationRunner; 182484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir 183484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir FontRequestMetadataLoader(@NonNull Context context, @NonNull FontRequest request, 1846436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka @NonNull FontProviderHelper fontProviderHelper) { 185484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir Preconditions.checkNotNull(context, "Context cannot be null"); 186484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir Preconditions.checkNotNull(request, "FontRequest cannot be null"); 187484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir mContext = context.getApplicationContext(); 188484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir mRequest = request; 1896436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mFontProviderHelper = fontProviderHelper; 1906436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 1916436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka 1926436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka public void setHandler(Handler handler) { 1936436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka synchronized (mLock) { 1946436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mHandler = handler; 1956436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 1966436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 1976436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka 1986436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka public void setRetryPolicy(RetryPolicy policy) { 1996436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka synchronized (mLock) { 2006436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mRetryPolicy = policy; 2016436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 202484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir } 203484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir 204484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir @Override 20577b5c5b734f9f665577d1e3d178615db43ae1d4fSiyamed Sinir @RequiresApi(19) 206c685244d1fa0ac20b32f3b62a301b7d7fbd5a832Siyamed Sinir public void load(@NonNull final EmojiCompat.MetadataRepoLoaderCallback loaderCallback) { 207484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir Preconditions.checkNotNull(loaderCallback, "LoaderCallback cannot be null"); 2086436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka synchronized (mLock) { 2096436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka if (mHandler == null) { 2106436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka // Developer didn't give a thread for fetching. Create our own one. 2116436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mThread = new HandlerThread("emojiCompat", Process.THREAD_PRIORITY_BACKGROUND); 2126436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mThread.start(); 2136436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mHandler = new Handler(mThread.getLooper()); 2146436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 2156436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mHandler.post(new Runnable() { 2166436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka @Override 2176436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka public void run() { 2186436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mCallback = loaderCallback; 2196436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka createMetadata(); 2206436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 2216436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka }); 2226436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 223484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir } 224484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir 2256436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka private FontsContractCompat.FontInfo retrieveFontInfo() { 2266436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka final FontsContractCompat.FontFamilyResult result; 2276436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka try { 2286436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka result = mFontProviderHelper.fetchFonts(mContext, mRequest); 2296436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } catch (NameNotFoundException e) { 2306436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka throw new RuntimeException("provider not found", e); 2316436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 2326436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka if (result.getStatusCode() != FontsContractCompat.FontFamilyResult.STATUS_OK) { 2336436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka throw new RuntimeException("fetchFonts failed (" + result.getStatusCode() + ")"); 2346436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 2356436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka final FontsContractCompat.FontInfo[] fonts = result.getFonts(); 2366436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka if (fonts == null || fonts.length == 0) { 2376436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka throw new RuntimeException("fetchFonts failed (empty result)"); 2386436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 2396436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka return fonts[0]; // Assuming the GMS Core provides only one font file. 240484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir } 241484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir 2426436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka // Must be called on the mHandler. 2436436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka @RequiresApi(19) 2446436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka private void scheduleRetry(Uri uri, long waitMs) { 2456436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka synchronized (mLock) { 2466436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka if (mObserver == null) { 2476436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mObserver = new ContentObserver(mHandler) { 2486436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka @Override 2496436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka public void onChange(boolean selfChange, Uri uri) { 2506436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka createMetadata(); 2516436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 2526436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka }; 2536436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mFontProviderHelper.registerObserver(mContext, uri, mObserver); 2548346a9a10895008f08f50969689a1ce16fbd577cSeigo Nonaka } 2556436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka if (mHandleMetadataCreationRunner == null) { 2566436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mHandleMetadataCreationRunner = new Runnable() { 2576436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka @Override 2586436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka public void run() { 2596436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka createMetadata(); 2606436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 2616436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka }; 2628346a9a10895008f08f50969689a1ce16fbd577cSeigo Nonaka } 2636436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mHandler.postDelayed(mHandleMetadataCreationRunner, waitMs); 2646436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 2656436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 2666436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka 2676436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka // Must be called on the mHandler. 2686436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka private void cleanUp() { 2696436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mCallback = null; 2706436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka if (mObserver != null) { 2716436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mFontProviderHelper.unregisterObserver(mContext, mObserver); 2726436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mObserver = null; 2736436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 2746436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka synchronized (mLock) { 2756436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mHandler.removeCallbacks(mHandleMetadataCreationRunner); 2766436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka if (mThread != null) { 2776436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mThread.quit(); 2788346a9a10895008f08f50969689a1ce16fbd577cSeigo Nonaka } 2796436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mHandler = null; 2806436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mThread = null; 2816436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 2826436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 2836436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka 2846436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka // Must be called on the mHandler. 2856436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka @RequiresApi(19) 2866436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka private void createMetadata() { 2876436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka if (mCallback == null) { 2886436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka return; // Already handled or cancelled. Do nothing. 2896436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 2906436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka try { 2916436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka final FontsContractCompat.FontInfo font = retrieveFontInfo(); 2928346a9a10895008f08f50969689a1ce16fbd577cSeigo Nonaka 2936436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka final int resultCode = font.getResultCode(); 2946436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka if (resultCode == FontsContractCompat.Columns.RESULT_CODE_FONT_UNAVAILABLE) { 2956436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka // The font provider is now downloading. Ask RetryPolicy for when to retry next. 2966436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka synchronized (mLock) { 2976436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka if (mRetryPolicy != null) { 2986436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka final long delayMs = mRetryPolicy.getRetryDelay(); 2996436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka if (delayMs >= 0) { 3006436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka scheduleRetry(font.getUri(), delayMs); 3016436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka return; 3026436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 3036436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 3046436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 3058346a9a10895008f08f50969689a1ce16fbd577cSeigo Nonaka } 3068346a9a10895008f08f50969689a1ce16fbd577cSeigo Nonaka 3076436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka if (resultCode != FontsContractCompat.Columns.RESULT_CODE_OK) { 3086436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka throw new RuntimeException("fetchFonts result is not OK. (" + resultCode + ")"); 309484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir } 3108346a9a10895008f08f50969689a1ce16fbd577cSeigo Nonaka 3116436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka // TODO: Good to add new API to create Typeface from FD not to open FD twice. 3126436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka final Typeface typeface = mFontProviderHelper.buildTypeface(mContext, font); 3136436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka final ByteBuffer buffer = TypefaceCompatUtil.mmap(mContext, null, font.getUri()); 3146436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka if (buffer == null) { 3156436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka throw new RuntimeException("Unable to open file."); 3166436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 3176436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mCallback.onLoaded(MetadataRepo.create(typeface, buffer)); 3186436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka cleanUp(); 319484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir } catch (Throwable t) { 3206436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka mCallback.onFailed(t); 3216436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka cleanUp(); 322484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir } 323484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir } 324484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir } 325484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir 3268346a9a10895008f08f50969689a1ce16fbd577cSeigo Nonaka /** 3278346a9a10895008f08f50969689a1ce16fbd577cSeigo Nonaka * Delegate class for mocking FontsContractCompat.fetchFonts. 3288346a9a10895008f08f50969689a1ce16fbd577cSeigo Nonaka * @hide 3298346a9a10895008f08f50969689a1ce16fbd577cSeigo Nonaka */ 3308346a9a10895008f08f50969689a1ce16fbd577cSeigo Nonaka @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 3316436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka public static class FontProviderHelper { 3328346a9a10895008f08f50969689a1ce16fbd577cSeigo Nonaka /** Calls FontsContractCompat.fetchFonts. */ 3338346a9a10895008f08f50969689a1ce16fbd577cSeigo Nonaka public FontFamilyResult fetchFonts(@NonNull Context context, 3348346a9a10895008f08f50969689a1ce16fbd577cSeigo Nonaka @NonNull FontRequest request) throws NameNotFoundException { 3358346a9a10895008f08f50969689a1ce16fbd577cSeigo Nonaka return FontsContractCompat.fetchFonts(context, null /* cancellation signal */, request); 3368346a9a10895008f08f50969689a1ce16fbd577cSeigo Nonaka } 3378346a9a10895008f08f50969689a1ce16fbd577cSeigo Nonaka 3386436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka /** Calls FontsContractCompat.buildTypeface. */ 3396436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka public Typeface buildTypeface(@NonNull Context context, 3406436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka @NonNull FontsContractCompat.FontInfo font) throws NameNotFoundException { 3416436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka return FontsContractCompat.buildTypeface(context, null /* cancellation signal */, 3426436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka new FontsContractCompat.FontInfo[] { font }); 3436436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 3446436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka 3456436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka /** Calls Context.getContentObserver().registerObserver */ 3466436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka public void registerObserver(@NonNull Context context, @NonNull Uri uri, 3476436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka @NonNull ContentObserver observer) { 3486436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka context.getContentResolver().registerContentObserver( 3496436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka uri, false /* notifyForDescendants */, observer); 3506436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka 3516436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 3526436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka /** Calls Context.getContentObserver().unregisterObserver */ 3536436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka public void unregisterObserver(@NonNull Context context, 3546436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka @NonNull ContentObserver observer) { 3556436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka context.getContentResolver().unregisterContentObserver(observer); 3566436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka } 3576436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka }; 3586436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka 3596436cc6a00500b2b50b1e46d0c12379c15fd4f04Seigo Nonaka private static final FontProviderHelper DEFAULT_FONTS_CONTRACT = new FontProviderHelper(); 3608346a9a10895008f08f50969689a1ce16fbd577cSeigo Nonaka 361484205f290d5cc989074248f876f72d10000eba1Siyamed Sinir} 362