Merge "Add dynamic font support"
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 |
@@ -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>
|
||||
Binary file not shown.
Binary file not shown.
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user