diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index 35cf9038f9ae1..9bc8e18a0fa8d 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -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 index.
+ * @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 index.
* This gets the resource ID of the selected attribute, and uses
diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
index e0f8e1c33bc6a..d71cc6f7ddf04 100644
--- a/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
@@ -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 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 {
diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
index a43e54579da23..fb24c01086d74 100644
--- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -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);
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Accessor.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Accessor.java
new file mode 100644
index 0000000000000..ce669cba9c473
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Accessor.java
@@ -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);
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index c197e40eb4cff..b3a2d3e27f88e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -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
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/font_test.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/font_test.png
new file mode 100644
index 0000000000000..b2baa98be0a05
Binary files /dev/null and b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/font_test.png differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfamily.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfamily.xml
new file mode 100644
index 0000000000000..b1e9206bc9f64
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfamily.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont.ttf b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont.ttf
new file mode 100644
index 0000000000000..2852302935807
Binary files /dev/null and b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont.ttf differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont2.ttf b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont2.ttf
new file mode 100644
index 0000000000000..b7bf5b4aa8ad5
Binary files /dev/null and b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont2.ttf differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/fonts_test.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/fonts_test.xml
new file mode 100644
index 0000000000000..c63b211c967d4
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/fonts_test.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java
index 3e5f9e074fef5..67b42a7cf86d7 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java
@@ -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;
}
}
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
index 73e51ecf73ce5..913519ca56af8 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
@@ -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");
+ }
}
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/TestAssetRepository.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/TestAssetRepository.java
new file mode 100644
index 0000000000000..0856ac9252bb8
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/TestAssetRepository.java
@@ -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);
+ }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 94302d328313b..a8582c60bc64b 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -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",