diff options
| -rw-r--r-- | core/java/android/provider/FontsContract.java | 23 | ||||
| -rw-r--r-- | core/tests/coretests/AndroidManifest.xml | 5 | ||||
| -rw-r--r-- | core/tests/coretests/assets/fonts/samplefont1.ttf | bin | 0 -> 696 bytes | |||
| -rw-r--r-- | core/tests/coretests/assets/fonts/samplefont1.ttx | 177 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/provider/FontsContractE2ETest.java | 175 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/provider/MockFontProvider.java | 202 | ||||
| -rw-r--r-- | graphics/java/android/graphics/fonts/FontRequest.java | 14 |
7 files changed, 595 insertions, 1 deletions
diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java index 3fa92b8c829a..068628a6a783 100644 --- a/core/java/android/provider/FontsContract.java +++ b/core/java/android/provider/FontsContract.java @@ -44,6 +44,7 @@ import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.ResultReceiver; import android.util.Log; +import android.util.LruCache; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -181,6 +182,8 @@ public class FontsContract { @GuardedBy("mLock") private HandlerThread mThread; + private static final LruCache<String, Typeface> sTypefaceCache = new LruCache<>(16); + /** @hide */ public FontsContract(Context context) { mContext = context.getApplicationContext(); @@ -476,6 +479,11 @@ public class FontsContract { * therefore the result is delivered to the given callback. See {@link FontRequest}. * Only one of the methods in callback will be invoked, depending on whether the request * succeeds or fails. These calls will happen on the caller thread. + * + * Note that the result Typeface may be cached internally and the same instance will be returned + * the next time you call this method with the same request. If you want to bypass this cache, + * use {@link #fetchFonts} and {@link #buildTypeface} instead. + * * @param context A context to be used for fetching from font provider. * @param request A {@link FontRequest} object that identifies the provider and query for the * request. May not be null. @@ -486,8 +494,13 @@ public class FontsContract { @NonNull FontRequestCallback callback, @NonNull Handler handler) { final Handler callerThreadHandler = new Handler(); + final Typeface cachedTypeface = sTypefaceCache.get(request.getIdentifier()); + if (cachedTypeface != null) { + callerThreadHandler.post(() -> callback.onTypefaceRetrieved(cachedTypeface)); + return; + } + handler.post(() -> { - // TODO: Cache the result. FontFamilyResult result; try { result = fetchFonts(context, null /* cancellation signal */, request); @@ -497,6 +510,13 @@ public class FontsContract { return; } + // Same request might be dispatched during fetchFonts. Check the cache again. + final Typeface anotherCachedTypeface = sTypefaceCache.get(request.getIdentifier()); + if (anotherCachedTypeface != null) { + callerThreadHandler.post(() -> callback.onTypefaceRetrieved(anotherCachedTypeface)); + return; + } + if (result.getStatusCode() != FontFamilyResult.STATUS_OK) { switch (result.getStatusCode()) { case FontFamilyResult.STATUS_WRONG_CERTIFICATES: @@ -547,6 +567,7 @@ public class FontsContract { return; } + sTypefaceCache.put(request.getIdentifier(), typeface); callerThreadHandler.post(() -> callback.onTypefaceRetrieved(typeface)); }); } diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 5669189434e8..94a515b26466 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -1266,6 +1266,11 @@ <meta-data android:name="com.android.frameworks.coretests.reference" android:resource="@xml/metadata" /> </provider> + <provider android:name="android.provider.MockFontProvider" + android:authorities="android.provider.fonts.font" + android:exported="false" + android:multiprocess="true" /> + <!-- Application components used for content tests --> <provider android:name="android.content.MemoryFileProvider" android:authorities="android.content.MemoryFileProvider" diff --git a/core/tests/coretests/assets/fonts/samplefont1.ttf b/core/tests/coretests/assets/fonts/samplefont1.ttf Binary files differnew file mode 100644 index 000000000000..020436a2db72 --- /dev/null +++ b/core/tests/coretests/assets/fonts/samplefont1.ttf diff --git a/core/tests/coretests/assets/fonts/samplefont1.ttx b/core/tests/coretests/assets/fonts/samplefont1.ttx new file mode 100644 index 000000000000..40fa26805772 --- /dev/null +++ b/core/tests/coretests/assets/fonts/samplefont1.ttx @@ -0,0 +1,177 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0"> + + <GlyphOrder> + <GlyphID id="0" name=".notdef"/> + <GlyphID id="1" name="a"/> + </GlyphOrder> + + <head> + <tableVersion value="1.0"/> + <fontRevision value="1.0"/> + <checkSumAdjustment value="0x640cdb2f"/> + <magicNumber value="0x5f0f3cf5"/> + <flags value="00000000 00000011"/> + <unitsPerEm value="1000"/> + <created value="Fri Mar 17 07:26:00 2017"/> + <macStyle value="00000000 00000000"/> + <lowestRecPPEM value="7"/> + <fontDirectionHint value="2"/> + <glyphDataFormat value="0"/> + </head> + + <hhea> + <tableVersion value="1.0"/> + <ascent value="1000"/> + <descent value="-200"/> + <lineGap value="0"/> + <caretSlopeRise value="1"/> + <caretSlopeRun value="0"/> + <caretOffset value="0"/> + <reserved0 value="0"/> + <reserved1 value="0"/> + <reserved2 value="0"/> + <reserved3 value="0"/> + <metricDataFormat value="0"/> + </hhea> + + <maxp> + <tableVersion value="0x10000"/> + <maxZones value="0"/> + <maxTwilightPoints value="0"/> + <maxStorage value="0"/> + <maxFunctionDefs value="0"/> + <maxInstructionDefs value="0"/> + <maxStackElements value="0"/> + <maxSizeOfInstructions value="0"/> + <maxComponentElements value="0"/> + </maxp> + + <OS_2> + <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex' + will be recalculated by the compiler --> + <version value="3"/> + <xAvgCharWidth value="594"/> + <usWeightClass value="400"/> + <usWidthClass value="5"/> + <fsType value="00000000 00001000"/> + <ySubscriptXSize value="650"/> + <ySubscriptYSize value="600"/> + <ySubscriptXOffset value="0"/> + <ySubscriptYOffset value="75"/> + <ySuperscriptXSize value="650"/> + <ySuperscriptYSize value="600"/> + <ySuperscriptXOffset value="0"/> + <ySuperscriptYOffset value="350"/> + <yStrikeoutSize value="50"/> + <yStrikeoutPosition value="300"/> + <sFamilyClass value="0"/> + <panose> + <bFamilyType value="0"/> + <bSerifStyle value="0"/> + <bWeight value="5"/> + <bProportion value="0"/> + <bContrast value="0"/> + <bStrokeVariation value="0"/> + <bArmStyle value="0"/> + <bLetterForm value="0"/> + <bMidline value="0"/> + <bXHeight value="0"/> + </panose> + <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/> + <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/> + <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/> + <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/> + <achVendID value="UKWN"/> + <fsSelection value="00000000 01000000"/> + <usFirstCharIndex value="32"/> + <usLastCharIndex value="122"/> + <sTypoAscender value="800"/> + <sTypoDescender value="-200"/> + <sTypoLineGap value="200"/> + <usWinAscent value="1000"/> + <usWinDescent value="200"/> + <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/> + <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/> + <sxHeight value="500"/> + <sCapHeight value="700"/> + <usDefaultChar value="0"/> + <usBreakChar value="32"/> + <usMaxContext value="0"/> + </OS_2> + + <hmtx> + <mtx name=".notdef" width="500" lsb="93"/> + <mtx name="a" width="500" lsb="93"/> + </hmtx> + + <cmap> + <tableVersion version="0"/> + <cmap_format_4 platformID="3" platEncID="10" language="0"> + <map code="0x0061" name="a" /> + </cmap_format_4> + </cmap> + + <loca> + <!-- The 'loca' table will be calculated by the compiler --> + </loca> + + <glyf> + <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" /> + <TTGlyph name="a" xMin="0" yMin="0" xMax="0" yMax="0" /> + </glyf> + + <name> + <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True"> + Sample Font + </namerecord> + <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True"> + Regular + </namerecord> + <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True"> + Sample Font + </namerecord> + <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True"> + SampleFont-Regular + </namerecord> + <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409"> + Sample Font + </namerecord> + <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409"> + Regular + </namerecord> + <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409"> + Sample Font + </namerecord> + <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409"> + SampleFont-Regular + </namerecord> + </name> + + <post> + <formatType value="3.0"/> + <italicAngle value="0.0"/> + <underlinePosition value="-75"/> + <underlineThickness value="50"/> + <isFixedPitch value="0"/> + <minMemType42 value="0"/> + <maxMemType42 value="0"/> + <minMemType1 value="0"/> + <maxMemType1 value="0"/> + </post> + +</ttFont> diff --git a/core/tests/coretests/src/android/provider/FontsContractE2ETest.java b/core/tests/coretests/src/android/provider/FontsContractE2ETest.java new file mode 100644 index 000000000000..479f6dd0e495 --- /dev/null +++ b/core/tests/coretests/src/android/provider/FontsContractE2ETest.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.provider; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertNotSame; + +import android.app.Instrumentation; +import android.content.pm.Signature; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.PackageInfo; +import android.content.Context; +import android.graphics.Typeface; +import android.graphics.fonts.FontRequest; +import android.provider.FontsContract; +import android.provider.FontsContract.FontFamilyResult; +import android.provider.FontsContract.FontInfo; +import android.provider.FontsContract.Columns; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.os.Handler; +import java.util.List; +import java.util.ArrayList; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class FontsContractE2ETest { + private static final String AUTHORITY = "android.provider.fonts.font"; + private static final String PACKAGE = "com.android.frameworks.coretests"; + + // Signature to be used for authentication to access content provider. + // In this test case, the content provider and consumer live in the same package, self package's + // signature works. + private static List<List<byte[]>> SIGNATURE; + static { + final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + try { + PackageManager manager = context.getPackageManager(); + PackageInfo info = manager.getPackageInfo( + context.getPackageName(), PackageManager.GET_SIGNATURES); + ArrayList<byte[]> out = new ArrayList<>(); + for (Signature sig : info.signatures) { + out.add(sig.toByteArray()); + } + SIGNATURE = new ArrayList<>(); + SIGNATURE.add(out); + } catch (PackageManager.NameNotFoundException e) { + throw new RuntimeException(e); + } + } + + @Before + public void setUp() { + MockFontProvider.prepareFontFiles( + InstrumentationRegistry.getInstrumentation().getTargetContext()); + } + + @After + public void tearDown() { + MockFontProvider.cleanUpFontFiles( + InstrumentationRegistry.getInstrumentation().getTargetContext()); + } + + private static class TestCallback extends FontsContract.FontRequestCallback { + private Typeface mTypeface; + + private int mSuccessCallCount; + private int mFailedCallCount; + + public void onTypefaceRetrieved(Typeface typeface) { + mTypeface = typeface; + mSuccessCallCount++; + } + + public void onTypefaceRequestFailed(int reason) { + mFailedCallCount++; + } + + public Typeface getTypeface() { + return mTypeface; + } + + public int getSuccessCallCount() { + return mSuccessCallCount; + } + + public int getFailedCallCount() { + return mFailedCallCount; + } + } + + @Test + public void typefaceCacheTest() throws NameNotFoundException { + Instrumentation inst = InstrumentationRegistry.getInstrumentation(); + Context ctx = inst.getTargetContext(); + + final TestCallback callback = new TestCallback(); + inst.runOnMainSync(() -> { + FontRequest request = new FontRequest( + AUTHORITY, PACKAGE, "singleFontFamily", SIGNATURE); + FontsContract.requestFont(ctx, request, callback, new Handler()); + }); + inst.waitForIdleSync(); + assertEquals(1, callback.getSuccessCallCount()); + assertEquals(0, callback.getFailedCallCount()); + assertNotNull(callback.getTypeface()); + + final TestCallback callback2 = new TestCallback(); + inst.runOnMainSync(() -> { + FontRequest request = new FontRequest( + AUTHORITY, PACKAGE, "singleFontFamily", SIGNATURE); + FontsContract.requestFont(ctx, request, callback2, new Handler()); + }); + inst.waitForIdleSync(); + assertEquals(1, callback2.getSuccessCallCount()); + assertEquals(0, callback2.getFailedCallCount()); + assertSame(callback.getTypeface(), callback2.getTypeface()); + + final TestCallback callback3 = new TestCallback(); + inst.runOnMainSync(() -> { + FontRequest request = new FontRequest( + AUTHORITY, PACKAGE, "singleFontFamily2", SIGNATURE); + FontsContract.requestFont(ctx, request, callback3, new Handler()); + }); + inst.waitForIdleSync(); + assertEquals(1, callback3.getSuccessCallCount()); + assertEquals(0, callback3.getFailedCallCount()); + assertNotSame(callback.getTypeface(), callback3.getTypeface()); + } + + @Test + public void typefaceNotCacheTest() throws NameNotFoundException { + Instrumentation inst = InstrumentationRegistry.getInstrumentation(); + Context ctx = inst.getTargetContext(); + + FontRequest request = new FontRequest( + AUTHORITY, PACKAGE, "singleFontFamily", SIGNATURE); + FontFamilyResult result = FontsContract.fetchFonts( + ctx, null /* cancellation signal */, request); + assertEquals(FontFamilyResult.STATUS_OK, result.getStatusCode()); + Typeface typeface = FontsContract.buildTypeface( + ctx, null /* cancellation signal */, result.getFonts()); + + FontFamilyResult result2 = FontsContract.fetchFonts( + ctx, null /* cancellation signal */, request); + assertEquals(FontFamilyResult.STATUS_OK, result2.getStatusCode()); + Typeface typeface2 = FontsContract.buildTypeface( + ctx, null /* cancellation signal */, result2.getFonts()); + + // Neighter fetchFonts nor buildTypeface should cache the Typeface. + assertNotSame(typeface, typeface2); + } +} diff --git a/core/tests/coretests/src/android/provider/MockFontProvider.java b/core/tests/coretests/src/android/provider/MockFontProvider.java new file mode 100644 index 000000000000..339d5c3f7fa0 --- /dev/null +++ b/core/tests/coretests/src/android/provider/MockFontProvider.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.provider; + +import static android.provider.FontsContract.Columns; + +import android.content.ContentProvider; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.content.res.AssetManager; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.graphics.fonts.FontVariationAxis; +import android.net.Uri; +import android.os.CancellationSignal; +import android.os.ParcelFileDescriptor; +import android.util.SparseArray; + +import java.util.Collections; +import java.util.Map; +import java.util.HashMap; +import java.io.File; +import java.nio.file.Files; +import java.io.IOException; +import java.io.InputStream; +import java.io.FileNotFoundException; +import java.nio.file.StandardCopyOption; + +public class MockFontProvider extends ContentProvider { + final static String AUTHORITY = "android.provider.fonts.font"; + + final static String[] FONT_FILES = { + "samplefont1.ttf", + }; + private static final int SAMPLE_FONT_FILE_0_ID = 0; + private static final int SAMPLE_FONT_FILE_1_ID = 1; + + static class Font { + public Font(int id, int fileId, int ttcIndex, String varSettings, int weight, int italic, + int resultCode) { + mId = id; + mFileId = fileId; + mTtcIndex = ttcIndex; + mVarSettings = varSettings; + mWeight = weight; + mItalic = italic; + mResultCode = resultCode; + } + + public int getId() { + return mId; + } + + public int getTtcIndex() { + return mTtcIndex; + } + + public String getVarSettings() { + return mVarSettings; + } + + public int getWeight() { + return mWeight; + } + + public int getItalic() { + return mItalic; + } + + public int getResultCode() { + return mResultCode; + } + + public int getFileId() { + return mFileId; + } + + private int mId; + private int mFileId; + private int mTtcIndex; + private String mVarSettings; + private int mWeight; + private int mItalic; + private int mResultCode; + }; + + private static Map<String, Font[]> QUERY_MAP; + static { + HashMap<String, Font[]> map = new HashMap<>(); + int id = 0; + + map.put("singleFontFamily", new Font[] { + new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 400, 0, Columns.RESULT_CODE_OK), + }); + + map.put("singleFontFamily2", new Font[] { + new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, Columns.RESULT_CODE_OK), + }); + + QUERY_MAP = Collections.unmodifiableMap(map); + } + + private static Cursor buildCursor(Font[] in) { + MatrixCursor cursor = new MatrixCursor(new String[] { + Columns._ID, Columns.TTC_INDEX, Columns.VARIATION_SETTINGS, Columns.WEIGHT, + Columns.ITALIC, Columns.RESULT_CODE, Columns.FILE_ID}); + for (Font font : in) { + MatrixCursor.RowBuilder builder = cursor.newRow(); + builder.add(Columns._ID, font.getId()); + builder.add(Columns.FILE_ID, font.getFileId()); + builder.add(Columns.TTC_INDEX, font.getTtcIndex()); + builder.add(Columns.VARIATION_SETTINGS, font.getVarSettings()); + builder.add(Columns.WEIGHT, font.getWeight()); + builder.add(Columns.ITALIC, font.getItalic()); + builder.add(Columns.RESULT_CODE, font.getResultCode()); + } + return cursor; + } + + public MockFontProvider() { + } + + public static void prepareFontFiles(Context context) { + final AssetManager mgr = context.getAssets(); + for (String file : FONT_FILES) { + try (InputStream is = mgr.open("fonts/" + file)) { + Files.copy(is, getCopiedFile(context, file).toPath(), + StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + public static void cleanUpFontFiles(Context context) { + for (String file : FONT_FILES) { + getCopiedFile(context, file).delete(); + } + } + + public static File getCopiedFile(Context context, String path) { + return new File(context.getFilesDir(), path); + } + + @Override + public ParcelFileDescriptor openFile(Uri uri, String mode) { + final int id = (int)ContentUris.parseId(uri); + final File targetFile = getCopiedFile(getContext(), FONT_FILES[id]); + try { + return ParcelFileDescriptor.open(targetFile, ParcelFileDescriptor.MODE_READ_ONLY); + } catch (FileNotFoundException e) { + throw new RuntimeException( + "Failed to found font file. You might forget call prepareFontFiles in setUp"); + } + } + + @Override + public boolean onCreate() { + return true; + } + + @Override + public String getType(Uri uri) { + return "vnd.android.cursor.dir/vnd.android.provider.font"; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return buildCursor(QUERY_MAP.get(selectionArgs[0])); + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + throw new UnsupportedOperationException("insert is not supported."); + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + throw new UnsupportedOperationException("delete is not supported."); + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + throw new UnsupportedOperationException("update is not supported."); + } +} diff --git a/graphics/java/android/graphics/fonts/FontRequest.java b/graphics/java/android/graphics/fonts/FontRequest.java index c7a583056b76..df3951c80b8e 100644 --- a/graphics/java/android/graphics/fonts/FontRequest.java +++ b/graphics/java/android/graphics/fonts/FontRequest.java @@ -35,6 +35,9 @@ public final class FontRequest implements Parcelable { private final String mQuery; private final List<List<byte[]>> mCertificates; + // Used for key of the cache. + private final String mIdentifier; + /** * @param providerAuthority The authority of the Font Provider to be used for the request. This * should be a system installed app. @@ -49,6 +52,8 @@ public final class FontRequest implements Parcelable { mQuery = Preconditions.checkNotNull(query); mProviderPackage = Preconditions.checkNotNull(providerPackage); mCertificates = Collections.emptyList(); + mIdentifier = new StringBuilder(mProviderAuthority).append("-").append(mProviderPackage) + .append("-").append(mQuery).toString(); } /** @@ -68,6 +73,8 @@ public final class FontRequest implements Parcelable { mProviderPackage = Preconditions.checkNotNull(providerPackage); mQuery = Preconditions.checkNotNull(query); mCertificates = Preconditions.checkNotNull(certificates); + mIdentifier = new StringBuilder(mProviderAuthority).append("-").append(mProviderPackage) + .append("-").append(mQuery).toString(); } /** @@ -102,6 +109,11 @@ public final class FontRequest implements Parcelable { return mCertificates; } + /** @hide */ + public String getIdentifier() { + return mIdentifier; + } + @Override public int describeContents() { return 0; @@ -121,6 +133,8 @@ public final class FontRequest implements Parcelable { mQuery = in.readString(); mCertificates = new ArrayList<>(); in.readList(mCertificates, null); + mIdentifier = new StringBuilder(mProviderAuthority).append("-").append(mProviderPackage) + .append("-").append(mQuery).toString(); } public static final Parcelable.Creator<FontRequest> CREATOR = |