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 17package android.support.v4.graphics; 18 19import static org.junit.Assert.assertEquals; 20import static org.junit.Assert.assertNotNull; 21 22import android.annotation.SuppressLint; 23import android.app.Instrumentation; 24import android.content.Context; 25import android.content.pm.PackageInfo; 26import android.content.pm.PackageManager; 27import android.content.pm.Signature; 28import android.content.res.Resources; 29import android.graphics.Paint; 30import android.graphics.Typeface; 31import android.support.compat.test.R; 32import android.support.test.InstrumentationRegistry; 33import android.support.test.filters.SmallTest; 34import android.support.testutils.PollingCheck; 35import android.support.v4.content.res.FontResourcesParserCompat; 36import android.support.v4.content.res.FontResourcesParserCompat.FamilyResourceEntry; 37import android.support.v4.content.res.FontResourcesParserCompat.ProviderResourceEntry; 38import android.support.v4.provider.FontRequest; 39import android.support.v4.provider.MockFontProvider; 40import android.widget.TextView; 41 42import org.junit.After; 43import org.junit.Before; 44import org.junit.Test; 45import org.xmlpull.v1.XmlPullParserException; 46 47import java.io.IOException; 48import java.util.ArrayList; 49import java.util.List; 50 51@SmallTest 52public class TypefaceCompatTest { 53 54 public Context mContext; 55 public Resources mResources; 56 57 @Before 58 public void setUp() { 59 mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 60 mResources = mContext.getResources(); 61 MockFontProvider.prepareFontFiles(mContext); 62 } 63 64 @After 65 public void tearDown() { 66 MockFontProvider.cleanUpFontFiles(mContext); 67 } 68 69 // Signature to be used for authentication to access content provider. 70 // In this test case, the content provider and consumer live in the same package, self package's 71 // signature works. 72 private static final List<List<byte[]>> SIGNATURE; 73 static { 74 final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); 75 try { 76 PackageManager manager = context.getPackageManager(); 77 PackageInfo info = manager.getPackageInfo( 78 context.getPackageName(), PackageManager.GET_SIGNATURES); 79 ArrayList<byte[]> out = new ArrayList<>(); 80 for (Signature sig : info.signatures) { 81 out.add(sig.toByteArray()); 82 } 83 SIGNATURE = new ArrayList<>(); 84 SIGNATURE.add(out); 85 } catch (PackageManager.NameNotFoundException e) { 86 throw new RuntimeException(e); 87 } 88 } 89 90 /** 91 * Helper method to get the used font resource id by typeface. 92 * 93 * If the typeface is created from one of the R.font.large_a, R.font.large_b, R.font.large_c or 94 * R.font.large_d resource, this method returns the resource id used by the typeface. 95 */ 96 private static int getSelectedFontResourceId(Typeface typeface) { 97 // The glyph for "a" in R.font.large_a font has a 3em width and glyph for "b", "c" and "d" 98 // have 1em width. Similarly, The glyph for "b" in R.font.large_b font, the glyph for "c" 99 // in R.font.large_c font, the glyph for "d" in R.font.large_d font has 3em width and the 100 // glyph for the rest characters have 1em. Thus we can get the resource id of the source 101 // font file by comparing width of "a", "b", "c" and "d". 102 Paint p = new Paint(); 103 p.setTypeface(typeface); 104 final int[] ids = { R.font.large_a, R.font.large_b, R.font.large_c, R.font.large_d }; 105 final float[] widths = { 106 p.measureText("a"), p.measureText("b"), p.measureText("c"), p.measureText("d") 107 }; 108 109 int maxIndex = Integer.MIN_VALUE; 110 float maxValue = Float.MIN_VALUE; 111 for (int i = 0; i < widths.length; ++i) { 112 if (maxValue < widths[i]) { 113 maxIndex = i; 114 maxValue = widths[i]; 115 } 116 } 117 return ids[maxIndex]; 118 } 119 120 /** 121 * Helper method to obtain ProviderResourceEntry with overwriting correct signatures. 122 */ 123 private ProviderResourceEntry getProviderResourceEntry(int id) { 124 final ProviderResourceEntry entry; 125 try { 126 entry = (ProviderResourceEntry) FontResourcesParserCompat.parse( 127 mResources.getXml(id), mResources); 128 } catch (XmlPullParserException | IOException e) { 129 throw new RuntimeException(e); 130 } 131 final FontRequest parsedRequest = entry.getRequest(); 132 final FontRequest request = new FontRequest(parsedRequest.getProviderAuthority(), 133 parsedRequest.getProviderPackage(), parsedRequest.getQuery(), SIGNATURE); 134 return new ProviderResourceEntry(request, entry.getFetchStrategy(), entry.getTimeout()); 135 } 136 137 @Test 138 public void testCreateFromResourcesFamilyXml_resourceFont_syncloading() throws Exception { 139 Typeface typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext, 140 getProviderResourceEntry(R.font.styletest_sync_providerfont), mResources, 141 R.font.styletest_sync_providerfont, Typeface.NORMAL, null /* TextView */); 142 typeface = Typeface.create(typeface, Typeface.NORMAL); 143 assertEquals(R.font.large_a, getSelectedFontResourceId(typeface)); 144 145 typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext, 146 getProviderResourceEntry(R.font.styletest_sync_providerfont), mResources, 147 R.font.styletest_sync_providerfont, Typeface.ITALIC, null /* TextView */); 148 typeface = Typeface.create(typeface, Typeface.ITALIC); 149 assertEquals(R.font.large_b, getSelectedFontResourceId(typeface)); 150 151 typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext, 152 getProviderResourceEntry(R.font.styletest_sync_providerfont), mResources, 153 R.font.styletest_sync_providerfont, Typeface.BOLD, null /* TextView */); 154 typeface = Typeface.create(typeface, Typeface.BOLD); 155 assertEquals(R.font.large_c, getSelectedFontResourceId(typeface)); 156 157 typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext, 158 getProviderResourceEntry(R.font.styletest_sync_providerfont), mResources, 159 R.font.styletest_sync_providerfont, Typeface.BOLD_ITALIC, null /* TextView */); 160 typeface = Typeface.create(typeface, Typeface.BOLD_ITALIC); 161 assertEquals(R.font.large_d, getSelectedFontResourceId(typeface)); 162 } 163 164 @Test 165 public void testCreateFromResourcesFamilyXml_resourceFont_asyncloading() throws Exception { 166 Instrumentation inst = InstrumentationRegistry.getInstrumentation(); 167 final TextView textView = new TextView(mContext); 168 PollingCheck.PollingCheckCondition condition = new PollingCheck.PollingCheckCondition() { 169 @Override 170 public boolean canProceed() { 171 return textView.getTypeface() != null; 172 } 173 }; 174 175 textView.setTypeface(null); 176 inst.runOnMainSync(new Runnable() { 177 @Override 178 public void run() { 179 TypefaceCompat.createFromResourcesFamilyXml(mContext, 180 getProviderResourceEntry(R.font.styletest_async_providerfont), mResources, 181 R.font.styletest_async_providerfont, Typeface.NORMAL, textView); 182 } 183 }); 184 PollingCheck.waitFor(condition); 185 assertEquals(R.font.large_a, getSelectedFontResourceId(textView.getTypeface())); 186 187 textView.setTypeface(null); 188 inst.runOnMainSync(new Runnable() { 189 @Override 190 public void run() { 191 TypefaceCompat.createFromResourcesFamilyXml(mContext, 192 getProviderResourceEntry(R.font.styletest_async_providerfont), mResources, 193 R.font.styletest_async_providerfont, Typeface.ITALIC, textView); 194 } 195 }); 196 PollingCheck.waitFor(condition); 197 assertEquals(R.font.large_b, getSelectedFontResourceId(textView.getTypeface())); 198 199 textView.setTypeface(null); 200 inst.runOnMainSync(new Runnable() { 201 @Override 202 public void run() { 203 TypefaceCompat.createFromResourcesFamilyXml(mContext, 204 getProviderResourceEntry(R.font.styletest_async_providerfont), mResources, 205 R.font.styletest_async_providerfont, Typeface.BOLD, textView); 206 } 207 }); 208 PollingCheck.waitFor(condition); 209 assertEquals(R.font.large_c, getSelectedFontResourceId(textView.getTypeface())); 210 211 textView.setTypeface(null); 212 inst.runOnMainSync(new Runnable() { 213 @Override 214 public void run() { 215 TypefaceCompat.createFromResourcesFamilyXml(mContext, 216 getProviderResourceEntry(R.font.styletest_async_providerfont), mResources, 217 R.font.styletest_async_providerfont, Typeface.BOLD_ITALIC, textView); 218 } 219 }); 220 PollingCheck.waitFor(condition); 221 assertEquals(R.font.large_d, getSelectedFontResourceId(textView.getTypeface())); 222 } 223 224 @Test 225 public void testCreateFromResourcesFamilyXml_resourceFont() throws Exception { 226 @SuppressLint("ResourceType") 227 // We are retrieving the XML font as an XML resource for testing purposes. 228 final FamilyResourceEntry entry = FontResourcesParserCompat.parse( 229 mResources.getXml(R.font.styletestfont), mResources); 230 Typeface typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext, entry, mResources, 231 R.font.styletestfont, Typeface.NORMAL, null /* text view */); 232 assertEquals(typeface, TypefaceCompat.findFromCache( 233 mResources, R.font.styletestfont, Typeface.NORMAL)); 234 typeface = Typeface.create(typeface, Typeface.NORMAL); 235 // styletestfont has a node of fontStyle="normal" fontWeight="400" font="@font/large_a". 236 assertEquals(R.font.large_a, getSelectedFontResourceId(typeface)); 237 238 typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext, entry, mResources, 239 R.font.styletestfont, Typeface.ITALIC, null); 240 assertEquals(typeface, TypefaceCompat.findFromCache( 241 mResources, R.font.styletestfont, Typeface.ITALIC)); 242 typeface = Typeface.create(typeface, Typeface.ITALIC); 243 // styletestfont has a node of fontStyle="italic" fontWeight="400" font="@font/large_b". 244 assertEquals(R.font.large_b, getSelectedFontResourceId(typeface)); 245 246 typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext, entry, mResources, 247 R.font.styletestfont, Typeface.BOLD, null); 248 assertEquals(typeface, TypefaceCompat.findFromCache( 249 mResources, R.font.styletestfont, Typeface.BOLD)); 250 typeface = Typeface.create(typeface, Typeface.BOLD); 251 // styletestfont has a node of fontStyle="normal" fontWeight="700" font="@font/large_c". 252 assertEquals(R.font.large_c, getSelectedFontResourceId(typeface)); 253 254 typeface = TypefaceCompat.createFromResourcesFamilyXml(mContext, entry, mResources, 255 R.font.styletestfont, Typeface.BOLD_ITALIC, null); 256 assertEquals(typeface, TypefaceCompat.findFromCache( 257 mResources, R.font.styletestfont, Typeface.BOLD_ITALIC)); 258 typeface = Typeface.create(typeface, Typeface.BOLD_ITALIC); 259 // styletestfont has a node of fontStyle="italic" fontWeight="700" font="@font/large_d". 260 assertEquals(R.font.large_d, getSelectedFontResourceId(typeface)); 261 } 262 263 @Test 264 public void testCreateFromResourcesFontFile() { 265 Typeface typeface = TypefaceCompat.createFromResourcesFontFile(mContext, mResources, 266 R.font.large_a, "res/font/large_a.ttf", Typeface.NORMAL); 267 assertNotNull(typeface); 268 assertEquals(typeface, TypefaceCompat.findFromCache( 269 mResources, R.font.large_a, Typeface.NORMAL)); 270 assertEquals(R.font.large_a, getSelectedFontResourceId(typeface)); 271 272 typeface = TypefaceCompat.createFromResourcesFontFile(mContext, mResources, R.font.large_b, 273 "res/font/large_b.ttf", Typeface.NORMAL); 274 assertNotNull(typeface); 275 assertEquals(typeface, TypefaceCompat.findFromCache( 276 mResources, R.font.large_b, Typeface.NORMAL)); 277 assertEquals(R.font.large_b, getSelectedFontResourceId(typeface)); 278 } 279} 280