Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.support.v4.graphics;
     18 
     19 import static org.junit.Assert.assertEquals;
     20 import static org.junit.Assert.assertNotNull;
     21 
     22 import android.app.Instrumentation;
     23 import android.content.Context;
     24 import android.content.pm.PackageInfo;
     25 import android.content.pm.PackageManager;
     26 import android.content.pm.Signature;
     27 import android.content.res.Resources;
     28 import android.graphics.Paint;
     29 import android.graphics.Typeface;
     30 import android.support.compat.test.R;
     31 import android.support.test.InstrumentationRegistry;
     32 import android.support.test.filters.SdkSuppress;
     33 import android.support.test.filters.SmallTest;
     34 import android.support.testutils.PollingCheck;
     35 import android.support.v4.content.res.FontResourcesParserCompat;
     36 import android.support.v4.content.res.FontResourcesParserCompat.FamilyResourceEntry;
     37 import android.support.v4.content.res.FontResourcesParserCompat.ProviderResourceEntry;
     38 import android.support.v4.provider.FontRequest;
     39 import android.support.v4.provider.MockFontProvider;
     40 import android.widget.TextView;
     41 
     42 import org.junit.After;
     43 import org.junit.Before;
     44 import org.junit.Test;
     45 import org.xmlpull.v1.XmlPullParserException;
     46 
     47 import java.io.IOException;
     48 import java.util.ArrayList;
     49 import java.util.List;
     50 
     51 @SdkSuppress(maxSdkVersion = 25)  // on API 26, use platform implementation.
     52 @SmallTest
     53 public class TypefaceCompatTest {
     54     private static final String AUTHORITY = "android.provider.fonts.font";
     55     private static final String PACKAGE = "android.support.compat.test";
     56 
     57     public Context mContext;
     58     public Resources mResources;
     59 
     60     @Before
     61     public void setUp() {
     62         mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
     63         mResources = mContext.getResources();
     64         MockFontProvider.prepareFontFiles(mContext);
     65     }
     66 
     67     @After
     68     public void tearDown() {
     69         MockFontProvider.cleanUpFontFiles(mContext);
     70     }
     71 
     72     // Signature to be used for authentication to access content provider.
     73     // In this test case, the content provider and consumer live in the same package, self package's
     74     // signature works.
     75     private static final List<List<byte[]>> SIGNATURE;
     76     static {
     77         final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
     78         try {
     79             PackageManager manager = context.getPackageManager();
     80             PackageInfo info = manager.getPackageInfo(
     81                     context.getPackageName(), PackageManager.GET_SIGNATURES);
     82             ArrayList<byte[]> out = new ArrayList<>();
     83             for (Signature sig : info.signatures) {
     84                 out.add(sig.toByteArray());
     85             }
     86             SIGNATURE = new ArrayList<>();
     87             SIGNATURE.add(out);
     88         } catch (PackageManager.NameNotFoundException e) {
     89             throw new RuntimeException(e);
     90         }
     91     }
     92 
     93     /**
     94      * Helper method to get the used font resource id by typeface.
     95      *
     96      * If the typeface is created from one of the R.font.large_a, R.font.large_b, R.font.large_c or
     97      * R.font.large_d resource, this method returns the resource id used by the typeface.
     98      */
     99     private static int getSelectedFontResourceId(Typeface typeface) {
    100         // The glyph for "a" in R.font.large_a font has a 3em width and glyph for "b", "c" and "d"
    101         // have 1em width. Similarly, The glyph for "b" in R.font.large_b font, the glyph for "c"
    102         // in R.font.large_c font, the glyph for "d" in R.font.large_d font has 3em width and the
    103         // glyph for the rest characters have 1em. Thus we can get the resource id of the source
    104         // font file by comparing width of "a", "b", "c" and "d".
    105         Paint p = new Paint();
    106         p.setTypeface(typeface);
    107         final int[] ids = { R.font.large_a, R.font.large_b, R.font.large_c, R.font.large_d };
    108         final float[] widths = {
    109             p.measureText("a"), p.measureText("b"), p.measureText("c"), p.measureText("d")
    110         };
    111 
    112         int maxIndex = Integer.MIN_VALUE;
    113         float maxValue = Float.MIN_VALUE;
    114         for (int i = 0; i < widths.length; ++i) {
    115             if (maxValue < widths[i]) {
    116                 maxIndex = i;
    117                 maxValue = widths[i];
    118             }
    119         }
    120         return ids[maxIndex];
    121     }
    122 
    123     /**
    124      * Helper method to obtain ProviderResourceEntry with overwriting correct signatures.
    125      */
    126     private ProviderResourceEntry getProviderResourceEntry(int id) {
    127         final ProviderResourceEntry entry;
    128         try {
    129             entry = (ProviderResourceEntry) FontResourcesParserCompat.parse(
    130                     mResources.getXml(id), mResources);
    131         } catch (XmlPullParserException | IOException e) {
    132             throw new RuntimeException(e);
    133         }
    134         final FontRequest parsedRequest = entry.getRequest();
    135         final FontRequest request = new FontRequest(parsedRequest.getProviderAuthority(),
    136                 parsedRequest.getProviderPackage(), parsedRequest.getQuery(), SIGNATURE);
    137         return new ProviderResourceEntry(request, entry.getFetchStrategy(), entry.getTimeout());
    138     }
    139 
    140     @Test
    141     public void testCreateFromResourcesFamilyXml_resourceFont_syncloading() throws Exception {
    142         Typeface typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext,
    143                 getProviderResourceEntry(R.font.styletest_sync_providerfont), mResources,
    144                 R.font.styletest_sync_providerfont, Typeface.NORMAL, null /* TextView */);
    145         // TODO: Add support of styled font selection from family result.
    146         assertNotNull(typeface);
    147     }
    148 
    149     @Test
    150     public void testCreateFromResourcesFamilyXml_resourceFont_asyncloading() throws Exception {
    151         Instrumentation inst = InstrumentationRegistry.getInstrumentation();
    152         final TextView textView = new TextView(mContext);
    153         inst.runOnMainSync(new Runnable() {
    154             @Override
    155             public void run() {
    156                 TypefaceCompat.createFromResourcesFamilyXml(mContext,
    157                         getProviderResourceEntry(R.font.styletest_async_providerfont), mResources,
    158                         R.font.styletest_sync_providerfont, Typeface.NORMAL, textView);
    159             }
    160         });
    161         PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
    162             @Override
    163             public boolean canProceed() {
    164                 return textView.getTypeface() != null;
    165             }
    166         });
    167         // TODO: Add support of styled font selection from family result.
    168         assertNotNull(textView.getTypeface());
    169     }
    170 
    171     @Test
    172     public void testCreateFromResourcesFamilyXml_resourceFont() throws Exception {
    173         final FamilyResourceEntry entry = FontResourcesParserCompat.parse(
    174                 mResources.getXml(R.font.styletestfont), mResources);
    175         Typeface typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext, entry, mResources,
    176                 R.font.styletestfont, Typeface.NORMAL, null /* text view */);
    177         assertEquals(typeface, TypefaceCompat.findFromCache(
    178                 mResources, R.font.styletestfont, Typeface.NORMAL));
    179         typeface = Typeface.create(typeface, Typeface.NORMAL);
    180         // styletestfont has a node of fontStyle="normal" fontWeight="400" font="@font/large_a".
    181         assertEquals(R.font.large_a, getSelectedFontResourceId(typeface));
    182 
    183         typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext, entry, mResources,
    184                 R.font.styletestfont, Typeface.ITALIC, null);
    185         assertEquals(typeface, TypefaceCompat.findFromCache(
    186                 mResources, R.font.styletestfont, Typeface.ITALIC));
    187         typeface = Typeface.create(typeface, Typeface.ITALIC);
    188         // styletestfont has a node of fontStyle="italic" fontWeight="400" font="@font/large_b".
    189         assertEquals(R.font.large_b, getSelectedFontResourceId(typeface));
    190 
    191         typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext, entry, mResources,
    192                 R.font.styletestfont, Typeface.BOLD, null);
    193         assertEquals(typeface, TypefaceCompat.findFromCache(
    194                 mResources, R.font.styletestfont, Typeface.BOLD));
    195         typeface = Typeface.create(typeface, Typeface.BOLD);
    196         // styletestfont has a node of fontStyle="normal" fontWeight="700" font="@font/large_c".
    197         assertEquals(R.font.large_c, getSelectedFontResourceId(typeface));
    198 
    199         typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext, entry, mResources,
    200                 R.font.styletestfont, Typeface.BOLD_ITALIC, null);
    201         assertEquals(typeface, TypefaceCompat.findFromCache(
    202                 mResources, R.font.styletestfont, Typeface.BOLD_ITALIC));
    203         typeface = Typeface.create(typeface, Typeface.BOLD_ITALIC);
    204         // styletestfont has a node of fontStyle="italic" fontWeight="700" font="@font/large_d".
    205         assertEquals(R.font.large_d, getSelectedFontResourceId(typeface));
    206     }
    207 
    208     @Test
    209     public void testCreateFromResourcesFontFile() {
    210         Typeface typeface = TypefaceCompat.createFromResourcesFontFile(
    211                 mContext, mResources, R.font.large_a, Typeface.NORMAL);
    212         assertEquals(typeface, TypefaceCompat.findFromCache(
    213                 mResources, R.font.large_a, Typeface.NORMAL));
    214         assertEquals(R.font.large_a, getSelectedFontResourceId(typeface));
    215 
    216         typeface = TypefaceCompat.createFromResourcesFontFile(
    217                 mContext, mResources, R.font.large_b, Typeface.NORMAL);
    218         assertEquals(typeface, TypefaceCompat.findFromCache(
    219                 mResources, R.font.large_b, Typeface.NORMAL));
    220         assertEquals(R.font.large_b, getSelectedFontResourceId(typeface));
    221     }
    222 }
    223