| /* |
| * Copyright (C) 2014 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; |
| |
| import com.android.ide.common.rendering.api.RenderSession; |
| import com.android.ide.common.rendering.api.ResourceNamespace; |
| import com.android.ide.common.rendering.api.ResourceReference; |
| import com.android.ide.common.rendering.api.ResourceValueImpl; |
| import com.android.ide.common.rendering.api.SessionParams; |
| import com.android.ide.common.rendering.api.SessionParams.RenderingMode; |
| import com.android.ide.common.rendering.api.ViewInfo; |
| import com.android.ide.common.rendering.api.XmlParserFactory; |
| import com.android.internal.R; |
| import com.android.layoutlib.bridge.android.BridgeContext; |
| import com.android.layoutlib.bridge.android.RenderParamsFlags; |
| import com.android.layoutlib.bridge.impl.ParserFactory; |
| import com.android.layoutlib.bridge.impl.RenderAction; |
| import com.android.layoutlib.bridge.impl.RenderActionTestUtil; |
| import com.android.layoutlib.bridge.impl.ResourceHelper; |
| import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator; |
| import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback; |
| import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser; |
| import com.android.resources.Density; |
| import com.android.resources.Navigation; |
| import com.android.resources.ResourceType; |
| |
| import org.junit.After; |
| import org.junit.Test; |
| import org.kxml2.io.KXmlParser; |
| import org.xmlpull.v1.XmlPullParser; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.content.res.AssetManager; |
| import android.content.res.ColorStateList; |
| import android.content.res.Configuration; |
| import android.content.res.Resources; |
| import android.content.res.Resources_Delegate; |
| import android.graphics.Color; |
| import android.util.DisplayMetrics; |
| import android.util.StateSet; |
| import android.util.TypedValue; |
| |
| import java.awt.BasicStroke; |
| import java.awt.Graphics2D; |
| import java.awt.image.BufferedImage; |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.PrintWriter; |
| import java.lang.reflect.Field; |
| import java.util.concurrent.TimeUnit; |
| |
| import static org.junit.Assert.assertArrayEquals; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotEquals; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertTrue; |
| |
| /** |
| * Set of render tests |
| */ |
| public class RenderTests extends RenderTestBase { |
| |
| @After |
| public void afterTestCase() { |
| com.android.layoutlib.bridge.test.widgets.HookWidget.reset(); |
| } |
| |
| @Test |
| public void testActivity() throws ClassNotFoundException, FileNotFoundException { |
| renderAndVerify("activity.xml", "activity.png", true); |
| } |
| |
| @Test |
| public void testActivityOnOldTheme() throws ClassNotFoundException, FileNotFoundException { |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| LayoutPullParser parser = LayoutPullParser.createFromString( |
| "<RelativeLayout xmlns:android=\"http://schemas" + |
| ".android.com/apk/res/android\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:layout_height=\"match_parent\"\n" + |
| " android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n" + |
| " android:paddingRight=\"@dimen/activity_horizontal_margin\"\n" + |
| " android:paddingTop=\"@dimen/activity_vertical_margin\"\n" + |
| " android:paddingBottom=\"@dimen/activity_vertical_margin\">\n" + |
| " <TextView\n" + |
| " android:text=\"@string/hello_world\"\n" + |
| " android:layout_width=\"wrap_content\"\n" + |
| " android:layout_height=\"200dp\"\n" + |
| " android:background=\"#FF0000\"\n" + |
| " android:id=\"@+id/text1\"/>\n" + |
| "</RelativeLayout>"); |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.NoTitleBar", false) |
| .build(); |
| |
| renderAndVerify(params, "simple_activity-old-theme.png"); |
| } |
| |
| @Test |
| public void testTranslucentBars() throws ClassNotFoundException, FileNotFoundException { |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| LayoutPullParser parser = createParserFromPath("four_corners.xml"); |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.Light.NoActionBar.TranslucentDecor", false) |
| .build(); |
| renderAndVerify(params, "four_corners_translucent.png"); |
| |
| parser = createParserFromPath("four_corners.xml"); |
| params = getSessionParamsBuilder() |
| .setConfigGenerator(ConfigGenerator.NEXUS_5_LAND) |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.Light.NoActionBar.TranslucentDecor", false) |
| .build(); |
| renderAndVerify(params, "four_corners_translucent_land.png"); |
| |
| parser = createParserFromPath("four_corners.xml"); |
| params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.Light.NoActionBar", false) |
| .build(); |
| renderAndVerify(params, "four_corners.png"); |
| } |
| |
| @Test |
| public void testAllWidgets() throws ClassNotFoundException, FileNotFoundException { |
| LayoutPullParser parser = createParserFromPath("allwidgets.xml"); |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| layoutLibCallback.setUseShadow(false); |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setConfigGenerator(ConfigGenerator.NEXUS_5) |
| .setCallback(layoutLibCallback) |
| .build(); |
| |
| renderAndVerify(params, "allwidgets.png"); |
| |
| // We expect fidelity warnings for Path.isConvex. Fail for anything else. |
| sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported.")); |
| } |
| |
| @Test |
| public void testArrayCheck() throws ClassNotFoundException, FileNotFoundException { |
| renderAndVerify("array_check.xml", "array_check.png", false); |
| |
| // We expect fidelity warnings for Path.isConvex. Fail for anything else. |
| sRenderMessages.removeIf( |
| message -> message.equals("Font$Builder.nAddAxis is not supported.")); |
| } |
| |
| @Test |
| public void testAllWidgetsTablet() throws ClassNotFoundException, FileNotFoundException { |
| LayoutPullParser parser = createParserFromPath("allwidgets.xml"); |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| layoutLibCallback.setUseShadow(false); |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setConfigGenerator(ConfigGenerator.NEXUS_7_2012) |
| .setCallback(layoutLibCallback) |
| .build(); |
| renderAndVerify(params, "allwidgets_tab.png"); |
| |
| // We expect fidelity warnings for Path.isConvex. Fail for anything else. |
| sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported.")); |
| sRenderMessages.removeIf(message -> message.equals("Font$Builder.nAddAxis is not supported.")); |
| } |
| |
| @Test |
| public void testActivityActionBar() throws ClassNotFoundException { |
| String simpleActivity = |
| "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:layout_height=\"match_parent\"\n" + |
| " android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n" + |
| " android:paddingRight=\"@dimen/activity_horizontal_margin\"\n" + |
| " android:paddingTop=\"@dimen/activity_vertical_margin\"\n" + |
| " android:paddingBottom=\"@dimen/activity_vertical_margin\">\n" + |
| " <TextView\n" + |
| " android:text=\"@string/hello_world\"\n" + |
| " android:layout_width=\"wrap_content\"\n" + |
| " android:layout_height=\"200dp\"\n" + |
| " android:background=\"#FF0000\"\n" + |
| " android:id=\"@+id/text1\"/>\n" + |
| "</RelativeLayout>"; |
| |
| LayoutPullParser parser = LayoutPullParser.createFromString(simpleActivity); |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.Light.NoActionBar", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| |
| renderAndVerify(params, "simple_activity_noactionbar.png"); |
| |
| parser = LayoutPullParser.createFromString(simpleActivity); |
| params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.Light", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| |
| renderAndVerify(params, "simple_activity.png"); |
| |
| // This also tests that a theme with "NoActionBar" DOES HAVE an action bar when we are |
| // displaying menus. |
| parser = LayoutPullParser.createFromString(simpleActivity); |
| params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.Light.NoActionBar", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| params.setFlag(RenderParamsFlags.FLAG_KEY_ROOT_TAG, "menu"); |
| renderAndVerify(params, "simple_activity.png"); |
| } |
| |
| @Test |
| public void testOnApplyInsetsCall() |
| throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException { |
| // We get the widget via reflection to avoid IntelliJ complaining about the class being |
| // located in the wrong package. (From the Bridge tests point of view, it is) |
| Class insetsWidgetClass = Class.forName("com.android.layoutlib.test.myapplication.widgets" + |
| ".InsetsWidget"); |
| Field field = insetsWidgetClass.getDeclaredField("sApplyInsetsCalled"); |
| assertFalse((Boolean)field.get(null)); |
| |
| LayoutPullParser parser = LayoutPullParser.createFromString( |
| "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:padding=\"16dp\"\n" + |
| " android:orientation=\"horizontal\"\n" + |
| " android:layout_width=\"wrap_content\"\n" + |
| " android:layout_height=\"wrap_content\">\n" + "\n" + |
| " <com.android.layoutlib.test.myapplication.widgets.InsetsWidget\n" + |
| " android:text=\"Hello world\"\n" + |
| " android:layout_width=\"wrap_content\"\n" + |
| " android:layout_height=\"wrap_content\"\n" + |
| " android:id=\"@+id/text1\"/>\n" + "</LinearLayout>\n"); |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.Light.NoActionBar", false) |
| .build(); |
| |
| render(sBridge, params, -1); |
| |
| assertTrue((Boolean)field.get(null)); |
| field.set(null, false); |
| } |
| |
| /** Test expand_layout.xml */ |
| @Test |
| public void testExpand() throws ClassNotFoundException, FileNotFoundException { |
| // Create the layout pull parser. |
| LayoutPullParser parser = createParserFromPath("expand_vert_layout.xml"); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| ConfigGenerator customConfigGenerator = new ConfigGenerator() |
| .setScreenWidth(300) |
| .setScreenHeight(20) |
| .setDensity(Density.XHIGH) |
| .setNavigation(Navigation.NONAV); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setConfigGenerator(customConfigGenerator) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| |
| renderAndVerify(params, "expand_vert_layout.png"); |
| |
| customConfigGenerator = new ConfigGenerator() |
| .setScreenWidth(20) |
| .setScreenHeight(300) |
| .setDensity(Density.XHIGH) |
| .setNavigation(Navigation.NONAV); |
| parser = createParserFromPath("expand_horz_layout.xml"); |
| params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setConfigGenerator(customConfigGenerator) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.H_SCROLL) |
| .build(); |
| |
| renderAndVerify(params, "expand_horz_layout.png"); |
| } |
| |
| /** Test indeterminate_progressbar.xml */ |
| @Test |
| public void testVectorAnimation() throws ClassNotFoundException { |
| String layout = "\n" + |
| "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:padding=\"16dp\"\n" + |
| " android:orientation=\"horizontal\"\n" + |
| " android:layout_width=\"fill_parent\"\n" + |
| " android:layout_height=\"fill_parent\">\n" + "\n" + |
| " <ProgressBar\n" + " android:layout_height=\"fill_parent\"\n" + |
| " android:layout_width=\"fill_parent\" />\n" + "\n" + |
| "</LinearLayout>\n"; |
| |
| // Create the layout pull parser. |
| LayoutPullParser parser = LayoutPullParser.createFromString(layout); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| |
| renderAndVerify(params, "animated_vector.png", TimeUnit.SECONDS.toNanos(2)); |
| |
| parser = LayoutPullParser.createFromString(layout); |
| params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| renderAndVerify(params, "animated_vector_1.png", TimeUnit.SECONDS.toNanos(3)); |
| } |
| |
| /** |
| * Test a vector drawable that uses trimStart and trimEnd. It also tests all the primitives |
| * for vector drawables (lines, moves and cubic and quadratic curves). |
| */ |
| @Test |
| public void testVectorDrawable() throws ClassNotFoundException { |
| // Create the layout pull parser. |
| LayoutPullParser parser = LayoutPullParser.createFromString( |
| "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:padding=\"16dp\"\n" + |
| " android:orientation=\"horizontal\"\n" + |
| " android:layout_width=\"fill_parent\"\n" + |
| " android:layout_height=\"fill_parent\">\n" + |
| " <ImageView\n" + |
| " android:layout_height=\"fill_parent\"\n" + |
| " android:layout_width=\"fill_parent\"\n" + |
| " android:src=\"@drawable/multi_path\" />\n" + "\n" + |
| "</LinearLayout>"); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .disableDecoration() |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| |
| renderAndVerify(params, "vector_drawable.png", TimeUnit.SECONDS.toNanos(2)); |
| } |
| |
| /** |
| * Regression test for http://b.android.com/91383 and http://b.android.com/203797 |
| */ |
| @Test |
| public void testVectorDrawable91383() throws ClassNotFoundException { |
| // Create the layout pull parser. |
| LayoutPullParser parser = LayoutPullParser.createFromString( |
| "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:padding=\"16dp\"\n" + |
| " android:orientation=\"vertical\"\n" + |
| " android:layout_width=\"fill_parent\"\n" + |
| " android:layout_height=\"fill_parent\">\n" + |
| " <ImageView\n" + |
| " android:layout_height=\"wrap_content\"\n" + |
| " android:layout_width=\"wrap_content\"\n" + |
| " android:src=\"@drawable/android\"/>\n" + |
| " <ImageView\n" + |
| " android:layout_height=\"wrap_content\"\n" + |
| " android:layout_width=\"wrap_content\"\n" + |
| " android:src=\"@drawable/headset\"/>\n" + |
| " <ImageView\n" + |
| " android:layout_height=\"wrap_content\"\n" + |
| " android:layout_width=\"wrap_content\"\n" + |
| " android:src=\"@drawable/clipped_even_odd\"/>\n" + |
| "</LinearLayout>"); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| |
| renderAndVerify(params, "vector_drawable_91383.png", TimeUnit.SECONDS.toNanos(2)); |
| } |
| |
| /** |
| * Test a ImageView which has a vector drawable as its src and tint attribute. |
| */ |
| @Test |
| public void testVectorDrawableWithTintInImageView() throws ClassNotFoundException { |
| // Create the layout pull parser. |
| LayoutPullParser parser = LayoutPullParser.createFromString( |
| "<ImageView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:layout_height=\"match_parent\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:src=\"@drawable/vector_drawable_without_tint\"\n" + |
| " android:tint=\"#FF00FF00\" />"); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| |
| renderAndVerify(params, "vector_drawable_with_tint_in_image_view.png", |
| TimeUnit.SECONDS.toNanos(2)); |
| } |
| |
| /** |
| * Test a vector drawable which has tint attribute. |
| */ |
| @Test |
| public void testVectorDrawableWithTintInItself() throws ClassNotFoundException { |
| // Create the layout pull parser. |
| LayoutPullParser parser = LayoutPullParser.createFromString( |
| "<ImageView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:layout_height=\"match_parent\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:src=\"@drawable/vector_drawable_with_tint\" />"); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| |
| renderAndVerify(params, "vector_drawable_with_tint_itself.png", |
| TimeUnit.SECONDS.toNanos(2)); |
| } |
| |
| /** |
| * Test a vector drawable that uses trimStart and trimEnd. It also tests all the primitives |
| * for vector drawables (lines, moves and cubic and quadratic curves). |
| */ |
| @Test |
| public void testVectorDrawableHasMultipleLineInPathData() throws ClassNotFoundException { |
| // Create the layout pull parser. |
| LayoutPullParser parser = LayoutPullParser.createFromString( |
| "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:padding=\"16dp\"\n" + |
| " android:orientation=\"horizontal\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:layout_height=\"match_parent\">\n" + |
| " <ImageView\n" + |
| " android:layout_height=\"match_parent\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:src=\"@drawable/multi_line_of_path_data\" />\n\n" + |
| "</LinearLayout>"); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| |
| renderAndVerify(params, "vector_drawable_multi_line_of_path_data.png", |
| TimeUnit.SECONDS.toNanos(2)); |
| } |
| |
| /** |
| * Tests that the gradients are correctly transformed using the viewport of the vector drawable. |
| * <p/> |
| * If a vector drawable is 50x50 and the gradient has startX=25 and startY=25, the gradient |
| * will start in the middle of the box. |
| * <p/> |
| * http://b/65495452 |
| */ |
| @Test |
| public void testVectorDrawableGradient() throws ClassNotFoundException { |
| // Create the layout pull parser. |
| LayoutPullParser parser = LayoutPullParser.createFromString( |
| "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:padding=\"16dp\"\n" + |
| " android:orientation=\"horizontal\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:layout_height=\"match_parent\">\n" + |
| " <ImageView\n" + |
| " android:layout_height=\"match_parent\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:src=\"@drawable/shadow\" />\n\n" + |
| "</LinearLayout>"); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| |
| renderAndVerify(params, "vector_drawable_gradient.png", |
| TimeUnit.SECONDS.toNanos(2)); |
| } |
| |
| /** |
| * Tests that the radial gradients are correctly transformed using the viewport of the vector |
| * drawable. |
| * <p/> |
| * http://b/66168608 |
| */ |
| @Test |
| public void testVectorDrawableRadialGradient() throws ClassNotFoundException { |
| // Create the layout pull parser. |
| LayoutPullParser parser = LayoutPullParser.createFromString( |
| "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:padding=\"16dp\"\n" + |
| " android:orientation=\"horizontal\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:layout_height=\"match_parent\">\n" + |
| " <ImageView\n" + |
| " android:layout_height=\"match_parent\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:src=\"@drawable/radial_gradient\" />\n\n" + |
| "</LinearLayout>"); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| |
| renderAndVerify(params, "vector_drawable_radial_gradient.png", |
| TimeUnit.SECONDS.toNanos(2)); |
| } |
| |
| /** |
| * Tests that the gradients are correctly displayed when using transparent colors |
| * and a wide range of offset values. |
| * <p/> |
| * http://b/112759140 |
| */ |
| @Test |
| public void testGradientColors() throws ClassNotFoundException { |
| // Create the layout pull parser. |
| LayoutPullParser parser = LayoutPullParser.createFromString( |
| "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:padding=\"16dp\"\n" + |
| " android:orientation=\"horizontal\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:layout_height=\"match_parent\">\n" + |
| " <ImageView\n" + |
| " android:layout_height=\"match_parent\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:src=\"@drawable/gradient\" />\n\n" + |
| "</LinearLayout>"); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .disableDecoration() |
| .build(); |
| |
| renderAndVerify(params, "gradient_colors.png", |
| TimeUnit.SECONDS.toNanos(2)); |
| } |
| |
| /** |
| * Tests that the gradients are correctly combined with alpha values. |
| * <p/> |
| * http://b/122260583 |
| */ |
| @Test |
| public void testGradientAlphaDrawable() throws ClassNotFoundException { |
| // Create the layout pull parser. |
| LayoutPullParser parser = LayoutPullParser.createFromString( |
| "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:padding=\"16dp\"\n" + |
| " android:orientation=\"horizontal\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:layout_height=\"match_parent\">\n" + |
| " <ImageView\n" + |
| " android:layout_height=\"match_parent\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:src=\"@drawable/vector_gradient_alpha\" />\n\n" + |
| "</LinearLayout>"); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .disableDecoration() |
| .build(); |
| |
| renderAndVerify(params, "gradient_alpha_drawable.png", |
| TimeUnit.SECONDS.toNanos(2)); |
| } |
| |
| /** Test activity.xml */ |
| @Test |
| public void testScrollingAndMeasure() throws ClassNotFoundException, FileNotFoundException { |
| // Create the layout pull parser. |
| LayoutPullParser parser = createParserFromPath("scrolled.xml"); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .disableDecoration() |
| .build(); |
| params.setExtendedViewInfoMode(true); |
| |
| // Do an only-measure pass |
| RenderSession session = sBridge.createSession(params); |
| session.measure(); |
| RenderResult result = RenderResult.getFromSession(session); |
| assertNotNull(result); |
| assertNotNull(result.getResult()); |
| assertTrue(result.getResult().isSuccess()); |
| |
| ViewInfo rootLayout = result.getRootViews().get(0); |
| // Check the first box in the main LinearLayout |
| assertEquals(-90, rootLayout.getChildren().get(0).getTop()); |
| assertEquals(-30, rootLayout.getChildren().get(0).getLeft()); |
| assertEquals(90, rootLayout.getChildren().get(0).getBottom()); |
| assertEquals(150, rootLayout.getChildren().get(0).getRight()); |
| |
| // Check the first box within the nested LinearLayout |
| assertEquals(-450, rootLayout.getChildren().get(5).getChildren().get(0).getTop()); |
| assertEquals(90, rootLayout.getChildren().get(5).getChildren().get(0).getLeft()); |
| assertEquals(-270, rootLayout.getChildren().get(5).getChildren().get(0).getBottom()); |
| assertEquals(690, rootLayout.getChildren().get(5).getChildren().get(0).getRight()); |
| |
| // Do a full render pass |
| parser = createParserFromPath("scrolled.xml"); |
| |
| params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .disableDecoration() |
| .build(); |
| params.setExtendedViewInfoMode(true); |
| |
| result = renderAndVerify(params, "scrolled.png"); |
| assertNotNull(result); |
| assertNotNull(result.getResult()); |
| assertTrue(result.getResult().isSuccess()); |
| } |
| |
| @Test |
| public void testGetResourceNameVariants() throws Exception { |
| // Setup |
| // Create the layout pull parser for our resources (empty.xml can not be part of the test |
| // app as it won't compile). |
| LayoutPullParser parser = LayoutPullParser.createFromPath("/empty.xml"); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setConfigGenerator(ConfigGenerator.NEXUS_4) |
| .setCallback(layoutLibCallback) |
| .build(); |
| AssetManager assetManager = AssetManager.getSystem(); |
| DisplayMetrics metrics = new DisplayMetrics(); |
| Configuration configuration = RenderAction.getConfiguration(params); |
| BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(), |
| params.getAssets(), params.getLayoutlibCallback(), configuration, |
| params.getTargetSdkVersion(), params.isRtlSupported()); |
| Resources resources = Resources_Delegate.initSystem(context, assetManager, metrics, |
| configuration, params.getLayoutlibCallback()); |
| // Test |
| assertEquals("android:style/ButtonBar", |
| resources.getResourceName(android.R.style.ButtonBar)); |
| assertEquals("android", resources.getResourcePackageName(android.R.style.ButtonBar)); |
| assertEquals("ButtonBar", resources.getResourceEntryName(android.R.style.ButtonBar)); |
| assertEquals("style", resources.getResourceTypeName(android.R.style.ButtonBar)); |
| Integer id = Resources_Delegate.getLayoutlibCallback(resources).getOrGenerateResourceId( |
| new ResourceReference(ResourceNamespace.RES_AUTO, ResourceType.STRING, "app_name")); |
| assertNotNull(id); |
| assertEquals("com.android.layoutlib.test.myapplication:string/app_name", |
| resources.getResourceName(id)); |
| assertEquals("com.android.layoutlib.test.myapplication", |
| resources.getResourcePackageName(id)); |
| assertEquals("string", resources.getResourceTypeName(id)); |
| assertEquals("app_name", resources.getResourceEntryName(id)); |
| |
| context.disposeResources(); |
| } |
| |
| @Test |
| public void testStringEscaping() throws Exception { |
| // Setup |
| // Create the layout pull parser for our resources (empty.xml can not be part of the test |
| // app as it won't compile). |
| LayoutPullParser parser = LayoutPullParser.createFromPath("/empty.xml"); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(RenderTestBase.getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| SessionParams params = getSessionParamsBuilder() |
| .setConfigGenerator(ConfigGenerator.NEXUS_4) |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .build(); |
| AssetManager assetManager = AssetManager.getSystem(); |
| DisplayMetrics metrics = new DisplayMetrics(); |
| Configuration configuration = RenderAction.getConfiguration(params); |
| BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(), |
| params.getAssets(), params.getLayoutlibCallback(), configuration, |
| params.getTargetSdkVersion(), params.isRtlSupported()); |
| Resources resources = Resources_Delegate.initSystem(context, assetManager, metrics, |
| configuration, params.getLayoutlibCallback()); |
| |
| Integer id = |
| Resources_Delegate.getLayoutlibCallback(resources) |
| .getOrGenerateResourceId( |
| new ResourceReference( |
| ResourceNamespace.RES_AUTO, |
| ResourceType.ARRAY, |
| "string_array")); |
| assertNotNull(id); |
| String[] strings = resources.getStringArray(id); |
| assertArrayEquals( |
| new String[]{"mystring", "Hello world!", "candidates", "Unknown", "?EC"}, |
| strings); |
| assertTrue(sRenderMessages.isEmpty()); |
| |
| context.disposeResources(); |
| } |
| |
| @Test |
| public void testFonts() throws ClassNotFoundException, FileNotFoundException { |
| // TODO: styles seem to be broken in TextView |
| renderAndVerify("fonts_test.xml", "font_test.png", false); |
| sRenderMessages.removeIf( |
| message -> message.equals("Font$Builder.nAddAxis is not supported.")); |
| } |
| |
| @Test |
| public void testAdaptiveIcon() throws ClassNotFoundException, FileNotFoundException { |
| // Create the layout pull parser. |
| String layout = |
| "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:padding=\"16dp\"\n" + |
| " android:orientation=\"horizontal\"\n" + |
| " android:layout_width=\"fill_parent\"\n" + |
| " android:layout_height=\"fill_parent\">\n" + |
| " <ImageView\n" + |
| " android:layout_height=\"wrap_content\"\n" + |
| " android:layout_width=\"wrap_content\"\n" + |
| " android:src=\"@drawable/adaptive\" />\n" + |
| "</LinearLayout>\n"; |
| LayoutPullParser parser = LayoutPullParser.createFromString(layout); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| |
| renderAndVerify(params, "adaptive_icon.png"); |
| |
| layoutLibCallback.setAdaptiveIconMaskPath( |
| "M50 0C77.6 0 100 22.4 100 50C100 77.6 77.6 100 50 100C22.4 100 0 77.6 0 50C0 " + |
| "22.4 22.4 0 50 0Z"); |
| params = getSessionParamsBuilder() |
| .setParser(LayoutPullParser.createFromString(layout)) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| renderAndVerify(params, "adaptive_icon_circle.png"); |
| |
| layoutLibCallback.setAdaptiveIconMaskPath( |
| "M50,0L92,0C96.42,0 100,4.58 100 8L100,92C100, 96.42 96.42 100 92 100L8 100C4.58," + |
| " 100 0 96.42 0 92L0 8 C 0 4.42 4.42 0 8 0L50 0Z"); |
| params = getSessionParamsBuilder() |
| .setParser(LayoutPullParser.createFromString(layout)) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| renderAndVerify(params, "adaptive_icon_rounded_corners.png"); |
| |
| layoutLibCallback.setAdaptiveIconMaskPath( |
| "M50,0 C10,0 0,10 0,50 0,90 10,100 50,100 90,100 100,90 100,50 100,10 90,0 50,0 Z"); |
| params = getSessionParamsBuilder() |
| .setParser(LayoutPullParser.createFromString(layout)) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| renderAndVerify(params, "adaptive_icon_squircle.png"); |
| } |
| |
| @Test |
| public void testColorTypedValue() throws Exception { |
| // Setup |
| // Create the layout pull parser for our resources (empty.xml can not be part of the test |
| // app as it won't compile). |
| LayoutPullParser parser = LayoutPullParser.createFromPath("/empty.xml"); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(RenderTestBase.getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| SessionParams params = getSessionParamsBuilder() |
| .setConfigGenerator(ConfigGenerator.NEXUS_4) |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .build(); |
| DisplayMetrics metrics = new DisplayMetrics(); |
| Configuration configuration = RenderAction.getConfiguration(params); |
| |
| BridgeContext mContext = |
| new BridgeContext(params.getProjectKey(), metrics, params.getResources(), |
| params.getAssets(), params.getLayoutlibCallback(), configuration, |
| params.getTargetSdkVersion(), params.isRtlSupported()); |
| |
| TypedValue outValue = new TypedValue(); |
| mContext.resolveThemeAttribute(android.R.attr.colorPrimary, outValue, true); |
| assertEquals(TypedValue.TYPE_INT_COLOR_ARGB8, outValue.type); |
| assertNotEquals(0, outValue.data); |
| assertTrue(sRenderMessages.isEmpty()); |
| } |
| |
| @Test |
| public void testColorStateList() throws Exception { |
| final String STATE_LIST = "<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n" + |
| " <item android:state_pressed=\"true\"\n" + |
| " android:color=\"?android:attr/colorForeground\"/> \n" + |
| " <item android:state_focused=\"true\"\n" + |
| " android:color=\"?android:attr/colorBackground\"/> \n" + |
| " <item android:color=\"#a000\"/> <!-- default -->\n" + "</selector>"; |
| |
| File tmpColorList = File.createTempFile("statelist", "xml"); |
| try(PrintWriter output = new PrintWriter(new FileOutputStream(tmpColorList))) { |
| output.println(STATE_LIST); |
| } |
| |
| // Setup |
| // Create the layout pull parser for our resources (empty.xml can not be part of the test |
| // app as it won't compile). |
| ParserFactory.setParserFactory(new XmlParserFactory() { |
| @Override |
| @Nullable |
| public XmlPullParser createXmlParserForPsiFile(@NonNull String fileName) { |
| return null; |
| } |
| |
| @Override |
| @Nullable |
| public XmlPullParser createXmlParserForFile(@NonNull String fileName) { |
| return null; |
| } |
| |
| @Override |
| @NonNull |
| public XmlPullParser createXmlParser() { |
| return new KXmlParser(); |
| } |
| }); |
| |
| LayoutPullParser parser = LayoutPullParser.createFromPath("/empty.xml"); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(RenderTestBase.getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| SessionParams params = getSessionParamsBuilder() |
| .setConfigGenerator(ConfigGenerator.NEXUS_4) |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material", false) |
| .build(); |
| DisplayMetrics metrics = new DisplayMetrics(); |
| Configuration configuration = RenderAction.getConfiguration(params); |
| |
| BridgeContext mContext = |
| new BridgeContext(params.getProjectKey(), metrics, params.getResources(), |
| params.getAssets(), params.getLayoutlibCallback(), configuration, |
| params.getTargetSdkVersion(), params.isRtlSupported()); |
| mContext.initResources(); |
| BridgeContext oldContext = RenderActionTestUtil.setBridgeContext(mContext); |
| |
| try { |
| ColorStateList stateList = ResourceHelper.getColorStateList( |
| new ResourceValueImpl( |
| ResourceNamespace.RES_AUTO, |
| ResourceType.COLOR, |
| "test_list", |
| tmpColorList.getAbsolutePath()), |
| mContext, |
| null); |
| assertNotNull(stateList); |
| assertEquals(Color.parseColor("#ffffffff"), stateList.getColorForState( |
| StateSet.get(StateSet.VIEW_STATE_PRESSED), |
| 0 |
| )); |
| assertEquals(Color.parseColor("#ff303030"), stateList.getColorForState( |
| StateSet.get(StateSet.VIEW_STATE_FOCUSED), |
| 0 |
| )); |
| assertEquals(Color.parseColor("#AA000000"), stateList.getDefaultColor()); |
| |
| // Now apply theme overlay and check the colors changed |
| Resources.Theme theme = mContext.getResources().newTheme(); |
| theme.applyStyle(R.style.ThemeOverlay_Material_Light, true); |
| stateList = ResourceHelper.getColorStateList( |
| new ResourceValueImpl( |
| ResourceNamespace.RES_AUTO, |
| ResourceType.COLOR, |
| "test_list", |
| tmpColorList.getAbsolutePath()), |
| mContext, |
| theme); |
| assertNotNull(stateList); |
| assertEquals(Color.parseColor("#ff000000"), stateList.getColorForState( |
| StateSet.get(StateSet.VIEW_STATE_PRESSED), |
| 0 |
| )); |
| assertEquals(Color.parseColor("#fffafafa"), stateList.getColorForState( |
| StateSet.get(StateSet.VIEW_STATE_FOCUSED), |
| 0 |
| )); |
| assertEquals(Color.parseColor("#AA000000"), stateList.getDefaultColor()); |
| } finally { |
| RenderActionTestUtil.setBridgeContext(oldContext); |
| } |
| mContext.disposeResources(); |
| } |
| |
| @Test |
| public void testShadowFlagsNoShadows() throws Exception { |
| LayoutPullParser parser = createParserFromPath("shadows_test.xml"); |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| layoutLibCallback.setUseShadow(false); |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setConfigGenerator(ConfigGenerator.NEXUS_5) |
| .setCallback(layoutLibCallback) |
| .disableDecoration() |
| .build(); |
| |
| renderAndVerify(params, "shadows_test_no_shadow.png"); |
| // We expect fidelity warnings for Path.isConvex. Fail for anything else. |
| sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported.")); |
| } |
| |
| @Test |
| public void testRectangleShadow() throws Exception { |
| LayoutPullParser parser = createParserFromPath("shadows_test.xml"); |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| layoutLibCallback.setShadowQuality(false); |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setConfigGenerator(ConfigGenerator.NEXUS_5) |
| .setCallback(layoutLibCallback) |
| .disableDecoration() |
| .build(); |
| |
| renderAndVerify(params, "shadows_test.png"); |
| // We expect fidelity warnings for Path.isConvex. Fail for anything else. |
| sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported.")); |
| } |
| |
| @Test |
| public void testHighQualityRectangleShadow() throws Exception { |
| LayoutPullParser parser = createParserFromPath("shadows_test.xml"); |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| layoutLibCallback.setShadowQuality(true); |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setConfigGenerator(ConfigGenerator.NEXUS_5) |
| .setCallback(layoutLibCallback) |
| .disableDecoration() |
| .build(); |
| |
| renderAndVerify(params, "shadows_test_high_quality.png"); |
| // We expect fidelity warnings for Path.isConvex. Fail for anything else. |
| sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported.")); |
| } |
| |
| @Test |
| public void testHighQualityRoundedEdgeRectangleShadow() throws Exception { |
| LayoutPullParser parser = createParserFromPath("shadows_rounded_edge_test.xml"); |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| layoutLibCallback.setShadowQuality(true); |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setConfigGenerator(ConfigGenerator.NEXUS_5) |
| .setCallback(layoutLibCallback) |
| .build(); |
| |
| renderAndVerify(params, "shadows_test_high_quality_rounded_edge.png"); |
| // We expect fidelity warnings for Path.isConvex. Fail for anything else. |
| sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported.")); |
| } |
| |
| @Test |
| public void testHighQualityLargeViewShadow() throws Exception { |
| LayoutPullParser parser = createParserFromPath("large_view_shadows_test.xml"); |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| layoutLibCallback.setShadowQuality(true); |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setConfigGenerator(ConfigGenerator.NEXUS_5) |
| .setCallback(layoutLibCallback) |
| .disableDecoration() |
| .build(); |
| |
| renderAndVerify(params, "large_shadows_test_high_quality.png"); |
| // We expect fidelity warnings for Path.isConvex. Fail for anything else. |
| sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported.")); |
| } |
| |
| @Test |
| public void testHighQualityShadowSizes() throws Exception { |
| LayoutPullParser parser = createParserFromPath("shadow_sizes_test.xml"); |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| layoutLibCallback.setShadowQuality(true); |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setConfigGenerator(ConfigGenerator.NEXUS_5) |
| .setCallback(layoutLibCallback) |
| .build(); |
| |
| renderAndVerify(params, "shadow_sizes_test_high_quality.png"); |
| // We expect fidelity warnings for Path.isConvex. Fail for anything else. |
| sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported.")); |
| } |
| |
| @Test |
| public void testResourcesGetIdentifier() throws Exception { |
| // Setup |
| // Create the layout pull parser for our resources (empty.xml can not be part of the test |
| // app as it won't compile). |
| LayoutPullParser parser = LayoutPullParser.createFromPath("/empty.xml"); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| SessionParams params = getSessionParamsBuilder() |
| .setConfigGenerator(ConfigGenerator.NEXUS_4) |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .build(); |
| AssetManager assetManager = AssetManager.getSystem(); |
| DisplayMetrics metrics = new DisplayMetrics(); |
| Configuration configuration = RenderAction.getConfiguration(params); |
| BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(), |
| params.getAssets(), params.getLayoutlibCallback(), configuration, |
| params.getTargetSdkVersion(), params.isRtlSupported()); |
| Resources resources = Resources_Delegate.initSystem(context, assetManager, metrics, |
| configuration, params.getLayoutlibCallback()); |
| Integer id = |
| Resources_Delegate.getLayoutlibCallback(resources) |
| .getOrGenerateResourceId( |
| new ResourceReference( |
| ResourceNamespace.RES_AUTO, |
| ResourceType.STRING, |
| "app_name")); |
| assertNotNull(id); |
| assertEquals(id.intValue(), |
| resources.getIdentifier("com.android.layoutlib.test.myapplication:string/app_name", |
| null, null)); |
| assertEquals(id.intValue(), resources.getIdentifier("app_name", "string", |
| "com.android.layoutlib.test.myapplication")); |
| assertEquals(0, resources.getIdentifier("string/app_name", null, null)); |
| assertEquals(0, resources.getIdentifier("string/app_name", null, "com.foo.bar")); |
| assertEquals(0, resources.getIdentifier("string/does_not_exist", null, |
| "com.android.layoutlib.test.myapplication")); |
| assertEquals(R.string.accept, resources.getIdentifier("android:string/accept", null, |
| null)); |
| assertEquals(R.string.accept, resources.getIdentifier("string/accept", null, |
| "android")); |
| assertEquals(R.id.message, resources.getIdentifier("id/message", null, |
| "android")); |
| assertEquals(R.string.accept, resources.getIdentifier("accept", "string", |
| "android")); |
| |
| context.disposeResources(); |
| } |
| |
| /** |
| * If a 9patch image was in the nodpi or anydpi folder, the density of the image was 0 resulting |
| * in a float division by 0 and thus an infinite padding |
| * when layoutlib tries to scale the padding of the 9patch. |
| * |
| * http://b/37136109 |
| */ |
| @Test |
| public void test9PatchNoDPIBackground() throws Exception { |
| String layout = |
| "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:layout_height=\"match_parent\"\n" + |
| " android:background=\"@drawable/ninepatch\"\n" + |
| " android:layout_margin=\"20dp\"\n" + |
| " android:orientation=\"vertical\">\n\n" + |
| " <Button\n" + |
| " android:layout_width=\"wrap_content\"\n" + |
| " android:layout_height=\"wrap_content\"\n" + |
| " android:text=\"Button\" />\n\n" + |
| " <Button\n" + |
| " android:layout_width=\"wrap_content\"\n" + |
| " android:layout_height=\"wrap_content\"\n" + |
| " android:text=\"Button\" />\n" |
| + "</LinearLayout>"; |
| |
| LayoutPullParser parser = LayoutPullParser.createFromString(layout); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| |
| renderAndVerify(params, "ninepatch_background.png"); |
| } |
| |
| @Test |
| public void testAssetManager() throws Exception { |
| String layout = |
| "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:padding=\"16dp\"\n" + |
| " android:orientation=\"horizontal\"\n" + |
| " android:layout_width=\"fill_parent\"\n" + |
| " android:layout_height=\"fill_parent\">\n" + |
| " <com.android.layoutlib.test.myapplication.widgets.AssetView\n" + |
| " android:layout_height=\"wrap_content\"\n" + |
| " android:layout_width=\"wrap_content\" />\n" + |
| "</LinearLayout>\n"; |
| LayoutPullParser parser = LayoutPullParser.createFromString(layout); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| |
| renderAndVerify(params, "asset.png"); |
| } |
| |
| /** |
| * Tests that calling setTheme in a ContextThemeWrapper actually applies the theme |
| * |
| * http://b/66902070 |
| */ |
| @Test |
| public void testContextThemeWrapper() throws ClassNotFoundException { |
| // Create the layout pull parser. |
| LayoutPullParser parser = LayoutPullParser.createFromString( |
| "<com.android.layoutlib.test.myapplication.ThemableWidget " + |
| "xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:layout_width=\"wrap_content\"\n" + |
| " android:layout_height=\"wrap_content\" />\n"); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| |
| renderAndVerify(params, "context_theme_wrapper.png", TimeUnit.SECONDS.toNanos(2)); |
| } |
| |
| /** |
| * Tests that a crashing view does not prevent others from working. This is meant to prevent |
| * crashes in framework views since custom views are already handled by Android Studio by |
| * rewriting the byte code. |
| */ |
| @Test |
| public void testCrashes() throws ClassNotFoundException { |
| final String layout = |
| "<LinearLayout " + |
| "xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:layout_height=\"match_parent\">\n" + |
| "<com.android.layoutlib.bridge.test.widgets.HookWidget " + |
| " android:layout_width=\"100dp\"\n" + |
| " android:layout_height=\"200dp\" />\n" + |
| "<LinearLayout " + |
| " android:background=\"#CBBAF0\"\n" + |
| " android:layout_width=\"100dp\"\n" + |
| " android:layout_height=\"200dp\" />\n" + |
| "</LinearLayout>"; |
| { |
| com.android.layoutlib.bridge.test.widgets.HookWidget.setOnPreDrawHook(() -> { |
| throw new NullPointerException(); |
| }); |
| |
| // Create the layout pull parser. |
| LayoutPullParser parser = LayoutPullParser.createFromString(layout); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| |
| renderAndVerify(params, "ondraw_crash.png", TimeUnit.SECONDS.toNanos(2)); |
| } |
| |
| com.android.layoutlib.bridge.test.widgets.HookWidget.reset(); |
| |
| { |
| com.android.layoutlib.bridge.test.widgets.HookWidget.setOnPreMeasure(() -> { |
| throw new NullPointerException(); |
| }); |
| |
| LayoutPullParser parser = LayoutPullParser.createFromString(layout); |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .build(); |
| |
| renderAndVerify(params, "onmeasure_crash.png", TimeUnit.SECONDS.toNanos(2)); |
| } |
| |
| // We expect the view error messages. Fail for anything else. |
| sRenderMessages.removeIf(message -> message.equals("View draw failed")); |
| sRenderMessages.removeIf(message -> message.equals("View measure failed")); |
| } |
| |
| /** |
| * Paints the borders of the given {@link ViewInfo} and its children to the passed |
| * {@link Graphics2D} context. |
| * The method will used the given parentLeft and parentTop as the given vInfo coordinates. |
| * The depth is used to calculate different colors for the borders depending on the hierarchy |
| * depth. |
| */ |
| private void paintBorders(@NonNull Graphics2D g, int parentLeft, int parentTop, int depth, |
| @NonNull ViewInfo vInfo) { |
| int leftMargin = Math.max(0, vInfo.getLeftMargin()); |
| int topMargin = Math.max(0, vInfo.getTopMargin()); |
| int x = parentLeft + vInfo.getLeft() + leftMargin; |
| int y = parentTop + vInfo.getTop() + topMargin; |
| int w = vInfo.getRight() - vInfo.getLeft(); |
| int h = vInfo.getBottom() - vInfo.getTop(); |
| g.setXORMode(java.awt.Color.decode(Integer.toString(depth * 50000))); |
| g.drawRect(x, y, w, h); |
| |
| for (ViewInfo child : vInfo.getChildren()) { |
| paintBorders(g, x, y, depth + 1, child); |
| } |
| } |
| |
| @Test |
| public void testViewBoundariesReporting() throws Exception { |
| String layout = |
| "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:layout_height=\"match_parent\"\n" + |
| " android:background=\"@drawable/ninepatch\"\n" + |
| " android:layout_margin=\"20dp\"\n" + |
| " android:orientation=\"vertical\">\n" + "\n" + |
| " <TextView\n" + |
| " android:layout_width=\"150dp\"\n" + |
| " android:layout_height=\"50dp\"\n" + |
| " android:background=\"#FF0\"/>\n" + |
| " <TextView\n" + |
| " android:layout_width=\"150dp\"\n" + |
| " android:layout_height=\"50dp\"\n" + |
| " android:background=\"#F00\"/>\n" + |
| " <LinearLayout\n" + |
| " android:layout_width=\"wrap_content\"\n" + |
| " android:layout_height=\"wrap_content\"\n" + |
| " android:paddingLeft=\"10dp\">\n" + |
| " <TextView\n" + |
| " android:layout_width=\"150dp\"\n" + |
| " android:layout_height=\"50dp\"\n" + |
| " android:background=\"#00F\"/>\n" + |
| " </LinearLayout>\n" + |
| " <LinearLayout\n" + |
| " android:layout_width=\"wrap_content\"\n" + |
| " android:layout_height=\"wrap_content\"\n" + |
| " android:layout_marginLeft=\"30dp\"\n" + |
| " android:layout_marginTop=\"15dp\">\n" + |
| " <TextView\n" + |
| " android:layout_width=\"150dp\"\n" + |
| " android:layout_height=\"50dp\"\n" + |
| " android:background=\"#F0F\"/>\n" + |
| " </LinearLayout>\n" + |
| "</LinearLayout>"; |
| |
| LayoutPullParser parser = LayoutPullParser.createFromString(layout); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .disableDecoration() |
| .build(); |
| |
| RenderResult result = RenderTestBase.render(sBridge, params, -1); |
| BufferedImage image = result.getImage(); |
| assertNotNull(image); |
| Graphics2D g = (Graphics2D) image.getGraphics(); |
| g.setStroke(new BasicStroke(8)); |
| for (ViewInfo vInfo : result.getSystemViews()) { |
| paintBorders(g, 0, 0, 0, vInfo); |
| } |
| |
| RenderTestBase.verify("view_boundaries.png", image); |
| } |
| |
| /** |
| * Test rendering of strings that have mixed RTL and LTR scripts. |
| * <p> |
| * http://b/37510906 |
| */ |
| @Test |
| public void testMixedRtlLtrRendering() throws Exception { |
| // |
| final String layout = |
| "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:layout_height=\"match_parent\"\n" + |
| " android:orientation=\"vertical\">\n" + "\n" + |
| " <TextView\n" + |
| " android:layout_width=\"wrap_content\"\n" + |
| " android:layout_height=\"wrap_content\"\n" + |
| " android:textSize=\"30sp\"\n" + |
| " android:background=\"#55FF0000\"\n" + |
| " android:text=\"این یک رشته ایرانی است\"/>\n" + |
| " <TextView\n" + |
| " android:layout_width=\"wrap_content\"\n" + |
| " android:layout_height=\"wrap_content\"\n" + |
| " android:textSize=\"30sp\"\n" + |
| " android:background=\"#55FF00FF\"\n" + |
| " android:text=\"این یک رشته ایرانی است(\"/>\n" + |
| " <TextView\n" + |
| " android:layout_width=\"wrap_content\"\n" + |
| " android:layout_height=\"wrap_content\"\n" + |
| " android:textSize=\"30sp\"\n" + |
| " android:background=\"#55FAF012\"\n" + |
| " android:text=\")(این یک رشته ایرانی است(\"/>\n" + |
| "</LinearLayout>"; |
| |
| LayoutPullParser parser = LayoutPullParser.createFromString(layout); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .disableDecoration() |
| .build(); |
| |
| renderAndVerify(params, "rtl_ltr.png", -1); |
| } |
| |
| @Test |
| public void testViewStub() throws Exception { |
| // |
| final String layout = |
| "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:layout_height=\"match_parent\"\n" + |
| " android:orientation=\"vertical\">\n" + "\n" + |
| " <ViewStub\n" + |
| " xmlns:tools=\"http://schemas.android.com/tools\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:layout_height=\"match_parent\"\n" + |
| " android:layout=\"@layout/four_corners\"\n" + |
| " tools:visibility=\"visible\" />" + |
| "</LinearLayout>"; |
| |
| // Create the layout pull parser. |
| LayoutPullParser parser = LayoutPullParser.createFromString(layout); |
| |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .disableDecoration() |
| .build(); |
| |
| renderAndVerify(params, "view_stub.png", -1); |
| } |
| |
| @Test |
| public void testImageResize() throws ClassNotFoundException { |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| layoutLibCallback.setUseShadow(false); |
| |
| LayoutPullParser parser = LayoutPullParser.createFromString( |
| "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:layout_height=\"match_parent\"\n" + |
| " android:background=\"@drawable/ninepatch\"\n" + |
| " android:layout_margin=\"20dp\"\n" + |
| " android:orientation=\"vertical\">\n\n" + |
| " <Button\n" + |
| " android:layout_width=\"wrap_content\"\n" + |
| " android:layout_height=\"wrap_content\"\n" + |
| " android:text=\"Button\" />\n\n" + |
| " <Button\n" + |
| " android:layout_width=\"wrap_content\"\n" + |
| " android:layout_height=\"wrap_content\"\n" + |
| " android:text=\"Button\" />\n" |
| + "</LinearLayout>"); |
| |
| // Ask for an image that it's 1/10th the size of the actual device image |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setImageFactory((width, height) -> |
| new BufferedImage(width / 10, height / 10, |
| BufferedImage.TYPE_INT_ARGB)) |
| .setFlag(RenderParamsFlags.FLAG_KEY_RESULT_IMAGE_AUTO_SCALE, true) |
| .build(); |
| |
| renderAndVerify(params, "auto-scale-image.png"); |
| } |
| |
| @Test |
| public void testTranslation() throws ClassNotFoundException, FileNotFoundException { |
| RenderResult res = renderAndVerify("translate_test.xml", "translate_test.png", false); |
| ViewInfo rootInfo = res.getRootViews().get(0); |
| ViewInfo buttonInfo = rootInfo.getChildren().get(0); |
| assertEquals(100, buttonInfo.getLeft()); |
| } |
| |
| /** |
| * Test a vector drawable that uses trimStart and trimEnd. It also tests all the primitives |
| * for vector drawables (lines, moves and cubic and quadratic curves). |
| */ |
| @Test |
| public void testCanvas() throws ClassNotFoundException { |
| // Create the layout pull parser. |
| LayoutPullParser parser = LayoutPullParser.createFromString( |
| "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:padding=\"16dp\"\n" + |
| " android:orientation=\"horizontal\"\n" + |
| " android:layout_width=\"fill_parent\"\n" + |
| " android:layout_height=\"fill_parent\">\n" + |
| " <com.android.layoutlib.test.myapplication.widgets.CanvasTestView\n" + |
| " android:layout_height=\"fill_parent\"\n" + |
| " android:layout_width=\"fill_parent\"\n" + |
| " android:src=\"@drawable/android\" />\n" + "\n" + |
| "</LinearLayout>"); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .disableDecoration() |
| .build(); |
| |
| renderAndVerify(params, "canvas.png", TimeUnit.SECONDS.toNanos(2)); |
| } |
| |
| @Test |
| public void testTypedArrays() throws ClassNotFoundException, FileNotFoundException { |
| renderAndVerify("typed_array.xml", "typed_arrays.png", false); |
| } |
| |
| /** |
| * Tests that the gradients are correctly displayed when using transparent colors |
| * and a wide range of offset values. |
| * <p/> |
| * http://b/112759140 |
| */ |
| @Test |
| public void testAnimatedVectorDrawableWithColorInterpolator() throws ClassNotFoundException { |
| // Create the layout pull parser. |
| LayoutPullParser parser = LayoutPullParser.createFromString( |
| "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:padding=\"16dp\"\n" + |
| " android:orientation=\"horizontal\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:layout_height=\"match_parent\">\n" + |
| " <ImageView\n" + |
| " android:layout_height=\"match_parent\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:src=\"@drawable/avd_color_interpolator\" />\n\n" + |
| "</LinearLayout>"); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .disableDecoration() |
| .build(); |
| |
| renderAndVerify(params, "color_interpolation.png", |
| TimeUnit.SECONDS.toNanos(2)); |
| } |
| |
| @Test |
| public void testManyLineBreaks() throws Exception { |
| String layout = |
| "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:layout_height=\"match_parent\">\n" + "\n" + |
| " <EditText\n" + |
| " android:layout_width=\"match_parent\"\n" + |
| " android:layout_height=\"wrap_content\"\n" + |
| " android:fallbackLineSpacing=\"true\"\n" + |
| " android:text=\"A very very very very very very very very very " + |
| "very very very very very very very very very very very very very very " + |
| "very very very very very very very very very very very very very very " + |
| "very very very very very very very very very very very very very very " + |
| "very very very very very very very very very very very very very very " + |
| "very very very very very very very very very very very very very very " + |
| "very very very very very very very very very very very very very very " + |
| "very very very very very very very very very very very very very very " + |
| "very very very very very very very very very very very very very very " + |
| "very very very very very very very very very very very very very very " + |
| "very very very very very very very very very very very very very very " + |
| "very very very very very very very very very very very very very very " + |
| "very very very very very very very long text\"/>\n" + |
| "</FrameLayout>"; |
| |
| LayoutPullParser parser = LayoutPullParser.createFromString(layout); |
| // Create LayoutLibCallback. |
| LayoutLibTestCallback layoutLibCallback = |
| new LayoutLibTestCallback(getLogger(), mDefaultClassLoader); |
| layoutLibCallback.initResources(); |
| |
| SessionParams params = getSessionParamsBuilder() |
| .setParser(parser) |
| .setCallback(layoutLibCallback) |
| .setTheme("Theme.Material.NoActionBar.Fullscreen", false) |
| .setRenderingMode(RenderingMode.V_SCROLL) |
| .disableDecoration() |
| .build(); |
| |
| renderAndVerify(params, "many_line_breaks.png", |
| TimeUnit.SECONDS.toNanos(2)); |
| } |
| |
| } |