diff options
Diffstat (limited to 'graphics')
9 files changed, 972 insertions, 25 deletions
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 3e4d93b..ba2a890 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -96,6 +96,7 @@ public final class Bitmap implements Parcelable { if (sDefaultDensity >= 0) { return sDefaultDensity; } + //noinspection deprecation sDefaultDensity = DisplayMetrics.DENSITY_DEVICE; return sDefaultDensity; } diff --git a/graphics/java/android/graphics/FontListConverter.java b/graphics/java/android/graphics/FontListConverter.java new file mode 100644 index 0000000..c675c88 --- /dev/null +++ b/graphics/java/android/graphics/FontListConverter.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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.graphics; + +import android.graphics.FontListParser; +import android.graphics.FontListParser.Alias; +import android.graphics.FontListParser.Font; +import android.graphics.LegacyFontListParser.Family; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; + +/** + * Converts a list of Family to FontListParser.Config + * {@hide} + */ +public class FontListConverter { + // These array values were determined by the order + // of fonts in a fileset. The order is: + // "Normal, Bold, Italic, BoldItalic" + // Additionally the weight values in L's fonts.xml + // are used to determine a generic weight value for each type + // e.g The 2nd entry in a fileset is the bold font. + protected static final int[] WEIGHTS = { 400, 700, 400, 700 }; + protected static boolean[] ITALICS = { false, false, true, true }; + protected static final int DEFAULT_WEIGHT = WEIGHTS[0]; + + // Arbitrarily chosen list based on L's fonts.xml. + // There could be more out there, but we can't use a generic pattern of "fontName-styleName" + // as "sans-serif" would be translated as a font called "sans" with the style "serif". + public static final String[] STYLES = { + "thin", + "light", + "medium", + "black" + }; + + // Maps a "normal" family to a list of similar families differing by style + // Example: "sans-serif" -> { sans-serif-thin, sans-serif-light, sans-serif-medium } + private HashMap<Family, List<Family>> mFamilyVariants = new HashMap<Family, List<Family>>(); + private List<Family> mFamilies = + new ArrayList<Family >(); + private String mFontDir; + + + public FontListConverter(List<Family> families, String fontDir) { + mFamilies.addAll(families); + mFontDir = fontDir; + findFamilyVariants(); + } + + public FontListConverter(Family family, String fontDir) { + mFamilies.add(family); + mFontDir = fontDir; + findFamilyVariants(); + } + + private void findFamilyVariants() { + for(Family family : mFamilies) { + if (isNormalStyle(family)) { + List<Family> variants = findVariants(family, mFamilies); + mFamilyVariants.put(family, variants); + } + } + } + + private List<Family> findVariants(Family normalFamily, List<Family> legacyFamilies) { + List<Family> variants = new ArrayList<Family>(); + + String normalFamilyName = normalFamily.getName(); + + for(Family family : legacyFamilies) { + String name = family.getName(); + + if (name.startsWith(normalFamilyName) && !isNormalStyle(family)) { + variants.add(family); + } + } + return variants; + } + + public FontListParser.Config convert() { + FontListParser.Config config = new FontListParser.Config(); + config.families.addAll(convertFamilies()); + config.aliases.addAll(createAliases()); + return config; + } + + /** + * A "normal" style is just standard font, + * eg Roboto is normal. Roboto-Thin is styled. + */ + protected boolean isNormalStyle(Family family) { + String name = family.getName(); + if (name == null) return false; + + for(String style : STYLES) { + if (name.endsWith('-' + style)) { + return false; + } + } + return true; + } + + protected List<FontListParser.Family> convertFamilies() { + List<FontListParser.Family> convertedFamilies = new ArrayList<FontListParser.Family>(); + + // Only convert normal families. Each normal family will add in its variants + for(Family family : mFamilyVariants.keySet()) { + convertedFamilies.add(convertFamily(family)); + } + + return convertedFamilies; + } + + protected FontListParser.Family convertFamily(Family legacyFamily) { + List<String> nameset = legacyFamily.nameset; + List<String> fileset = legacyFamily.fileset; + + // Arbitrarily choose the first entry in the nameset to be the name + String name = nameset.isEmpty() ? null : nameset.get(0); + + List<Font> fonts = convertFonts(fileset); + + // Add fonts from other variants + for(Family variantFamily : mFamilyVariants.get(legacyFamily)) { + fonts.addAll(convertFonts(variantFamily.fileset)); + } + + return new FontListParser.Family(name, fonts, null, null); + } + + protected List<FontListParser.Font> convertFonts(List<String> fileset) { + List<Font> fonts = new ArrayList<Font>(); + + for(int i=0; i < fileset.size(); i++) { + String fullpath = mFontDir + File.separatorChar + fileset.get(i); + // fileset should only be 4 entries, but if + // its more we can just assign a default. + int weight = i < WEIGHTS.length ? WEIGHTS[i] : DEFAULT_WEIGHT; + boolean isItalic = i < ITALICS.length ? ITALICS[i] : false; + + Font font = new Font(fullpath, weight, isItalic); + fonts.add(font); + } + + return fonts; + } + + protected List<Alias> createAliases() { + List<Alias> aliases = new ArrayList<Alias>(); + + for(Family family : mFamilyVariants.keySet()) { + // Get any aliases that might be from a normal family's nameset. + // eg sans-serif, arial, helvetica, tahoma etc. + if (isNormalStyle(family)) { + aliases.addAll(adaptNamesetAliases(family.nameset)); + } + } + + aliases.addAll(getAliasesForRelatedFamilies()); + + return aliases; + } + + private List<Alias> getAliasesForRelatedFamilies() { + List<Alias> aliases = new ArrayList<Alias>(); + + for(Entry<Family, List<Family>> entry : mFamilyVariants.entrySet()) { + String toName = entry.getKey().nameset.get(0); + List<Family> relatedFamilies = entry.getValue(); + for(Family relatedFamily : relatedFamilies) { + aliases.addAll(adaptNamesetAliases(relatedFamily.nameset, toName)); + } + } + return aliases; + } + + private List<Alias> adaptNamesetAliases(List<String> nameset, String toName) { + List<Alias> aliases = new ArrayList<Alias>(); + for(String name : nameset) { + Alias alias = new Alias(); + alias.name = name; + alias.toName = toName; + aliases.add(alias); + } + return aliases; + } + + private List<Alias> adaptNamesetAliases(List<String> nameset) { + List<Alias> aliases = new ArrayList<Alias>(); + if (nameset.size() < 2) return aliases; // An alias requires a name and toName + + String toName = nameset.get(0); + for(int i = 1; i < nameset.size(); i++) { + Alias alias = new Alias(); + alias.name = nameset.get(i); + alias.toName = toName; + aliases.add(alias); + } + + return aliases; + } +} diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java index 97081f9..d789690 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -21,6 +21,9 @@ import android.util.Xml; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -74,25 +77,83 @@ public class FontListParser { } /* Parse fallback list (no names) */ - public static Config parse(InputStream in) throws XmlPullParserException, IOException { + public static Config parse(File configFilename, String fontDir) + throws XmlPullParserException, IOException { + FileInputStream in = null; + in = new FileInputStream(configFilename); + return FontListParser.parse(in, fontDir); + } + + /* Parse fallback list (no names) */ + public static Config parse(InputStream in, String fontDir) + throws XmlPullParserException, IOException { + BufferedInputStream bis = null; try { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(in, null); - parser.nextTag(); - return readFamilies(parser); + // wrap input stream in a BufferedInputStream, if it's not already, for mark support + if (!(in instanceof BufferedInputStream)) { + bis = new BufferedInputStream(in); + } else { + bis = (BufferedInputStream) in; + } + // mark the beginning so we can reset to this position after checking format + bis.mark(in.available()); + if (isLegacyFormat(bis)) { + return parseLegacyFormat(bis, fontDir); + } else { + return parseNormalFormat(bis, fontDir); + } + } finally { + if (bis != null) bis.close(); + } + } + + public static boolean isLegacyFormat(InputStream in) + throws XmlPullParserException, IOException { + if (!in.markSupported()) { + throw new IllegalArgumentException("InputStream does not support mark"); + } + boolean isLegacy = false; + + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(in, null); + parser.nextTag(); + parser.require(XmlPullParser.START_TAG, null, "familyset"); + String version = parser.getAttributeValue(null, "version"); + isLegacy = version == null; + + // reset the stream so we can read it + in.reset(); + + return isLegacy; + } + + public static Config parseLegacyFormat(InputStream in, String dirName) + throws XmlPullParserException, IOException { + try { + List<LegacyFontListParser.Family> legacyFamilies = LegacyFontListParser.parse(in); + FontListConverter converter = new FontListConverter(legacyFamilies, dirName); + return converter.convert(); } finally { in.close(); } } - private static Config readFamilies(XmlPullParser parser) + public static Config parseNormalFormat(InputStream in, String dirName) + throws XmlPullParserException, IOException { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(in, null); + parser.nextTag(); + return readFamilies(parser, dirName); + } + + private static Config readFamilies(XmlPullParser parser, String dirPath) throws XmlPullParserException, IOException { Config config = new Config(); parser.require(XmlPullParser.START_TAG, null, "familyset"); while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) continue; if (parser.getName().equals("family")) { - config.families.add(readFamily(parser)); + config.families.add(readFamily(parser, dirPath)); } else if (parser.getName().equals("alias")) { config.aliases.add(readAlias(parser)); } else { @@ -102,7 +163,7 @@ public class FontListParser { return config; } - private static Family readFamily(XmlPullParser parser) + private static Family readFamily(XmlPullParser parser, String dirPath) throws XmlPullParserException, IOException { String name = parser.getAttributeValue(null, "name"); String lang = parser.getAttributeValue(null, "lang"); @@ -116,7 +177,7 @@ public class FontListParser { int weight = weightStr == null ? 400 : Integer.parseInt(weightStr); boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style")); String filename = parser.nextText(); - String fullFilename = "/system/fonts/" + filename; + String fullFilename = dirPath + File.separatorChar + filename; fonts.add(new Font(fullFilename, weight, isItalic)); } else { skip(parser); diff --git a/graphics/java/android/graphics/LegacyFontListParser.java b/graphics/java/android/graphics/LegacyFontListParser.java new file mode 100644 index 0000000..22923cb --- /dev/null +++ b/graphics/java/android/graphics/LegacyFontListParser.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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.graphics; + +import android.util.Xml; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * Parses an XML font config. Example: + * + *<familyset> + * + * <family> + * <nameset> + * <name>sans-serif</name> + * <name>arial</name> + * </nameset> + * <fileset> + * <file>Roboto-Regular.ttf</file> + * <file>Roboto-Bold.ttf</file> + * <file>Roboto-Italic.ttf</file> + * <file>Roboto-BoldItalic.ttf</file> + * </fileset> + * </family> + * <family> + * ... + * </family> + *</familyset> + * @hide + */ +public class LegacyFontListParser { + public static class Family { + public List<String> nameset = new ArrayList<String>(); + public List<String> fileset = new ArrayList<String>(); + + public String getName() { + if (nameset != null && !nameset.isEmpty()) { + return nameset.get(0); + } + return null; + } + } + + public static List<Family> parse(InputStream in) + throws XmlPullParserException, IOException { + try { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(in, null); + parser.nextTag(); + return readFamilySet(parser); + } finally { + in.close(); + } + } + + private static List<Family> readFamilySet(XmlPullParser parser) + throws XmlPullParserException, IOException { + List<Family> families = new ArrayList<Family>(); + parser.require(XmlPullParser.START_TAG, null, "familyset"); + + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + + // Starts by looking for the entry tag + if (name.equals("family")) { + Family family = readFamily(parser); + families.add(family); + } + } + return families; + } + + private static Family readFamily(XmlPullParser parser) + throws XmlPullParserException, IOException { + Family family = new Family(); + parser.require(XmlPullParser.START_TAG, null, "family"); + + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + if (name.equals("nameset")) { + List<String> nameset = readNameset(parser); + family.nameset = nameset; + } else if (name.equals("fileset")) { + List<String> fileset = readFileset(parser); + family.fileset = fileset; + } else { + skip(parser); + } + } + return family; + } + + private static List<String> readNameset(XmlPullParser parser) + throws XmlPullParserException, IOException { + List<String> names = new ArrayList<String>(); + parser.require(XmlPullParser.START_TAG, null, "nameset"); + + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String tagname = parser.getName(); + if (tagname.equals("name")) { + String name = readText(parser); + names.add(name); + } else { + skip(parser); + } + } + return names; + } + + private static List<String> readFileset(XmlPullParser parser) + throws XmlPullParserException, IOException { + List<String> files = new ArrayList<String>(); + parser.require(XmlPullParser.START_TAG, null, "fileset"); + + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + if (name.equals("file")) { + String file = readText(parser); + files.add(file); + } else { + skip(parser); + } + } + return files; + } + + // For the tags title and summary, extracts their text values. + private static String readText(XmlPullParser parser) + throws IOException, XmlPullParserException { + String result = ""; + if (parser.next() == XmlPullParser.TEXT) { + result = parser.getText(); + parser.nextTag(); + } + return result; + } + + private static void skip(XmlPullParser parser) + throws XmlPullParserException, IOException { + if (parser.getEventType() != XmlPullParser.START_TAG) { + throw new IllegalStateException(); + } + int depth = 1; + while (depth != 0) { + switch (parser.next()) { + case XmlPullParser.END_TAG: + depth--; + break; + case XmlPullParser.START_TAG: + depth++; + break; + } + } + } +}
\ No newline at end of file diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index db42314..4e9788a 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -25,7 +25,6 @@ import android.util.SparseArray; import org.xmlpull.v1.XmlPullParserException; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; @@ -68,6 +67,8 @@ public class Typeface { static final String FONTS_CONFIG = "fonts.xml"; + static final String SANS_SERIF_FAMILY_NAME = "sans-serif"; + /** * @hide */ @@ -81,6 +82,13 @@ public class Typeface { private int mStyle = 0; + // Typefaces that we can garbage collect when changing fonts, and so we don't break public APIs + private static Typeface DEFAULT_INTERNAL; + private static Typeface DEFAULT_BOLD_INTERNAL; + private static Typeface SANS_SERIF_INTERNAL; + private static Typeface SERIF_INTERNAL; + private static Typeface MONOSPACE_INTERNAL; + private static void setDefault(Typeface t) { sDefaultTypeface = t; nativeSetDefault(t.native_instance); @@ -228,7 +236,10 @@ public class Typeface { for (int i = 0; i < families.length; i++) { ptrArray[i] = families[i].mNativePtr; } - return new Typeface(nativeCreateFromArray(ptrArray)); + + + Typeface typeface = new Typeface(nativeCreateFromArray(ptrArray)); + return typeface; } /** @@ -267,18 +278,111 @@ public class Typeface { return fontFamily; } + /** + * Adds the family from src with the name familyName as a fallback font in dst + * @param src Source font config + * @param dst Destination font config + * @param familyName Name of family to add as a fallback + */ + private static void addFallbackFontsForFamilyName(FontListParser.Config src, + FontListParser.Config dst, String familyName) { + for (Family srcFamily : src.families) { + if (familyName.equals(srcFamily.name)) { + // set the name to null so that it will be added as a fallback + srcFamily.name = null; + dst.families.add(srcFamily); + return; + } + } + } + + /** + * Adds any font families in src that do not exist in dst + * @param src Source font config + * @param dst Destination font config + */ + private static void addMissingFontFamilies(FontListParser.Config src, + FontListParser.Config dst) { + final int N = dst.families.size(); + // add missing families + for (Family srcFamily : src.families) { + boolean addFamily = true; + for (int i = 0; i < N && addFamily; i++) { + final Family dstFamily = dst.families.get(i); + final String dstFamilyName = dstFamily.name; + if (dstFamilyName != null && dstFamilyName.equals(srcFamily.name)) { + addFamily = false; + break; + } + } + if (addFamily) { + dst.families.add(srcFamily); + } + } + } + + /** + * Adds any aliases in src that do not exist in dst + * @param src Source font config + * @param dst Destination font config + */ + private static void addMissingFontAliases(FontListParser.Config src, + FontListParser.Config dst) { + final int N = dst.aliases.size(); + // add missing aliases + for (FontListParser.Alias alias : src.aliases) { + boolean addAlias = true; + for (int i = 0; i < N && addAlias; i++) { + final String dstAliasName = dst.aliases.get(i).name; + if (dstAliasName != null && dstAliasName.equals(alias.name)) { + addAlias = false; + break; + } + } + if (addAlias) { + dst.aliases.add(alias); + } + } + } + /* * (non-Javadoc) * * This should only be called once, from the static class initializer block. */ - private static void init() { + private static void init(boolean forceSystemFonts) { // Load font config and initialize Minikin state File systemFontConfigLocation = getSystemFontConfigLocation(); - File configFilename = new File(systemFontConfigLocation, FONTS_CONFIG); + File themeFontConfigLocation = getThemeFontConfigLocation(); + + File systemConfigFile = new File(systemFontConfigLocation, FONTS_CONFIG); + File themeConfigFile = new File(themeFontConfigLocation, FONTS_CONFIG); + File configFile = null; + File fontDir; + + if (!forceSystemFonts && themeConfigFile.exists()) { + configFile = themeConfigFile; + fontDir = getThemeFontDirLocation(); + } else { + configFile = systemConfigFile; + fontDir = getSystemFontDirLocation(); + } + try { - FileInputStream fontsIn = new FileInputStream(configFilename); - FontListParser.Config fontConfig = FontListParser.parse(fontsIn); + FontListParser.Config fontConfig = FontListParser.parse(configFile, + fontDir.getAbsolutePath()); + FontListParser.Config systemFontConfig = null; + + // If the fonts are coming from a theme, we will need to make sure that we include + // any font families from the system fonts that the theme did not include. + // NOTE: All the system font families without names ALWAYS get added. + if (configFile == themeConfigFile) { + systemFontConfig = FontListParser.parse(systemConfigFile, + getSystemFontDirLocation().getAbsolutePath()); + addMissingFontFamilies(systemFontConfig, fontConfig); + addMissingFontAliases(systemFontConfig, fontConfig); + addFallbackFontsForFamilyName(systemFontConfig, fontConfig, SANS_SERIF_FAMILY_NAME); + } List<FontFamily> familyList = new ArrayList<FontFamily>(); // Note that the default typeface is always present in the fallback list; @@ -289,6 +393,7 @@ public class Typeface { familyList.add(makeFamilyFromParsed(f)); } } + sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]); setDefault(Typeface.createFromFamilies(sFallbackFonts)); @@ -324,22 +429,58 @@ public class Typeface { Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)", e); // TODO: normal in non-Minikin case, remove or make error when Minikin-only } catch (FileNotFoundException e) { - Log.e(TAG, "Error opening " + configFilename); + Log.e(TAG, "Error opening " + configFile, e); } catch (IOException e) { - Log.e(TAG, "Error reading " + configFilename); + Log.e(TAG, "Error reading " + configFile, e); } catch (XmlPullParserException e) { - Log.e(TAG, "XML parse exception for " + configFilename); + Log.e(TAG, "XML parse exception for " + configFile, e); } } + /** @hide */ + public static void recreateDefaults() { + recreateDefaults(false); + } + + /** + * Clears caches in java and skia. + * Skia will then reparse font config + * @hide + */ + public static void recreateDefaults(boolean forceSystemFonts) { + sTypefaceCache.clear(); + sSystemFontMap.clear(); + init(forceSystemFonts); + + DEFAULT_INTERNAL = create((String) null, 0); + DEFAULT_BOLD_INTERNAL = create((String) null, Typeface.BOLD); + SANS_SERIF_INTERNAL = create("sans-serif", 0); + SERIF_INTERNAL = create("serif", 0); + MONOSPACE_INTERNAL = create("monospace", 0); + + DEFAULT.native_instance = DEFAULT_INTERNAL.native_instance; + DEFAULT_BOLD.native_instance = DEFAULT_BOLD_INTERNAL.native_instance; + SANS_SERIF.native_instance = SANS_SERIF_INTERNAL.native_instance; + SERIF.native_instance = SERIF_INTERNAL.native_instance; + MONOSPACE.native_instance = MONOSPACE_INTERNAL.native_instance; + sDefaults[2] = create((String) null, Typeface.ITALIC); + sDefaults[3] = create((String) null, Typeface.BOLD_ITALIC); + } + static { - init(); + init(false); // Set up defaults and typefaces exposed in public API - DEFAULT = create((String) null, 0); - DEFAULT_BOLD = create((String) null, Typeface.BOLD); - SANS_SERIF = create("sans-serif", 0); - SERIF = create("serif", 0); - MONOSPACE = create("monospace", 0); + DEFAULT_INTERNAL = create((String) null, 0); + DEFAULT_BOLD_INTERNAL = create((String) null, Typeface.BOLD); + SANS_SERIF_INTERNAL = create("sans-serif", 0); + SERIF_INTERNAL = create("serif", 0); + MONOSPACE_INTERNAL = create("monospace", 0); + + DEFAULT = new Typeface(DEFAULT_INTERNAL.native_instance); + DEFAULT_BOLD = new Typeface(DEFAULT_BOLD_INTERNAL.native_instance); + SANS_SERIF = new Typeface(SANS_SERIF_INTERNAL.native_instance); + SERIF = new Typeface(SERIF_INTERNAL.native_instance); + MONOSPACE = new Typeface(MONOSPACE_INTERNAL.native_instance); sDefaults = new Typeface[] { DEFAULT, @@ -354,6 +495,18 @@ public class Typeface { return new File("/system/etc/"); } + private static File getSystemFontDirLocation() { + return new File("/system/fonts/"); + } + + private static File getThemeFontConfigLocation() { + return new File("/data/system/theme/fonts/"); + } + + private static File getThemeFontDirLocation() { + return new File("/data/system/theme/fonts/"); + } + @Override protected void finalize() throws Throwable { try { diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index 32af59a..3f1f76a 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -1076,7 +1076,7 @@ public abstract class Drawable { // drawn to the screen. if (opts == null) opts = new BitmapFactory.Options(); opts.inScreenDensity = res != null - ? res.getDisplayMetrics().noncompatDensityDpi : DisplayMetrics.DENSITY_DEVICE; + ? res.getDisplayMetrics().noncompatDensityDpi : DisplayMetrics.DENSITY_DEFAULT; Bitmap bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts); if (bm != null) { byte[] np = bm.getNinePatchChunk(); diff --git a/graphics/tests/localtests/Android.mk b/graphics/tests/localtests/Android.mk new file mode 100644 index 0000000..0b95ecd --- /dev/null +++ b/graphics/tests/localtests/Android.mk @@ -0,0 +1,18 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# We only want this apk build for tests. +LOCAL_MODULE_TAGS := tests +LOCAL_MODULE := FrameworksGraphicsHostTests + +# Include all test java files. +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_JAVA_LIBRARIES := android.test.runner +#LOCAL_PACKAGE_NAME := FrameworksGraphicsTests + +include $(BUILD_STATIC_JAVA_LIBRARY) + +##################################### + + diff --git a/graphics/tests/localtests/src/android/graphics/FontListConverterTest.java b/graphics/tests/localtests/src/android/graphics/FontListConverterTest.java new file mode 100644 index 0000000..60c9053 --- /dev/null +++ b/graphics/tests/localtests/src/android/graphics/FontListConverterTest.java @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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.graphics; + +import android.graphics.FontListParser.Alias; +import android.graphics.LegacyFontListParser.Family; +import android.test.suitebuilder.annotation.SmallTest; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import junit.framework.TestCase; + +public class FontListConverterTest extends TestCase { + // VALID nameset includes the default name first and + // and some other 'aliases' with it. + private static final String[] VALID_NAMESET = { + "sans-serif", + "arial", + "helvetica", + "tahoma", + "verdana" + }; + + // The correct fileset will have 4 files in + // order by type (regular, bold, italic, bolditalic) + private static final String[] VALID_FILESET = { + "Roboto-Regular.ttf", + "Roboto-Bold.ttf", + "Roboto-Italic.ttf", + "Roboto-BoldItalic.ttf" + }; + + // The legacy fontlist format considered thin, light, and black styles + // each as part of their own familysets. The new format does not, so we need + // to provide a test case to adapt this. Note: "condensed" is still considered + // to be its own familyset. So we must be careful + private static final String[] VALID_ADDITIONAL_STYLE_NAMESET = { + "sans-serif-thin" + }; + + private static final String[] VALID_ADDITIONAL_STYLE_FILESET = { + "Roboto-Thin.ttf", + "Roboto-ThinItalic.ttf" + }; + + // thin, light, and black styles are part of the same family but a Roboto "condensed" + // or Roboto "slab" would be considered part of a different family. Since the legacy + // format would already consider these as a different family, we just have to make sure + // they don't get brought back into a common family like thin/light/black + private static final String[] VALID_RELATED_FAMILY_NAMESET = { + "sans-serif-condensed" + }; + + private static final String[] VALID_RELATED_FAMILY_FILESET = { + "RobotoCondensed-Regular.ttf", + "RobotoCondensed-Bold.ttf", + "RobotoCondensed-Italic.ttf", + "RobotoCondensed-BoldItalic.ttf" + }; + + // Some typefaces will only have one style. + private static final String[] VALID_SINGLE_STYLE_FAMIlY_NAMESET = { + "monospace" + }; + private static final String[] VALID_SINGLE_STYLE_FAMIlY_FILESET = { + "DroidSansMono.ttf" + }; + + final String VALID_PATH = "/valid/path/"; + + private Family sValidFamily; // eg "sans-serif" + private Family sValidAdditionalStyleFamily; // eg "sans-serif-light" + private Family sValidRelatedFamily; // eg "sans-serif-condensed" + private Family mValidSingleStyleFamily; // eg "monospace" which only uses DroidSansMono.ttf + + protected void setUp() { + sValidFamily = new Family(); + sValidFamily.nameset = new ArrayList<String>(Arrays.asList(VALID_NAMESET)); + sValidFamily.fileset = new ArrayList<String>(Arrays.asList(VALID_FILESET)); + + sValidAdditionalStyleFamily = new Family(); + sValidAdditionalStyleFamily.nameset = + new ArrayList<String>(Arrays.asList(VALID_ADDITIONAL_STYLE_NAMESET)); + sValidAdditionalStyleFamily.fileset = + new ArrayList<String>(Arrays.asList(VALID_ADDITIONAL_STYLE_FILESET)); + + sValidRelatedFamily = new Family(); + sValidRelatedFamily.nameset = + new ArrayList<String>(Arrays.asList(VALID_RELATED_FAMILY_NAMESET)); + sValidRelatedFamily.fileset = + new ArrayList<String>(Arrays.asList(VALID_RELATED_FAMILY_FILESET)); + + mValidSingleStyleFamily = new Family(); + } + + @SmallTest + public void testValidAdaptedFamilyShouldHaveNameOfNamesetsFirstElement() { + FontListConverter adapter = new FontListConverter(sValidFamily, VALID_PATH); + FontListParser.Family convertedFamily = adapter.convertFamily(sValidFamily); + assertEquals(VALID_NAMESET[0], convertedFamily.name); + } + + @SmallTest + public void testValidAdaptedFamilyShouldHaveFonts() { + FontListConverter adapter = new FontListConverter(sValidFamily, VALID_PATH); + FontListParser.Family convertedFamily = adapter.convertFamily(sValidFamily); + List<FontListParser.Font> fonts = convertedFamily.fonts; + assertEquals(VALID_FILESET.length, fonts.size()); + } + + @SmallTest + public void testValidAdaptedFontsShouldHaveCorrectProperties() { + FontListConverter adapter = new FontListConverter(sValidFamily, VALID_PATH); + List<FontListParser.Font> fonts = adapter.convertFonts(Arrays.asList(VALID_FILESET)); + + assertEquals(VALID_FILESET.length, fonts.size()); + for(int i=0; i < fonts.size(); i++) { + FontListParser.Font font = fonts.get(i); + assertEquals(VALID_PATH + VALID_FILESET[i], font.fontName); + assertEquals("shouldBeItalic", shouldBeItalic(i), font.isItalic); + assertEquals(FontListConverter.WEIGHTS[i], font.weight); + } + } + + @SmallTest + public void testExtraNamesetsShouldConvertToAliases() { + List<Family> families = new ArrayList<Family>(); + families.add(sValidFamily); + + FontListConverter adapter = new FontListConverter(sValidFamily, VALID_PATH); + List<FontListParser.Alias> aliases = adapter.createAliases(); + + // Be sure the aliases point to the first name in the nameset + for(int i = 0; i < aliases.size(); i++) { + FontListParser.Alias alias = aliases.get(i); + assertEquals(VALID_NAMESET[0], alias.toName); + } + + // Be sure the extra namesets are in the alias list + for(int i = 1; i < VALID_NAMESET.length; i++) { + assertTrue("hasAliasWithName", hasAliasWithName(aliases, VALID_NAMESET[i])); + } + } + + /** + * The legacy format treats thin, light, and black fonts to be different families + * The new format treats these as all part of the original + * eg sans-serif and sans-serif-thin become one family + */ + @SmallTest + public void testAdditionalStylesShouldConvertToSameFamily() { + List<Family> families = new ArrayList<Family>(); + families.add(sValidFamily); //eg "sans-serif" + families.add(sValidAdditionalStyleFamily); //eg "sans-serif-light" + + FontListConverter adapter = new FontListConverter(families, VALID_PATH); + List<FontListParser.Family> convertedFamilies = adapter.convertFamilies(); + + // We started with two similiar families, and now should have one + assertEquals(1, convertedFamilies.size()); + + // The name of the family should be the base name, no style modifiers + // ie "sans-serif" not "sans-serif-light" + FontListParser.Family convertedFamily = convertedFamilies.get(0); + assertEquals(sValidFamily.nameset.get(0), convertedFamily.name); + + // Verify all the fonts from both families exist now in the converted Family + List<String> combinedFileSet = new ArrayList<String>(); + combinedFileSet.addAll(sValidFamily.fileset); + combinedFileSet.addAll(sValidAdditionalStyleFamily.fileset); + for(String filename : combinedFileSet) { + String fontName = VALID_PATH + filename; + assertTrue("hasFontWithName", hasFontWithName(convertedFamily, fontName)); + } + } + + /** + * When two families combine, the "varied" family (ie light, light, black) should + * have their namesets converted to aliases. + * IE sans-serif-light should point to sans-serif because the light family + * gets merged to sans-serif + */ + @SmallTest + public void testAdditionalStylesNamesShouldBecomeAliases() { + List<Family> families = new ArrayList<Family>(); + families.add(sValidFamily); //eg "sans-serif" + families.add(sValidAdditionalStyleFamily); //eg "sans-serif-light" + + FontListConverter adapter = new FontListConverter(families, VALID_PATH); + List<Alias> aliases = adapter.createAliases(); + + // Subtract 1 from the total length since VALID_NAMESET[0] will be the family name + int expectedSize = VALID_NAMESET.length + VALID_ADDITIONAL_STYLE_NAMESET.length - 1; + assertEquals(expectedSize, aliases.size()); + + // All aliases should point at the base family + for(Alias alias : aliases) { + assertEquals(VALID_NAMESET[0], alias.toName); + } + + // There should be an alias for every name in the merged in family + for(String name : VALID_ADDITIONAL_STYLE_NAMESET) { + assertTrue("hasAliasWithName", hasAliasWithName(aliases, name)); + } + } + + /** + * sans-serif-condensed should not get merged in with sans-serif + */ + @SmallTest + public void testSimiliarFontsShouldKeepSameFamily() { + List<Family> families = new ArrayList<Family>(); + families.add(sValidFamily); //eg "sans-serif" + families.add(sValidRelatedFamily); //eg "sans-serif-condensed" + + FontListConverter adapter = new FontListConverter(families, VALID_PATH); + List<FontListParser.Family> convertedFamilies = adapter.convertFamilies(); + FontListParser.Family convertedValidFamily = + getFontListFamilyWithName(convertedFamilies, VALID_NAMESET[0]); + FontListParser.Family convertedRelatedFamily = + getFontListFamilyWithName(convertedFamilies, VALID_RELATED_FAMILY_NAMESET[0]); + + + // Valid family should only have its own fonts. Will fail if these were merged + for(String filename : sValidFamily.fileset) { + String fontName = VALID_PATH + filename; + assertTrue("hasFontWithName", hasFontWithName(convertedValidFamily, fontName)); + assertFalse("hasFontWIthName", hasFontWithName(convertedRelatedFamily, fontName)); + } + + // Related family should also only have have its own fonts. Will fail if these were merged + for(String filename : sValidRelatedFamily.fileset) { + String fontName = VALID_PATH + filename; + assertTrue("hasFontWithName", hasFontWithName(convertedRelatedFamily, fontName)); + assertFalse("hasFontWIthName", hasFontWithName(convertedValidFamily, fontName)); + } + } + + private static boolean hasAliasWithName(List<Alias> aliases, String name) { + for (Alias alias : aliases) if (name.equals(alias.name)) return true; + return false; + } + + private static boolean hasFontWithName(FontListParser.Family family, String name) { + for (FontListParser.Font font : family.fonts) { + if(font.fontName != null && font.fontName.equals(name)) { + return true; + } + } + return false; + } + + private static FontListParser.Family getFontListFamilyWithName( + List<FontListParser.Family> families, String name) { + for(FontListParser.Family family : families) { + if (name.equals(family.name)) return family; + } + return null; + } + + private boolean shouldBeItalic(int index) { + // Since the fileset format is regular, bold, italic, bolditalic, anything >= 2 is italic + return index >= 2; + } +} diff --git a/graphics/tests/localtests/src/android/graphics/TypefaceTestSuite.java b/graphics/tests/localtests/src/android/graphics/TypefaceTestSuite.java new file mode 100644 index 0000000..9ef8421 --- /dev/null +++ b/graphics/tests/localtests/src/android/graphics/TypefaceTestSuite.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2008 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.graphics; + +import junit.framework.TestSuite; + +public class TypefaceTestSuite { + public static TestSuite suite() { + TestSuite suite = new TestSuite(TypefaceTestSuite.class.getName()); + suite.addTestSuite(FontListConverterTest.class); + return suite; + } +} |