Merge "Add dynamic font support"

This commit is contained in:
Diego Perez
2017-02-15 10:36:18 +00:00
committed by Android (Google) Code Review
14 changed files with 288 additions and 1 deletions

View File

@@ -31,6 +31,7 @@ import com.android.resources.ResourceType;
import android.annotation.Nullable;
import android.content.res.Resources.NotFoundException;
import android.content.res.Resources.Theme;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
import android.util.TypedValue;
@@ -696,6 +697,22 @@ public final class BridgeTypedArray extends TypedArray {
}
/**
* Retrieve the Typeface for the attribute at <var>index</var>.
* @param index Index of attribute to retrieve.
*
* @return Typeface for the attribute, or null if not defined.
*/
@Override
public Typeface getFont(int index) {
if (!hasValue(index)) {
return null;
}
ResourceValue value = mResourceData[index];
return ResourceHelper.getFont(value, mContext, mTheme);
}
/**
* Retrieve the CharSequence[] for the attribute at <var>index</var>.
* This gets the resource ID of the selected attribute, and uses

View File

@@ -43,6 +43,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.Resources.NotFoundException;
import android.content.res.Resources.Theme;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.icu.text.PluralRules;
import android.util.AttributeSet;
@@ -778,6 +779,35 @@ public class Resources_Delegate {
return getQuantityString(resources, id, quantity);
}
@LayoutlibDelegate
static Typeface getFont(Resources resources, int id) throws
NotFoundException {
Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
if (value != null) {
return ResourceHelper.getFont(value.getSecond(), resources.mContext, null);
}
throwException(resources, id);
// this is not used since the method above always throws
return null;
}
@LayoutlibDelegate
static Typeface getFont(Resources resources, TypedValue outValue, int id) throws
NotFoundException {
Resources_Delegate.getValue(resources, id, outValue, true);
if (outValue.string != null) {
return ResourceHelper.getFont(outValue.string.toString(), resources.mContext, null,
mPlatformResourceFlag[0]);
}
throwException(resources, id);
// this is not used since the method above always throws
return null;
}
@LayoutlibDelegate
static void getValue(Resources resources, int id, TypedValue outValue, boolean resolveRefs)
throws NotFoundException {

View File

@@ -344,7 +344,9 @@ public class FontFamily_Delegate {
ffd.addFont(fontInfo);
return true;
}
fontStream = assetRepository.openAsset(path, AssetManager.ACCESS_STREAMING);
fontStream = isAsset ?
assetRepository.openAsset(path, AssetManager.ACCESS_STREAMING) :
assetRepository.openNonAsset(cookie, path, AssetManager.ACCESS_STREAMING);
if (fontStream == null) {
Bridge.getLog().error(LayoutLog.TAG_MISSING_ASSET, "Asset not found: " + path,
path);

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2017 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 android.annotation.NonNull;
/**
* Class allowing access to package-protected methods/fields.
*/
public class Typeface_Accessor {
public static boolean isSystemFont(@NonNull String fontName) {
return Typeface.sSystemFontMap.containsKey(fontName);
}
}

View File

