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