diff options
author | 2017-02-13 13:11:02 +0000 | |
---|---|---|
committer | 2017-02-14 18:24:16 +0000 | |
commit | 789c4b4b14880621f05e7750f594b24bc93fcff9 (patch) | |
tree | f61aa876436da3a7bd796867c20b783b6f5ba231 | |
parent | 349a8aeff997c603852681a3af58d6841c6940c0 (diff) |
Add dynamic font support
This CL allows loading fonts from resources.
Test: Added new fonts test
Change-Id: Ic82239121cc3f78f2a22b22de42e54575c1f2d98
14 files changed, 288 insertions, 1 deletions
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java index 35cf9038f9ae..9bc8e18a0fa8 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; @@ -697,6 +698,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 * {@link Resources#getTextArray Resources.getTextArray} of the owning 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 e0f8e1c33bc6..d71cc6f7ddf0 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; @@ -779,6 +780,35 @@ public class Resources_Delegate { } @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 { Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag); diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java index a43e54579da2..fb24c01086d7 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 000000000000..ce669cba9c47 --- /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 c197e40eb4cf..b3a2d3e27f88 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 Binary files differnew file mode 100644 index 000000000000..b2baa98be0a0 --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/font_test.png 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 000000000000..b1e9206bc9f6 --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfamily.xml @@ -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>
\ 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 Binary files differnew file mode 100644 index 000000000000..285230293580 --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont.ttf 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 Binary files differnew file mode 100644 index 000000000000..b7bf5b4aa8ad --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont2.ttf 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 000000000000..c63b211c967d --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/fonts_test.xml @@ -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>
\ 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 3e5f9e074fef..67b42a7cf86d 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 73e51ecf73ce..913519ca56af 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 000000000000..0856ac9252bb --- /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 94302d328313..a8582c60bc64 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", |