@@ -38,16 +38,20 @@ import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.content.res.ComplexColor;
import android.content.res.ComplexColor_Accessor;
import android.content.res.FontResourcesParser;
import android.content.res.GradientColor;
import android.content.res.Resources.Theme;
import android.graphics.Bitmap;
import android.graphics.Bitmap_Delegate;
import android.graphics.NinePatch_Delegate;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.Typeface_Accessor;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
import android.text.FontConfig;
import android.util.TypedValue;
import java.io.File;
@@ -367,6 +371,89 @@ public final class ResourceHelper {
return null;
}
/**
* Returns a {@link Typeface} given a font name. The font name, can be a system font family
* (like sans-serif) or a full path if the font is to be loaded from resources.
*/
public static Typeface getFont(String fontName, BridgeContext context, Theme theme, boolean
isFramework) {
if (fontName == null) {
return null;
}
if (Typeface_Accessor.isSystemFont(fontName)) {
// Shortcut for the case where we are asking for a system font name. Those are not
// loaded using external resources.
return null;
}
// Check if this is an asset that we've already loaded dynamically
Typeface typeface = Typeface.findFromCache(context.getAssets(), fontName);
if (typeface != null) {
return typeface;
}
String lowerCaseValue = fontName.toLowerCase();
if (lowerCaseValue.endsWith(".xml")) {
// create a block parser for the file
Boolean psiParserSupport = context.getLayoutlibCallback().getFlag(
RenderParamsFlags.FLAG_KEY_XML_FILE_PARSER_SUPPORT);
XmlPullParser parser = null;
if (psiParserSupport != null && psiParserSupport) {
parser = context.getLayoutlibCallback().getXmlFileParser(fontName);
}
else {
File f = new File(fontName);
if (f.isFile()) {
try {
parser = ParserFactory.create(f);
} catch (XmlPullParserException | FileNotFoundException e) {
// this is an error and not warning since the file existence is checked before
// attempting to parse it.
Bridge.getLog().error(null, "Failed to parse file " + fontName,
e, null /*data*/);
}
}
}
if (parser != null) {
BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
parser, context, isFramework);
try {
FontConfig config = FontResourcesParser.parse(blockParser, context
.getResources());
typeface = Typeface.createFromResources(config, context.getAssets(),
fontName);
} catch (XmlPullParserException | IOException e) {
Bridge.getLog().error(null, "Failed to parse file " + fontName,
e, null /*data*/);
} finally {
blockParser.ensurePopped();
}
} else {
Bridge.getLog().error(LayoutLog.TAG_BROKEN,
String.format("File %s does not exist (or is not a file)", fontName),
null /*data*/);
}
} else {
typeface = Typeface.createFromResources(context.getAssets(), fontName, 0);
}
return typeface;
}
/**
* Returns a {@link Typeface} given a font name. The font name, can be a system font family
* (like sans-serif) or a full path if the font is to be loaded from resources.
*/
public static Typeface getFont(ResourceValue value, BridgeContext context, Theme theme) {
if (value == null) {
return null;
}
return getFont(value.getValue(), context, theme, value.isFramework());
}
private static Drawable getNinePatchDrawable(InputStream inputStream, Density density,
boolean isFramework, String cacheKey, BridgeContext context) throws IOException {
// see if we still have both the chunk and the bitmap in the caches

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android">
<font android:fontStyle="normal" android:fontWeight="400" android:font="@font/testfont" />
<font android:fontStyle="italic" android:fontWeight="400" android:font="@font/testfont2" />
</font-family>

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CONDENSED"
android:textSize="50sp"
android:fontFamily="sans-serif-condensed"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CONDENSED ITALIC"
android:textSize="30sp"
android:fontFamily="sans-serif-condensed"
android:textStyle="italic"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="MONOSPACE"
android:textSize="50sp"
android:fontFamily="monospace"/>
<Space
android:layout_width="wrap_content"
android:layout_height="30dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Custom"
android:textSize="20sp"
android:fontFamily="@font/testfont"/>
<Space
android:layout_width="wrap_content"
android:layout_height="30dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Custom family"
android:textSize="20sp"
android:fontFamily="@font/testfamily"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Custom family"
android:textSize="20sp"
android:fontFamily="@font/testfamily"
android:textStyle="italic"/>
</LinearLayout>

View File

@@ -35,6 +35,7 @@ import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback;
import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
import com.android.layoutlib.bridge.intensive.util.ImageUtils;
import com.android.layoutlib.bridge.intensive.util.ModuleClassLoader;
import com.android.layoutlib.bridge.intensive.util.TestAssetRepository;
import com.android.layoutlib.bridge.intensive.util.TestUtils;
import com.android.tools.layoutlib.java.System_Delegate;
import com.android.utils.ILogger;
@@ -537,6 +538,7 @@ public class RenderTestBase {
configGenerator.getHardwareConfig(), resourceResolver, layoutLibCallback, 0,
targetSdk, getLayoutLog());
sessionParams.setFlag(RenderParamsFlags.FLAG_DO_NOT_RENDER_ON_CREATE, true);
sessionParams.setAssetRepository(new TestAssetRepository());
return sessionParams;
}
}

View File

@@ -384,4 +384,10 @@ public class RenderTests extends RenderTestBase {
strings);
assertTrue(sRenderMessages.isEmpty());
}
@Test
public void testFonts() throws ClassNotFoundException {
// TODO: styles seem to be broken in TextView
renderAndVerify("fonts_test.xml", "font_test.png");
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2017 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 com.android.layoutlib.bridge.intensive.util;
import com.android.ide.common.rendering.api.AssetRepository;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
/**
* {@link AssetRepository} used for render tests.
*/
public class TestAssetRepository extends AssetRepository {
private static InputStream open(String path) throws FileNotFoundException {
File asset = new File(path);
if (asset.isFile()) {
return new FileInputStream(asset);
}
return null;
}
@Override
public boolean isSupported() {
return true;
}
@Override
public InputStream openAsset(String path, int mode) throws IOException {
return open(path);
}
@Override
public InputStream openNonAsset(int cookie, String path, int mode) throws IOException {
return open(path);
}
}

View File

@@ -156,6 +156,7 @@ public final class CreateInfo implements ICreateInfo {
"android.content.res.Resources#getDimensionPixelOffset",
"android.content.res.Resources#getDimensionPixelSize",
"android.content.res.Resources#getDrawable",
"android.content.res.Resources#getFont",
"android.content.res.Resources#getIntArray",
"android.content.res.Resources#getInteger",
"android.content.res.Resources#getLayout",