Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 17e190e7 authored by Seigo Nonaka's avatar Seigo Nonaka Committed by Automerger Merge Worker
Browse files

Merge "Use separate XML for update config in system" into sc-dev am: df1fcdad

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13987642

Change-Id: If90eb93cf1ca79355c11370c19a61739dd30b9ae
parents 0f2dd258 df1fcdad
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.graphics.fonts;

import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;

@@ -213,6 +214,16 @@ public final class FontFamilyUpdateRequest {
        public List<FontVariationAxis> getAxes() {
            return mAxes;
        }

        /**
         * Returns the index of collection
         *
         * TODO(183752879): Make font index configurable and make this SystemApi.
         * @hide
         */
        public @IntRange(from = 0) int getIndex() {
            return 0;
        }
    }

    /**
+301 −18
Original line number Diff line number Diff line
@@ -17,19 +17,24 @@
package android.graphics.fonts;

import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.text.FontConfig;
import android.util.TypedXmlSerializer;

import java.io.File;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * Represents a font update request. Currently only font install request is supported.
@@ -47,6 +52,273 @@ public final class FontUpdateRequest implements Parcelable {
    @Retention(RetentionPolicy.SOURCE)
    public @interface Type {}

    /**
     * Font object used for update.
     *
     * Here is an example of Family/Font XML.
     * <family name="my-sans">
     *   <font name="MySans" weight="400" slant="0" axis="'wght' 400 'ital' 0" index="0" />
     *   <font name="MySans" weight="400" slant="0" axis="'wght' 400 'ital' 1" index="0" />
     *   <font name="MySans" weight="400" slant="0" axis="'wght' 700 'ital' 0" index="0" />
     *   <font name="MySans" weight="400" slant="0" axis="'wght' 700 'ital' 1" index="0" />
     * </family>
     *
     * @see Font#readFromXml(XmlPullParser)
     * @see Font#writeToXml(TypedXmlSerializer, Font)
     * @see Family#readFromXml(XmlPullParser)
     * @see Family#writeFamilyToXml(TypedXmlSerializer, Family)
     */
    public static final class Font implements Parcelable {
        private static final String ATTR_INDEX = "index";
        private static final String ATTR_WEIGHT = "weight";
        private static final String ATTR_SLANT = "slant";
        private static final String ATTR_AXIS = "axis";
        private static final String ATTR_POSTSCRIPT_NAME = "name";

        private final @NonNull String mPostScriptName;
        private final @NonNull FontStyle mFontStyle;
        private final @IntRange(from = 0) int mIndex;
        private final @NonNull String mFontVariationSettings;

        public Font(@NonNull String postScriptName, @NonNull FontStyle fontStyle,
                @IntRange(from = 0) int index, @NonNull String fontVariationSettings) {
            mPostScriptName = postScriptName;
            mFontStyle = fontStyle;
            mIndex = index;
            mFontVariationSettings = fontVariationSettings;
        }

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString8(mPostScriptName);
            dest.writeInt(mFontStyle.getWeight());
            dest.writeInt(mFontStyle.getSlant());
            dest.writeInt(mIndex);
            dest.writeString8(mFontVariationSettings);
        }

        public static final @NonNull Creator<Font> CREATOR = new Creator<Font>() {
            @Override
            public Font createFromParcel(Parcel source) {
                String fontName = source.readString8();
                int weight = source.readInt();
                int slant = source.readInt();
                int index = source.readInt();
                String varSettings = source.readString8();
                return new Font(fontName, new FontStyle(weight, slant), index, varSettings);
            }

            @Override
            public Font[] newArray(int size) {
                return new Font[size];
            }
        };

        /**
         * Write {@link Font} instance to XML file.
         *
         * For the XML format, see {@link Font} class comment.
         *
         * @param out output XML serializer
         * @param font a Font instance to be written.
         */
        public static void writeToXml(TypedXmlSerializer out, Font font) throws IOException {
            out.attribute(null, ATTR_POSTSCRIPT_NAME, font.getPostScriptName());
            out.attributeInt(null, ATTR_INDEX, font.getIndex());
            out.attributeInt(null, ATTR_WEIGHT, font.getFontStyle().getWeight());
            out.attributeInt(null, ATTR_SLANT, font.getFontStyle().getSlant());
            out.attribute(null, ATTR_AXIS, font.getFontVariationSettings());
        }

        /**
         * Read {@link Font} instance from &lt;font&gt; element in XML
         *
         * For the XML format, see {@link Font} class comment.
         *
         * @param parser a parser that point &lt;font&gt; element.
         * @return a font instance
         * @throws IOException if font element is invalid.
         */
        public static Font readFromXml(XmlPullParser parser) throws IOException {
            String psName = parser.getAttributeValue(null, ATTR_POSTSCRIPT_NAME);
            if (psName == null) {
                throw new IOException("name attribute is missing font tag.");
            }
            int index = getAttributeValueInt(parser, ATTR_INDEX, 0);
            int weight = getAttributeValueInt(parser, ATTR_WEIGHT, FontStyle.FONT_WEIGHT_NORMAL);
            int slant = getAttributeValueInt(parser, ATTR_SLANT, FontStyle.FONT_SLANT_UPRIGHT);
            String varSettings = parser.getAttributeValue(null, ATTR_AXIS);
            if (varSettings == null) {
                varSettings = "";
            }
            return new Font(psName, new FontStyle(weight, slant), index, varSettings);
        }

        public @NonNull String getPostScriptName() {
            return mPostScriptName;
        }

        public @NonNull FontStyle getFontStyle() {
            return mFontStyle;
        }

        public @IntRange(from = 0) int getIndex() {
            return mIndex;
        }

        public @NonNull String getFontVariationSettings() {
            return mFontVariationSettings;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Font font = (Font) o;
            return mIndex == font.mIndex
                    && mPostScriptName.equals(font.mPostScriptName)
                    && mFontStyle.equals(font.mFontStyle)
                    && mFontVariationSettings.equals(font.mFontVariationSettings);
        }

        @Override
        public int hashCode() {
            return Objects.hash(mPostScriptName, mFontStyle, mIndex, mFontVariationSettings);
        }

        @Override
        public String toString() {
            return "Font{"
                    + "mPostScriptName='" + mPostScriptName + '\''
                    + ", mFontStyle=" + mFontStyle
                    + ", mIndex=" + mIndex
                    + ", mFontVariationSettings='" + mFontVariationSettings + '\''
                    + '}';
        }
    }

    /**
     * Font Family object used for update request.
     */
    public static final class Family implements Parcelable {
        private static final String TAG_FAMILY = "family";
        private static final String ATTR_NAME = "name";
        private static final String TAG_FONT = "font";

        private final @Nullable String mName;
        private final @NonNull List<Font> mFonts;

        public Family(String name, List<Font> fonts) {
            mName = name;
            mFonts = fonts;
        }

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString8(mName);
            dest.writeParcelableList(mFonts, flags);
        }

        public static final @NonNull Creator<Family> CREATOR = new Creator<Family>() {

            @Override
            public Family createFromParcel(Parcel source) {
                String familyName = source.readString8();
                List<Font> fonts = source.readParcelableList(
                        new ArrayList<>(), Font.class.getClassLoader());
                return new Family(familyName, fonts);
            }

            @Override
            public Family[] newArray(int size) {
                return new Family[size];
            }
        };

        /**
         * Write {@link Family} instance to XML.
         *
         * For the XML format, see {@link Font} class comment.
         *
         * @param out an output XML serializer
         * @param family a {@link Family} instance to be written
         */
        public static void writeFamilyToXml(@NonNull TypedXmlSerializer out, @NonNull Family family)
                throws IOException {
            out.attribute(null, ATTR_NAME, family.getName());
            List<Font> fonts = family.getFonts();
            for (int i = 0; i < fonts.size(); ++i) {
                Font font = fonts.get(i);
                out.startTag(null, TAG_FONT);
                Font.writeToXml(out, font);
                out.endTag(null, TAG_FONT);
            }
        }

        /**
         * Read a {@link Family} instance from &lt;family&gt; element in XML
         *
         * For the XML format, see {@link Font} class comment.
         *
         * @param parser an XML parser that points &lt;family&gt; element.
         * @return an {@link Family} instance
         */
        public static @NonNull Family readFromXml(@NonNull XmlPullParser parser)
                throws XmlPullParserException, IOException {
            List<Font> fonts = new ArrayList<>();
            if (parser.getEventType() != XmlPullParser.START_TAG
                    || !parser.getName().equals(TAG_FAMILY)) {
                throw new IOException("Unexpected parser state: must be START_TAG with family");
            }
            String name = parser.getAttributeValue(null, ATTR_NAME);
            int type = 0;
            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
                if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_FONT)) {
                    fonts.add(Font.readFromXml(parser));
                } else if (type == XmlPullParser.END_TAG && parser.getName().equals(TAG_FAMILY)) {
                    break;
                }
            }
            return new Family(name, fonts);
        }

        public @NonNull String getName() {
            return mName;
        }

        public @NonNull List<Font> getFonts() {
            return mFonts;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Family family = (Family) o;
            return mName.equals(family.mName) && mFonts.equals(family.mFonts);
        }

        @Override
        public int hashCode() {
            return Objects.hash(mName, mFonts);
        }

        @Override
        public String toString() {
            return "Family{mName='" + mName + '\'' + ", mFonts=" + mFonts + '}';
        }
    }

    public static final Creator<FontUpdateRequest> CREATOR = new Creator<FontUpdateRequest>() {
        @Override
        public FontUpdateRequest createFromParcel(Parcel in) {
@@ -68,7 +340,7 @@ public final class FontUpdateRequest implements Parcelable {
    private final byte[] mSignature;
    // NonNull if mType == TYPE_UPDATE_FONT_FAMILY.
    @Nullable
    private final FontConfig.FontFamily mFontFamily;
    private final Family mFontFamily;

    public FontUpdateRequest(@NonNull ParcelFileDescriptor fd, @NonNull byte[] signature) {
        mType = TYPE_UPDATE_FONT_FILE;
@@ -77,31 +349,29 @@ public final class FontUpdateRequest implements Parcelable {
        mFontFamily = null;
    }

    public FontUpdateRequest(@NonNull FontConfig.FontFamily fontFamily) {
    public FontUpdateRequest(@NonNull Family fontFamily) {
        mType = TYPE_UPDATE_FONT_FAMILY;
        mFd = null;
        mSignature = null;
        mFontFamily = fontFamily;
    }

    public FontUpdateRequest(@NonNull String postScriptName,
    public FontUpdateRequest(@NonNull String familyName,
            @NonNull List<FontFamilyUpdateRequest.Font> variations) {
        // TODO: Serialize the request directly instead of reusing FontConfig.FontFamily.
        this(createFontFamily(postScriptName, variations));
        this(createFontFamily(familyName, variations));
    }

    private static FontConfig.FontFamily createFontFamily(@NonNull String postScriptName,
    private static Family createFontFamily(@NonNull String familyName,
            @NonNull List<FontFamilyUpdateRequest.Font> fonts) {
        List<FontConfig.Font> configFonts = new ArrayList<>(fonts.size());
        List<Font> updateFonts = new ArrayList<>(fonts.size());
        for (FontFamilyUpdateRequest.Font font : fonts) {
            // TODO: Support .otf.
            configFonts.add(new FontConfig.Font(new File(font.getPostScriptName() + ".ttf"), null,
                    font.getStyle(), 0 /* index */,
                    FontVariationAxis.toFontVariationSettings(font.getAxes()),
                    null /* fontFamilyName */));
            updateFonts.add(new Font(
                    font.getPostScriptName(),
                    font.getStyle(),
                    font.getIndex(),
                    FontVariationAxis.toFontVariationSettings(font.getAxes())));
        }
        return new FontConfig.FontFamily(configFonts, postScriptName,
                LocaleList.getEmptyLocaleList(), FontConfig.FontFamily.VARIANT_DEFAULT);
        return new Family(familyName, updateFonts);
    }

    protected FontUpdateRequest(Parcel in) {
@@ -126,7 +396,7 @@ public final class FontUpdateRequest implements Parcelable {
    }

    @Nullable
    public FontConfig.FontFamily getFontFamily() {
    public Family getFontFamily() {
        return mFontFamily;
    }

@@ -142,4 +412,17 @@ public final class FontUpdateRequest implements Parcelable {
        dest.writeBlob(mSignature);
        dest.writeParcelable(mFontFamily, flags);
    }

    // Utility functions
    private static int getAttributeValueInt(XmlPullParser parser, String name, int defaultValue) {
        try {
            String value = parser.getAttributeValue(null, name);
            if (value == null) {
                return defaultValue;
            }
            return Integer.parseInt(value);
        } catch (NumberFormatException e) {
            return defaultValue;
        }
    }
}
+0 −42
Original line number Diff line number Diff line
@@ -24,14 +24,12 @@ import static android.text.FontConfig.FontFamily.VARIANT_DEFAULT;
import static android.text.FontConfig.FontFamily.VARIANT_ELEGANT;

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;

import static junit.framework.Assert.fail;

import android.graphics.fonts.FontStyle;
import android.os.LocaleList;
import android.text.FontConfig;
import android.util.TypedXmlSerializer;
import android.util.Xml;

import androidx.test.filters.SmallTest;
@@ -43,7 +41,6 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -69,10 +66,6 @@ public final class FontListParserTest {

        FontConfig.FontFamily family = readFamily(xml);
        assertThat(family).isEqualTo(expected);

        String serialized = writeFamily(family);
        assertWithMessage("serialized = " + serialized)
                .that(readFamily(serialized)).isEqualTo(expected);
    }

    @Test
@@ -94,10 +87,6 @@ public final class FontListParserTest {

        FontConfig.FontFamily family = readFamily(xml);
        assertThat(family).isEqualTo(expected);

        String serialized = writeFamily(family);
        assertWithMessage("serialized = " + serialized)
                .that(readFamily(serialized)).isEqualTo(expected);
    }

    @Test
@@ -115,10 +104,6 @@ public final class FontListParserTest {

        FontConfig.FontFamily family = readFamily(xml);
        assertThat(family).isEqualTo(expected);

        String serialized = writeFamily(family);
        assertWithMessage("serialized = " + serialized)
                .that(readFamily(serialized)).isEqualTo(expected);
    }

    @Test
@@ -136,10 +121,6 @@ public final class FontListParserTest {

        FontConfig.FontFamily family = readFamily(xml);
        assertThat(family).isEqualTo(expected);

        String serialized = writeFamily(family);
        assertWithMessage("serialized = " + serialized)
                .that(readFamily(serialized)).isEqualTo(expected);
    }

    @Test
@@ -164,10 +145,6 @@ public final class FontListParserTest {
                "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
        FontConfig.FontFamily family = readFamily(xml);
        assertThat(family).isEqualTo(expected);

        String serialized = writeFamily(family);
        assertWithMessage("serialized = " + serialized)
                .that(readFamily(serialized)).isEqualTo(expected);
    }

    @Test
@@ -194,10 +171,6 @@ public final class FontListParserTest {
                "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
        FontConfig.FontFamily family = readFamily(xml);
        assertThat(family).isEqualTo(expected);

        String serialized = writeFamily(family);
        assertWithMessage("serialized = " + serialized)
                .that(readFamily(serialized)).isEqualTo(expected);
    }

    @Test
@@ -218,10 +191,6 @@ public final class FontListParserTest {
                "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
        FontConfig.FontFamily family = readFamily(xml);
        assertThat(family).isEqualTo(expected);

        String serialized = writeFamily(family);
        assertWithMessage("serialized = " + serialized)
                .that(readFamily(serialized)).isEqualTo(expected);
    }

    @Test
@@ -338,15 +307,4 @@ public final class FontListParserTest {
        parser.nextTag();
        return FontListParser.readFamily(parser, "", null);
    }

    private String writeFamily(FontConfig.FontFamily family) throws IOException {
        TypedXmlSerializer out = Xml.newFastSerializer();
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        out.setOutput(buffer, "UTF-8");
        out.startTag(null, "family");
        FontListParser.writeFamily(out, family);
        out.endTag(null, "family");
        out.endDocument();
        return buffer.toString("UTF-8");
    }
}
+0 −62
Original line number Diff line number Diff line
@@ -25,8 +25,6 @@ import android.graphics.fonts.FontVariationAxis;
import android.os.Build;
import android.os.LocaleList;
import android.text.FontConfig;
import android.text.TextUtils;
import android.util.TypedXmlSerializer;
import android.util.Xml;

import org.xmlpull.v1.XmlPullParser;
@@ -194,32 +192,6 @@ public class FontListParser {
        return new FontConfig.FontFamily(fonts, name, LocaleList.forLanguageTags(lang), intVariant);
    }

    /**
     * Write a family tag representing {@code fontFamily}. The tag should be started by the caller.
     */
    public static void writeFamily(TypedXmlSerializer out, FontConfig.FontFamily fontFamily)
            throws IOException {
        if (!TextUtils.isEmpty(fontFamily.getName())) {
            out.attribute(null, ATTR_NAME, fontFamily.getName());
        }
        if (!fontFamily.getLocaleList().isEmpty()) {
            out.attribute(null, ATTR_LANG, fontFamily.getLocaleList().toLanguageTags());
        }
        switch (fontFamily.getVariant()) {
            case FontConfig.FontFamily.VARIANT_COMPACT:
                out.attribute(null, ATTR_VARIANT, VARIANT_COMPACT);
                break;
            case FontConfig.FontFamily.VARIANT_ELEGANT:
                out.attribute(null, ATTR_VARIANT, VARIANT_ELEGANT);
                break;
        }
        for (FontConfig.Font font : fontFamily.getFontList()) {
            out.startTag(null, TAG_FONT);
            writeFont(out, font);
            out.endTag(null, TAG_FONT);
        }
    }

    /** Matches leading and trailing XML whitespace. */
    private static final Pattern FILENAME_WHITESPACE_PATTERN =
            Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$");
@@ -292,34 +264,6 @@ public class FontListParser {
        return null;
    }

    private static void writeFont(TypedXmlSerializer out, FontConfig.Font font)
            throws IOException {
        if (font.getTtcIndex() != 0) {
            out.attributeInt(null, ATTR_INDEX, font.getTtcIndex());
        }
        if (font.getStyle().getWeight() != FontStyle.FONT_WEIGHT_NORMAL) {
            out.attributeInt(null, ATTR_WEIGHT, font.getStyle().getWeight());
        }
        if (font.getStyle().getSlant() == FontStyle.FONT_SLANT_ITALIC) {
            out.attribute(null, ATTR_STYLE, STYLE_ITALIC);
        } else {
            out.attribute(null, ATTR_STYLE, STYLE_NORMAL);
        }
        if (!TextUtils.isEmpty(font.getFontFamilyName())) {
            out.attribute(null, ATTR_FALLBACK_FOR, font.getFontFamilyName());
        }
        out.text(font.getFile().getName());
        FontVariationAxis[] axes =
                FontVariationAxis.fromFontVariationSettings(font.getFontVariationSettings());
        if (axes != null) {
            for (FontVariationAxis axis : axes) {
                out.startTag(null, TAG_AXIS);
                writeAxis(out, axis);
                out.endTag(null, TAG_AXIS);
            }
        }
    }

    private static FontVariationAxis readAxis(XmlPullParser parser)
            throws XmlPullParserException, IOException {
        String tagStr = parser.getAttributeValue(null, ATTR_TAG);
@@ -328,12 +272,6 @@ public class FontListParser {
        return new FontVariationAxis(tagStr, Float.parseFloat(styleValueStr));
    }

    private static void writeAxis(TypedXmlSerializer out, FontVariationAxis axis)
            throws IOException {
        out.attribute(null, ATTR_TAG, axis.getTag());
        out.attributeFloat(null, ATTR_STYLEVALUE, axis.getStyleValue());
    }

    /**
     * Reads alias elements
     */
+7 −8
Original line number Diff line number Diff line
@@ -17,8 +17,7 @@
package com.android.server.graphics.fonts;

import android.annotation.NonNull;
import android.graphics.FontListParser;
import android.text.FontConfig;
import android.graphics.fonts.FontUpdateRequest;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Slog;
@@ -48,7 +47,7 @@ import java.util.Set;
    /* package */ static class Config {
        public long lastModifiedMillis;
        public final Set<String> updatedFontDirs = new ArraySet<>();
        public final List<FontConfig.FontFamily> fontFamilies = new ArrayList<>();
        public final List<FontUpdateRequest.Family> fontFamilies = new ArrayList<>();
    }

    /**
@@ -81,8 +80,8 @@ import java.util.Set;
                    case TAG_FAMILY:
                        // updatableFontMap is not ready here. We get the base file names by passing
                        // empty fontDir, and resolve font paths later.
                        out.fontFamilies.add(FontListParser.readFamily(
                                parser, "" /* fontDir */, null /* updatableFontMap */));
                        out.fontFamilies.add(FontUpdateRequest.Family.readFromXml(parser));
                        break;
                    default:
                        Slog.w(TAG, "Skipping unknown tag: " + tag);
                }
@@ -108,11 +107,11 @@ import java.util.Set;
            out.attribute(null, ATTR_VALUE, dir);
            out.endTag(null, TAG_UPDATED_FONT_DIR);
        }
        List<FontConfig.FontFamily> fontFamilies = config.fontFamilies;
        List<FontUpdateRequest.Family> fontFamilies = config.fontFamilies;
        for (int i = 0; i < fontFamilies.size(); i++) {
            FontConfig.FontFamily fontFamily = fontFamilies.get(i);
            FontUpdateRequest.Family fontFamily = fontFamilies.get(i);
            out.startTag(null, TAG_FAMILY);
            FontListParser.writeFamily(out, fontFamily);
            FontUpdateRequest.Family.writeFamilyToXml(out, fontFamily);
            out.endTag(null, TAG_FAMILY);
        }
        out.endTag(null, TAG_ROOT);
Loading