blob: 1949745518f49c728dc648bbe2b5312473f0edcc [file] [log] [blame]
package org.robolectric.shadows;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static android.os.Build.VERSION_CODES.P;
import static org.robolectric.RuntimeEnvironment.getApiLevel;
import static org.robolectric.Shadows.shadowOf;
import android.content.res.AssetManager;
import android.graphics.FontFamily;
import android.graphics.Typeface;
import android.util.ArrayMap;
import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.robolectric.annotation.HiddenApi;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.annotation.Resetter;
import org.robolectric.res.FsFile;
import org.robolectric.util.ReflectionHelpers;
import org.robolectric.util.ReflectionHelpers.ClassParameter;
@Implements(value = Typeface.class, looseSignatures = true)
public class ShadowTypeface {
private static Map<Long, FontDesc> FONTS = new HashMap<>();
private static long nextFontId = 1;
private FontDesc description;
@RealObject private Typeface realTypeface;
@HiddenApi
@Implementation
public void __constructor__(int fontId) {
description = findById((long) fontId);
}
@HiddenApi
@Implementation
public void __constructor__(long fontId) {
description = findById(fontId);
}
@Implementation
public static Typeface create(String familyName, int style) {
return createUnderlyingTypeface(familyName, style);
}
@Implementation
public static Typeface create(Typeface family, int style) {
if (family == null) {
return createUnderlyingTypeface(null, style);
} else {
return createUnderlyingTypeface(shadowOf(family).getFontDescription().getFamilyName(), style);
}
}
@Implementation
public static Typeface createFromAsset(AssetManager mgr, String path) {
Collection<FsFile> assetDirs = shadowOf(mgr).getAllAssetsDirectories();
for (FsFile assetDir : assetDirs) {
// check if in zip file too?
FsFile[] files = assetDir.listFiles(new StartsWith(path));
FsFile assetFile = assetDir.join(path);
if (assetFile.exists() || files.length != 0) {
return createUnderlyingTypeface(path, Typeface.NORMAL);
}
}
throw new RuntimeException("Font not found at " + assetDirs);
}
@Implementation
public static Typeface createFromFile(File path) {
String familyName = path.toPath().getFileName().toString();
return createUnderlyingTypeface(familyName, Typeface.NORMAL);
}
@Implementation
public static Typeface createFromFile(String path) {
return createFromFile(new File(path));
}
@Implementation
public int getStyle() {
return description.getStyle();
}
@HiddenApi
@Implementation(minSdk = LOLLIPOP)
public static Typeface createFromFamilies(Object /*FontFamily[]*/ families) {
return null;
}
@HiddenApi
@Implementation(minSdk = LOLLIPOP)
public static Typeface createFromFamiliesWithDefault(Object /*FontFamily[]*/ families) {
return null;
}
@Implementation(minSdk = P)
protected static void buildSystemFallback(String xmlPath, String fontDir,
ArrayMap<String, Typeface> fontMap, ArrayMap<String, FontFamily[]> fallbackMap) {
fontMap.put("sans-serif", createUnderlyingTypeface("sans-serif", 0));
}
@Resetter
synchronized public static void reset() {
FONTS.clear();
}
private static Typeface createUnderlyingTypeface(String familyName, int style) {
long thisFontId = nextFontId++;
FONTS.put(thisFontId, new FontDesc(familyName, style));
if (getApiLevel() >= LOLLIPOP) {
return ReflectionHelpers.callConstructor(Typeface.class, ClassParameter.from(long.class, thisFontId));
} else {
return ReflectionHelpers.callConstructor(Typeface.class, ClassParameter.from(int.class, (int) thisFontId));
}
}
private synchronized static FontDesc findById(long fontId) {
if (FONTS.containsKey(fontId)) {
return FONTS.get(fontId);
}
throw new RuntimeException("Unknown font id: " + fontId);
}
/**
* Returns the font description.
*
* @return Font description.
*/
public FontDesc getFontDescription() {
return description;
}
public static class FontDesc {
public final String familyName;
public final int style;
public FontDesc(String familyName, int style) {
this.familyName = familyName;
this.style = style;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FontDesc fontDesc = (FontDesc) o;
if (style != fontDesc.style) return false;
if (familyName != null ? !familyName.equals(fontDesc.familyName) : fontDesc.familyName != null)
return false;
return true;
}
@Override
public int hashCode() {
int result = familyName != null ? familyName.hashCode() : 0;
result = 31 * result + style;
return result;
}
public String getFamilyName() {
return familyName;
}
public int getStyle() {
return style;
}
}
private static class StartsWith implements FsFile.Filter {
private final String contains;
public StartsWith(String contains) {
this.contains = contains;
}
@Override
public boolean accept(FsFile file) {
return file.getName().startsWith(contains);
}
}
}