1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.layoutlib.bridge.intensive;
18 
19 import com.android.ide.common.rendering.api.RenderSession;
20 import com.android.ide.common.rendering.api.ResourceNamespace;
21 import com.android.ide.common.rendering.api.ResourceReference;
22 import com.android.ide.common.rendering.api.ResourceValueImpl;
23 import com.android.ide.common.rendering.api.SessionParams;
24 import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
25 import com.android.ide.common.rendering.api.ViewInfo;
26 import com.android.ide.common.rendering.api.XmlParserFactory;
27 import com.android.internal.R;
28 import com.android.layoutlib.bridge.android.BridgeContext;
29 import com.android.layoutlib.bridge.android.RenderParamsFlags;
30 import com.android.layoutlib.bridge.impl.ParserFactory;
31 import com.android.layoutlib.bridge.impl.RenderAction;
32 import com.android.layoutlib.bridge.impl.RenderActionTestUtil;
33 import com.android.layoutlib.bridge.impl.ResourceHelper;
34 import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
35 import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback;
36 import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
37 import com.android.resources.Density;
38 import com.android.resources.Navigation;
39 import com.android.resources.ResourceType;
40 
41 import org.junit.After;
42 import org.junit.Test;
43 import org.kxml2.io.KXmlParser;
44 import org.xmlpull.v1.XmlPullParser;
45 
46 import android.R.attr;
47 import android.annotation.NonNull;
48 import android.annotation.Nullable;
49 import android.content.res.AssetManager;
50 import android.content.res.ColorStateList;
51 import android.content.res.Configuration;
52 import android.content.res.Resources;
53 import android.content.res.Resources_Delegate;
54 import android.graphics.Color;
55 import android.util.DisplayMetrics;
56 import android.util.StateSet;
57 import android.util.TypedValue;
58 
59 import java.awt.BasicStroke;
60 import java.awt.Graphics2D;
61 import java.awt.image.BufferedImage;
62 import java.io.File;
63 import java.io.FileNotFoundException;
64 import java.io.FileOutputStream;
65 import java.io.PrintWriter;
66 import java.lang.reflect.Field;
67 import java.util.concurrent.TimeUnit;
68 
69 import static org.junit.Assert.assertArrayEquals;
70 import static org.junit.Assert.assertEquals;
71 import static org.junit.Assert.assertFalse;
72 import static org.junit.Assert.assertNotEquals;
73 import static org.junit.Assert.assertNotNull;
74 import static org.junit.Assert.assertTrue;
75 
76 /**
77  * Set of render tests
78  */
79 public class RenderTests extends RenderTestBase {
80 
81     @After
afterTestCase()82     public void afterTestCase() {
83         com.android.layoutlib.bridge.test.widgets.HookWidget.reset();
84     }
85 
86     @Test
testActivity()87     public void testActivity() throws ClassNotFoundException, FileNotFoundException {
88         renderAndVerify("activity.xml", "activity.png", true);
89     }
90 
91     @Test
testActivityOnOldTheme()92     public void testActivityOnOldTheme() throws ClassNotFoundException, FileNotFoundException {
93         LayoutLibTestCallback layoutLibCallback =
94                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
95         layoutLibCallback.initResources();
96 
97         LayoutPullParser parser = LayoutPullParser.createFromString(
98                 "<RelativeLayout xmlns:android=\"http://schemas" +
99                 ".android.com/apk/res/android\"\n" +
100                 "                android:layout_width=\"match_parent\"\n" +
101                 "                android:layout_height=\"match_parent\"\n" +
102                 "                android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n" +
103                 "                android:paddingRight=\"@dimen/activity_horizontal_margin\"\n" +
104                 "                android:paddingTop=\"@dimen/activity_vertical_margin\"\n" +
105                 "                android:paddingBottom=\"@dimen/activity_vertical_margin\">\n" +
106                 "    <TextView\n" +
107                 "        android:text=\"@string/hello_world\"\n" +
108                 "        android:layout_width=\"wrap_content\"\n" +
109                 "        android:layout_height=\"200dp\"\n" +
110                 "        android:background=\"#FF0000\"\n" +
111                 "        android:id=\"@+id/text1\"/>\n" +
112                 "</RelativeLayout>");
113         SessionParams params = getSessionParamsBuilder()
114                 .setParser(parser)
115                 .setCallback(layoutLibCallback)
116                 .setTheme("Theme.NoTitleBar", false)
117                 .build();
118 
119         renderAndVerify(params, "simple_activity-old-theme.png");
120     }
121 
122     @Test
testTranslucentBars()123     public void testTranslucentBars() throws ClassNotFoundException, FileNotFoundException {
124         LayoutLibTestCallback layoutLibCallback =
125                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
126         layoutLibCallback.initResources();
127 
128         LayoutPullParser parser = createParserFromPath("four_corners.xml");
129         SessionParams params = getSessionParamsBuilder()
130                 .setParser(parser)
131                 .setCallback(layoutLibCallback)
132                 .setTheme("Theme.Material.Light.NoActionBar.TranslucentDecor", false)
133                 .build();
134         renderAndVerify(params, "four_corners_translucent.png");
135 
136         parser = createParserFromPath("four_corners.xml");
137         params = getSessionParamsBuilder()
138                 .setConfigGenerator(ConfigGenerator.NEXUS_5_LAND)
139                 .setParser(parser)
140                 .setCallback(layoutLibCallback)
141                 .setTheme("Theme.Material.Light.NoActionBar.TranslucentDecor", false)
142                 .build();
143         renderAndVerify(params, "four_corners_translucent_land.png");
144 
145         parser = createParserFromPath("four_corners.xml");
146         params = getSessionParamsBuilder()
147                     .setParser(parser)
148                     .setCallback(layoutLibCallback)
149                     .setTheme("Theme.Material.Light.NoActionBar", false)
150                     .build();
151         renderAndVerify(params, "four_corners.png");
152     }
153 
154     @Test
testAllWidgets()155     public void testAllWidgets() throws ClassNotFoundException, FileNotFoundException {
156         LayoutPullParser parser = createParserFromPath("allwidgets.xml");
157         LayoutLibTestCallback layoutLibCallback =
158                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
159         layoutLibCallback.initResources();
160         SessionParams params = getSessionParamsBuilder()
161                 .setParser(parser)
162                 .setConfigGenerator(ConfigGenerator.NEXUS_5)
163                 .setCallback(layoutLibCallback)
164                 .disableShadows()
165                 .build();
166 
167         renderAndVerify(params, "allwidgets.png");
168 
169         // We expect fidelity warnings for Path.isConvex. Fail for anything else.
170         sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
171     }
172 
173     @Test
testArrayCheck()174     public void testArrayCheck() throws ClassNotFoundException, FileNotFoundException {
175         renderAndVerify("array_check.xml", "array_check.png", false);
176 
177         // We expect fidelity warnings for Path.isConvex. Fail for anything else.
178         sRenderMessages.removeIf(
179                 message -> message.equals("Font$Builder.nAddAxis is not supported."));
180     }
181 
182     @Test
testAllWidgetsTablet()183     public void testAllWidgetsTablet() throws ClassNotFoundException, FileNotFoundException {
184         LayoutPullParser parser = createParserFromPath("allwidgets.xml");
185         LayoutLibTestCallback layoutLibCallback =
186                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
187         layoutLibCallback.initResources();
188         SessionParams params = getSessionParamsBuilder()
189                 .setParser(parser)
190                 .setConfigGenerator(ConfigGenerator.NEXUS_7_2012)
191                 .setCallback(layoutLibCallback)
192                 .disableShadows()
193                 .build();
194         renderAndVerify(params, "allwidgets_tab.png");
195 
196         // We expect fidelity warnings for Path.isConvex. Fail for anything else.
197         sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
198         sRenderMessages.removeIf(message -> message.equals("Font$Builder.nAddAxis is not supported."));
199     }
200 
201     @Test
testActivityActionBar()202     public void testActivityActionBar() throws ClassNotFoundException {
203         String simpleActivity =
204                 "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
205                 "                android:layout_width=\"match_parent\"\n" +
206                 "                android:layout_height=\"match_parent\"\n" +
207                 "                android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n" +
208                 "                android:paddingRight=\"@dimen/activity_horizontal_margin\"\n" +
209                 "                android:paddingTop=\"@dimen/activity_vertical_margin\"\n" +
210                 "                android:paddingBottom=\"@dimen/activity_vertical_margin\">\n" +
211                 "    <TextView\n" +
212                 "        android:text=\"@string/hello_world\"\n" +
213                 "        android:layout_width=\"wrap_content\"\n" +
214                 "        android:layout_height=\"200dp\"\n" +
215                 "        android:background=\"#FF0000\"\n" +
216                 "        android:id=\"@+id/text1\"/>\n" +
217                 "</RelativeLayout>";
218 
219         LayoutPullParser parser = LayoutPullParser.createFromString(simpleActivity);
220         LayoutLibTestCallback layoutLibCallback =
221                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
222         layoutLibCallback.initResources();
223 
224         SessionParams params = getSessionParamsBuilder()
225                 .setParser(parser)
226                 .setCallback(layoutLibCallback)
227                 .setTheme("Theme.Material.Light.NoActionBar", false)
228                 .setRenderingMode(RenderingMode.V_SCROLL)
229                 .build();
230 
231         renderAndVerify(params, "simple_activity_noactionbar.png");
232 
233         parser = LayoutPullParser.createFromString(simpleActivity);
234         params = getSessionParamsBuilder()
235                     .setParser(parser)
236                     .setCallback(layoutLibCallback)
237                     .setTheme("Theme.Material.Light", false)
238                     .setRenderingMode(RenderingMode.V_SCROLL)
239                     .build();
240 
241         renderAndVerify(params, "simple_activity.png");
242 
243         // This also tests that a theme with "NoActionBar" DOES HAVE an action bar when we are
244         // displaying menus.
245         parser = LayoutPullParser.createFromString(simpleActivity);
246         params = getSessionParamsBuilder()
247                     .setParser(parser)
248                     .setCallback(layoutLibCallback)
249                     .setTheme("Theme.Material.Light.NoActionBar", false)
250                     .setRenderingMode(RenderingMode.V_SCROLL)
251                     .build();
252         params.setFlag(RenderParamsFlags.FLAG_KEY_ROOT_TAG, "menu");
253         renderAndVerify(params, "simple_activity.png");
254     }
255 
256     @Test
testOnApplyInsetsCall()257     public void testOnApplyInsetsCall()
258             throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
259         // We get the widget via reflection to avoid IntelliJ complaining about the class being
260         // located in the wrong package. (From the Bridge tests point of view, it is)
261         Class insetsWidgetClass = Class.forName("com.android.layoutlib.test.myapplication.widgets" +
262                 ".InsetsWidget");
263         Field field = insetsWidgetClass.getDeclaredField("sApplyInsetsCalled");
264         assertFalse((Boolean)field.get(null));
265 
266         LayoutPullParser parser = LayoutPullParser.createFromString(
267                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
268                 "              android:padding=\"16dp\"\n" +
269                 "              android:orientation=\"horizontal\"\n" +
270                 "              android:layout_width=\"wrap_content\"\n" +
271                 "              android:layout_height=\"wrap_content\">\n" + "\n" +
272                 "    <com.android.layoutlib.test.myapplication.widgets.InsetsWidget\n" +
273                 "        android:text=\"Hello world\"\n" +
274                 "        android:layout_width=\"wrap_content\"\n" +
275                 "        android:layout_height=\"wrap_content\"\n" +
276                 "        android:id=\"@+id/text1\"/>\n" + "</LinearLayout>\n");
277         LayoutLibTestCallback layoutLibCallback =
278                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
279         layoutLibCallback.initResources();
280         SessionParams params = getSessionParamsBuilder()
281                 .setParser(parser)
282                 .setCallback(layoutLibCallback)
283                 .setTheme("Theme.Material.Light.NoActionBar", false)
284                 .build();
285 
286         render(sBridge, params, -1);
287 
288         assertTrue((Boolean)field.get(null));
289         field.set(null, false);
290     }
291 
292     /** Test expand_layout.xml */
293     @Test
testExpand()294     public void testExpand() throws ClassNotFoundException, FileNotFoundException {
295         // Create the layout pull parser.
296         LayoutPullParser parser = createParserFromPath("expand_vert_layout.xml");
297         // Create LayoutLibCallback.
298         LayoutLibTestCallback layoutLibCallback =
299                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
300         layoutLibCallback.initResources();
301 
302         ConfigGenerator customConfigGenerator = new ConfigGenerator()
303                 .setScreenWidth(300)
304                 .setScreenHeight(20)
305                 .setDensity(Density.XHIGH)
306                 .setNavigation(Navigation.NONAV);
307 
308         SessionParams params = getSessionParamsBuilder()
309                 .setParser(parser)
310                 .setConfigGenerator(customConfigGenerator)
311                 .setCallback(layoutLibCallback)
312                 .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
313                 .setRenderingMode(RenderingMode.V_SCROLL)
314                 .build();
315 
316         renderAndVerify(params, "expand_vert_layout.png");
317 
318         customConfigGenerator = new ConfigGenerator()
319                 .setScreenWidth(20)
320                 .setScreenHeight(300)
321                 .setDensity(Density.XHIGH)
322                 .setNavigation(Navigation.NONAV);
323         parser = createParserFromPath("expand_horz_layout.xml");
324         params = getSessionParamsBuilder()
325                     .setParser(parser)
326                     .setConfigGenerator(customConfigGenerator)
327                     .setCallback(layoutLibCallback)
328                     .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
329                     .setRenderingMode(RenderingMode.H_SCROLL)
330                     .build();
331 
332         renderAndVerify(params, "expand_horz_layout.png");
333     }
334 
335     @Test
testShrink()336     public void testShrink() throws ClassNotFoundException, FileNotFoundException {
337         // Create the layout pull parser.
338         LayoutPullParser parser = createParserFromPath("expand_vert_layout.xml");
339         // Create LayoutLibCallback.
340         LayoutLibTestCallback layoutLibCallback =
341                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
342         layoutLibCallback.initResources();
343         // Normal mode
344         ConfigGenerator customConfigGenerator = new ConfigGenerator()
345                 .setScreenWidth(600)
346                 .setScreenHeight(3000)
347                 .setDensity(Density.XHIGH)
348                 .setNavigation(Navigation.NONAV);
349         SessionParams params = getSessionParamsBuilder()
350                 .setParser(parser)
351                 .setConfigGenerator(customConfigGenerator)
352                 .setCallback(layoutLibCallback)
353                 .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
354                 .setRenderingMode(RenderingMode.NORMAL)
355                 .disableDecoration()
356                 .build();
357 
358         renderAndVerify(params, "normal_layout.png");
359 
360         // Shrink mode
361         customConfigGenerator = new ConfigGenerator()
362                 .setScreenWidth(600)
363                 .setScreenHeight(3000)
364                 .setDensity(Density.XHIGH)
365                 .setNavigation(Navigation.NONAV);
366         parser = createParserFromPath("expand_vert_layout.xml");
367         params = getSessionParamsBuilder()
368                 .setParser(parser)
369                 .setConfigGenerator(customConfigGenerator)
370                 .setCallback(layoutLibCallback)
371                 .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
372                 .setRenderingMode(RenderingMode.SHRINK)
373                 .disableDecoration()
374                 .build();
375 
376         renderAndVerify(params, "shrunk_layout.png");
377     }
378 
379     /** Test indeterminate_progressbar.xml */
380     @Test
testVectorAnimation()381     public void testVectorAnimation() throws ClassNotFoundException {
382         String layout = "\n" +
383                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
384                 "              android:padding=\"16dp\"\n" +
385                 "              android:orientation=\"horizontal\"\n" +
386                 "              android:layout_width=\"fill_parent\"\n" +
387                 "              android:layout_height=\"fill_parent\">\n" + "\n" +
388                 "    <ProgressBar\n" + "             android:layout_height=\"fill_parent\"\n" +
389                 "             android:layout_width=\"fill_parent\" />\n" + "\n" +
390                 "</LinearLayout>\n";
391 
392         // Create the layout pull parser.
393         LayoutPullParser parser = LayoutPullParser.createFromString(layout);
394         // Create LayoutLibCallback.
395         LayoutLibTestCallback layoutLibCallback =
396                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
397         layoutLibCallback.initResources();
398 
399         SessionParams params = getSessionParamsBuilder()
400                 .setParser(parser)
401                 .setCallback(layoutLibCallback)
402                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
403                 .setRenderingMode(RenderingMode.V_SCROLL)
404                 .build();
405 
406         renderAndVerify(params, "animated_vector.png", TimeUnit.SECONDS.toNanos(2));
407 
408         parser = LayoutPullParser.createFromString(layout);
409         params = getSessionParamsBuilder()
410                     .setParser(parser)
411                     .setCallback(layoutLibCallback)
412                     .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
413                     .setRenderingMode(RenderingMode.V_SCROLL)
414                     .build();
415         renderAndVerify(params, "animated_vector_1.png", TimeUnit.SECONDS.toNanos(3));
416     }
417 
418     /**
419      * Test a vector drawable that uses trimStart and trimEnd. It also tests all the primitives
420      * for vector drawables (lines, moves and cubic and quadratic curves).
421      */
422     @Test
testVectorDrawable()423     public void testVectorDrawable() throws ClassNotFoundException {
424         // Create the layout pull parser.
425         LayoutPullParser parser = LayoutPullParser.createFromString(
426                "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
427                        "              android:padding=\"16dp\"\n" +
428                        "              android:orientation=\"horizontal\"\n" +
429                        "              android:layout_width=\"fill_parent\"\n" +
430                        "              android:layout_height=\"fill_parent\">\n" +
431                        "    <ImageView\n" +
432                        "             android:layout_height=\"fill_parent\"\n" +
433                        "             android:layout_width=\"fill_parent\"\n" +
434                        "             android:src=\"@drawable/multi_path\" />\n" + "\n" +
435                        "</LinearLayout>");
436         // Create LayoutLibCallback.
437         LayoutLibTestCallback layoutLibCallback =
438                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
439         layoutLibCallback.initResources();
440 
441         SessionParams params = getSessionParamsBuilder()
442                 .setParser(parser)
443                 .setCallback(layoutLibCallback)
444                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
445                 .disableDecoration()
446                 .setRenderingMode(RenderingMode.V_SCROLL)
447                 .build();
448 
449         renderAndVerify(params, "vector_drawable.png", TimeUnit.SECONDS.toNanos(2));
450     }
451 
452     /**
453      * Regression test for http://b.android.com/91383 and http://b.android.com/203797
454      */
455     @Test
testVectorDrawable91383()456     public void testVectorDrawable91383() throws ClassNotFoundException {
457         // Create the layout pull parser.
458         LayoutPullParser parser = LayoutPullParser.createFromString(
459                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
460                         "              android:padding=\"16dp\"\n" +
461                         "              android:orientation=\"vertical\"\n" +
462                         "              android:layout_width=\"fill_parent\"\n" +
463                         "              android:layout_height=\"fill_parent\">\n" +
464                         "    <ImageView\n" +
465                         "        android:layout_height=\"wrap_content\"\n" +
466                         "        android:layout_width=\"wrap_content\"\n" +
467                         "        android:src=\"@drawable/android\"/>\n" +
468                         "    <ImageView\n" +
469                         "        android:layout_height=\"wrap_content\"\n" +
470                         "        android:layout_width=\"wrap_content\"\n" +
471                         "        android:src=\"@drawable/headset\"/>\n" +
472                         "    <ImageView\n" +
473                         "        android:layout_height=\"wrap_content\"\n" +
474                         "        android:layout_width=\"wrap_content\"\n" +
475                         "        android:src=\"@drawable/clipped_even_odd\"/>\n" +
476                         "</LinearLayout>");
477         // Create LayoutLibCallback.
478         LayoutLibTestCallback layoutLibCallback =
479                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
480         layoutLibCallback.initResources();
481 
482         SessionParams params = getSessionParamsBuilder()
483                 .setParser(parser)
484                 .setCallback(layoutLibCallback)
485                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
486                 .setRenderingMode(RenderingMode.V_SCROLL)
487                 .build();
488 
489         renderAndVerify(params, "vector_drawable_91383.png", TimeUnit.SECONDS.toNanos(2));
490     }
491 
492     /**
493      * Test a ImageView which has a vector drawable as its src and tint attribute.
494      */
495     @Test
testVectorDrawableWithTintInImageView()496     public void testVectorDrawableWithTintInImageView() throws ClassNotFoundException {
497         // Create the layout pull parser.
498         LayoutPullParser parser = LayoutPullParser.createFromString(
499                 "<ImageView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
500                         "    android:layout_height=\"match_parent\"\n" +
501                         "    android:layout_width=\"match_parent\"\n" +
502                         "    android:src=\"@drawable/vector_drawable_without_tint\"\n" +
503                         "    android:tint=\"#FF00FF00\" />");
504         // Create LayoutLibCallback.
505         LayoutLibTestCallback layoutLibCallback =
506                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
507         layoutLibCallback.initResources();
508 
509         SessionParams params = getSessionParamsBuilder()
510                 .setParser(parser)
511                 .setCallback(layoutLibCallback)
512                 .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
513                 .setRenderingMode(RenderingMode.V_SCROLL)
514                 .build();
515 
516         renderAndVerify(params, "vector_drawable_with_tint_in_image_view.png",
517                 TimeUnit.SECONDS.toNanos(2));
518     }
519 
520     /**
521      * Test a vector drawable which has tint attribute.
522      */
523     @Test
testVectorDrawableWithTintInItself()524     public void testVectorDrawableWithTintInItself() throws ClassNotFoundException {
525         // Create the layout pull parser.
526         LayoutPullParser parser = LayoutPullParser.createFromString(
527                 "<ImageView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
528                         "    android:layout_height=\"match_parent\"\n" +
529                         "    android:layout_width=\"match_parent\"\n" +
530                         "    android:src=\"@drawable/vector_drawable_with_tint\" />");
531         // Create LayoutLibCallback.
532         LayoutLibTestCallback layoutLibCallback =
533                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
534         layoutLibCallback.initResources();
535 
536         SessionParams params = getSessionParamsBuilder()
537                 .setParser(parser)
538                 .setCallback(layoutLibCallback)
539                 .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
540                 .setRenderingMode(RenderingMode.V_SCROLL)
541                 .build();
542 
543         renderAndVerify(params, "vector_drawable_with_tint_itself.png",
544                 TimeUnit.SECONDS.toNanos(2));
545     }
546 
547     /**
548      * Test a vector drawable that uses trimStart and trimEnd. It also tests all the primitives
549      * for vector drawables (lines, moves and cubic and quadratic curves).
550      */
551     @Test
testVectorDrawableHasMultipleLineInPathData()552     public void testVectorDrawableHasMultipleLineInPathData() throws ClassNotFoundException {
553         // Create the layout pull parser.
554         LayoutPullParser parser = LayoutPullParser.createFromString(
555                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
556                         "              android:padding=\"16dp\"\n" +
557                         "              android:orientation=\"horizontal\"\n" +
558                         "              android:layout_width=\"match_parent\"\n" +
559                         "              android:layout_height=\"match_parent\">\n" +
560                         "    <ImageView\n" +
561                         "             android:layout_height=\"match_parent\"\n" +
562                         "             android:layout_width=\"match_parent\"\n" +
563                         "             android:src=\"@drawable/multi_line_of_path_data\" />\n\n" +
564                         "</LinearLayout>");
565         // Create LayoutLibCallback.
566         LayoutLibTestCallback layoutLibCallback =
567                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
568         layoutLibCallback.initResources();
569 
570         SessionParams params = getSessionParamsBuilder()
571                 .setParser(parser)
572                 .setCallback(layoutLibCallback)
573                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
574                 .setRenderingMode(RenderingMode.V_SCROLL)
575                 .build();
576 
577         renderAndVerify(params, "vector_drawable_multi_line_of_path_data.png",
578                 TimeUnit.SECONDS.toNanos(2));
579     }
580 
581     /**
582      * Tests that the gradients are correctly transformed using the viewport of the vector drawable.
583      * <p/>
584      * If a vector drawable is 50x50 and the gradient has startX=25 and startY=25, the gradient
585      * will start in the middle of the box.
586      * <p/>
587      * http://b/65495452
588      */
589     @Test
testVectorDrawableGradient()590     public void testVectorDrawableGradient() throws ClassNotFoundException {
591         // Create the layout pull parser.
592         LayoutPullParser parser = LayoutPullParser.createFromString(
593                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
594                         "              android:padding=\"16dp\"\n" +
595                         "              android:orientation=\"horizontal\"\n" +
596                         "              android:layout_width=\"match_parent\"\n" +
597                         "              android:layout_height=\"match_parent\">\n" +
598                         "    <ImageView\n" +
599                         "             android:layout_height=\"match_parent\"\n" +
600                         "             android:layout_width=\"match_parent\"\n" +
601                         "             android:src=\"@drawable/shadow\" />\n\n" +
602                         "</LinearLayout>");
603         // Create LayoutLibCallback.
604         LayoutLibTestCallback layoutLibCallback =
605                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
606         layoutLibCallback.initResources();
607 
608         SessionParams params = getSessionParamsBuilder()
609                 .setParser(parser)
610                 .setCallback(layoutLibCallback)
611                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
612                 .setRenderingMode(RenderingMode.V_SCROLL)
613                 .build();
614 
615         renderAndVerify(params, "vector_drawable_gradient.png",
616                 TimeUnit.SECONDS.toNanos(2));
617     }
618 
619     /**
620      * Tests that the radial gradients are correctly transformed using the viewport of the vector
621      * drawable.
622      * <p/>
623      * http://b/66168608
624      */
625     @Test
testVectorDrawableRadialGradient()626     public void testVectorDrawableRadialGradient() throws ClassNotFoundException {
627         // Create the layout pull parser.
628         LayoutPullParser parser = LayoutPullParser.createFromString(
629                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
630                         "              android:padding=\"16dp\"\n" +
631                         "              android:orientation=\"horizontal\"\n" +
632                         "              android:layout_width=\"match_parent\"\n" +
633                         "              android:layout_height=\"match_parent\">\n" +
634                         "    <ImageView\n" +
635                         "             android:layout_height=\"match_parent\"\n" +
636                         "             android:layout_width=\"match_parent\"\n" +
637                         "             android:src=\"@drawable/radial_gradient\" />\n\n" +
638                         "</LinearLayout>");
639         // Create LayoutLibCallback.
640         LayoutLibTestCallback layoutLibCallback =
641                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
642         layoutLibCallback.initResources();
643 
644         SessionParams params = getSessionParamsBuilder()
645                 .setParser(parser)
646                 .setCallback(layoutLibCallback)
647                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
648                 .setRenderingMode(RenderingMode.V_SCROLL)
649                 .build();
650 
651         renderAndVerify(params, "vector_drawable_radial_gradient.png",
652                 TimeUnit.SECONDS.toNanos(2));
653     }
654 
655     /**
656      * Tests that the gradients are correctly displayed when using transparent colors
657      * and a wide range of offset values.
658      * <p/>
659      * http://b/112759140
660      */
661     @Test
testGradientColors()662     public void testGradientColors() throws ClassNotFoundException {
663         // Create the layout pull parser.
664         LayoutPullParser parser = LayoutPullParser.createFromString(
665                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
666                         "              android:padding=\"16dp\"\n" +
667                         "              android:orientation=\"horizontal\"\n" +
668                         "              android:layout_width=\"match_parent\"\n" +
669                         "              android:layout_height=\"match_parent\">\n" +
670                         "    <ImageView\n" +
671                         "             android:layout_height=\"match_parent\"\n" +
672                         "             android:layout_width=\"match_parent\"\n" +
673                         "             android:src=\"@drawable/gradient\" />\n\n" +
674                         "</LinearLayout>");
675         // Create LayoutLibCallback.
676         LayoutLibTestCallback layoutLibCallback =
677                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
678         layoutLibCallback.initResources();
679 
680         SessionParams params = getSessionParamsBuilder()
681                 .setParser(parser)
682                 .setCallback(layoutLibCallback)
683                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
684                 .setRenderingMode(RenderingMode.V_SCROLL)
685                 .disableDecoration()
686                 .build();
687 
688         renderAndVerify(params, "gradient_colors.png",
689                 TimeUnit.SECONDS.toNanos(2));
690     }
691 
692     /**
693      * Tests that the gradients are correctly combined with alpha values.
694      * <p/>
695      * http://b/122260583
696      */
697     @Test
testGradientAlphaDrawable()698     public void testGradientAlphaDrawable() throws ClassNotFoundException {
699         // Create the layout pull parser.
700         LayoutPullParser parser = LayoutPullParser.createFromString(
701                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
702                         "              android:padding=\"16dp\"\n" +
703                         "              android:orientation=\"horizontal\"\n" +
704                         "              android:layout_width=\"match_parent\"\n" +
705                         "              android:layout_height=\"match_parent\">\n" +
706                         "    <ImageView\n" +
707                         "             android:layout_height=\"match_parent\"\n" +
708                         "             android:layout_width=\"match_parent\"\n" +
709                         "             android:src=\"@drawable/vector_gradient_alpha\" />\n\n" +
710                         "</LinearLayout>");
711         // Create LayoutLibCallback.
712         LayoutLibTestCallback layoutLibCallback =
713                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
714         layoutLibCallback.initResources();
715 
716         SessionParams params = getSessionParamsBuilder()
717                 .setParser(parser)
718                 .setCallback(layoutLibCallback)
719                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
720                 .setRenderingMode(RenderingMode.V_SCROLL)
721                 .disableDecoration()
722                 .build();
723 
724         renderAndVerify(params, "gradient_alpha_drawable.png",
725                 TimeUnit.SECONDS.toNanos(2));
726     }
727 
728     /** Test activity.xml */
729     @Test
testScrollingAndMeasure()730     public void testScrollingAndMeasure() throws ClassNotFoundException, FileNotFoundException {
731         // Create the layout pull parser.
732         LayoutPullParser parser = createParserFromPath("scrolled.xml");
733         // Create LayoutLibCallback.
734         LayoutLibTestCallback layoutLibCallback =
735                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
736         layoutLibCallback.initResources();
737 
738         SessionParams params = getSessionParamsBuilder()
739                 .setParser(parser)
740                 .setCallback(layoutLibCallback)
741                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
742                 .setRenderingMode(RenderingMode.V_SCROLL)
743                 .disableDecoration()
744                 .build();
745         params.setExtendedViewInfoMode(true);
746 
747         // Do an only-measure pass
748         RenderSession session = sBridge.createSession(params);
749         session.measure();
750         RenderResult result = RenderResult.getFromSession(session);
751         assertNotNull(result);
752         assertNotNull(result.getResult());
753         assertTrue(result.getResult().isSuccess());
754 
755         ViewInfo rootLayout = result.getRootViews().get(0);
756         // Check the first box in the main LinearLayout
757         assertEquals(-90, rootLayout.getChildren().get(0).getTop());
758         assertEquals(-30, rootLayout.getChildren().get(0).getLeft());
759         assertEquals(90, rootLayout.getChildren().get(0).getBottom());
760         assertEquals(150, rootLayout.getChildren().get(0).getRight());
761 
762         // Check the first box within the nested LinearLayout
763         assertEquals(-450, rootLayout.getChildren().get(5).getChildren().get(0).getTop());
764         assertEquals(90, rootLayout.getChildren().get(5).getChildren().get(0).getLeft());
765         assertEquals(-270, rootLayout.getChildren().get(5).getChildren().get(0).getBottom());
766         assertEquals(690, rootLayout.getChildren().get(5).getChildren().get(0).getRight());
767 
768         // Do a full render pass
769         parser = createParserFromPath("scrolled.xml");
770 
771         params = getSessionParamsBuilder()
772                     .setParser(parser)
773                     .setCallback(layoutLibCallback)
774                     .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
775                     .setRenderingMode(RenderingMode.V_SCROLL)
776                     .disableDecoration()
777                     .build();
778         params.setExtendedViewInfoMode(true);
779 
780         result = renderAndVerify(params, "scrolled.png");
781         assertNotNull(result);
782         assertNotNull(result.getResult());
783         assertTrue(result.getResult().isSuccess());
784     }
785 
786     @Test
testGetResourceNameVariants()787     public void testGetResourceNameVariants() throws Exception {
788         // Setup
789         // Create the layout pull parser for our resources (empty.xml can not be part of the test
790         // app as it won't compile).
791         LayoutPullParser parser = LayoutPullParser.createFromPath("/empty.xml");
792         // Create LayoutLibCallback.
793         LayoutLibTestCallback layoutLibCallback =
794                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
795         layoutLibCallback.initResources();
796         SessionParams params = getSessionParamsBuilder()
797                 .setParser(parser)
798                 .setConfigGenerator(ConfigGenerator.NEXUS_4)
799                 .setCallback(layoutLibCallback)
800                 .build();
801         AssetManager assetManager = AssetManager.getSystem();
802         DisplayMetrics metrics = new DisplayMetrics();
803         Configuration configuration = RenderAction.getConfiguration(params);
804         BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
805                 params.getAssets(), params.getLayoutlibCallback(), configuration,
806                 params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
807         Resources resources = Resources_Delegate.initSystem(context, assetManager, metrics,
808                 configuration, params.getLayoutlibCallback());
809         // Test
810         assertEquals("android:style/ButtonBar",
811                 resources.getResourceName(android.R.style.ButtonBar));
812         assertEquals("android", resources.getResourcePackageName(android.R.style.ButtonBar));
813         assertEquals("ButtonBar", resources.getResourceEntryName(android.R.style.ButtonBar));
814         assertEquals("style", resources.getResourceTypeName(android.R.style.ButtonBar));
815         Integer id = Resources_Delegate.getLayoutlibCallback(resources).getOrGenerateResourceId(
816                 new ResourceReference(ResourceNamespace.RES_AUTO, ResourceType.STRING, "app_name"));
817         assertNotNull(id);
818         assertEquals("com.android.layoutlib.test.myapplication:string/app_name",
819                 resources.getResourceName(id));
820         assertEquals("com.android.layoutlib.test.myapplication",
821                 resources.getResourcePackageName(id));
822         assertEquals("string", resources.getResourceTypeName(id));
823         assertEquals("app_name", resources.getResourceEntryName(id));
824 
825         context.disposeResources();
826     }
827 
828     @Test
testStringEscaping()829     public void testStringEscaping() throws Exception {
830         // Setup
831         // Create the layout pull parser for our resources (empty.xml can not be part of the test
832         // app as it won't compile).
833         LayoutPullParser parser = LayoutPullParser.createFromPath("/empty.xml");
834         // Create LayoutLibCallback.
835         LayoutLibTestCallback layoutLibCallback =
836                 new LayoutLibTestCallback(RenderTestBase.getLogger(), mDefaultClassLoader);
837         layoutLibCallback.initResources();
838         SessionParams params = getSessionParamsBuilder()
839                 .setConfigGenerator(ConfigGenerator.NEXUS_4)
840                 .setParser(parser)
841                 .setCallback(layoutLibCallback)
842                 .build();
843         AssetManager assetManager = AssetManager.getSystem();
844         DisplayMetrics metrics = new DisplayMetrics();
845         Configuration configuration = RenderAction.getConfiguration(params);
846         BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
847                 params.getAssets(), params.getLayoutlibCallback(), configuration,
848                 params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
849         Resources resources = Resources_Delegate.initSystem(context, assetManager, metrics,
850                 configuration, params.getLayoutlibCallback());
851 
852         Integer id =
853                 Resources_Delegate.getLayoutlibCallback(resources)
854                         .getOrGenerateResourceId(
855                                 new ResourceReference(
856                                         ResourceNamespace.RES_AUTO,
857                                         ResourceType.ARRAY,
858                                         "string_array"));
859         assertNotNull(id);
860         String[] strings = resources.getStringArray(id);
861         assertArrayEquals(
862                 new String[]{"mystring", "Hello world!", "candidates", "Unknown", "?EC"},
863                 strings);
864         assertTrue(sRenderMessages.isEmpty());
865 
866         context.disposeResources();
867     }
868 
869     @Test
testFonts()870     public void testFonts() throws ClassNotFoundException, FileNotFoundException {
871         // TODO: styles seem to be broken in TextView
872         renderAndVerify("fonts_test.xml", "font_test.png", false);
873         sRenderMessages.removeIf(
874                 message -> message.equals("Font$Builder.nAddAxis is not supported."));
875     }
876 
877     @Test
testAdaptiveIcon()878     public void testAdaptiveIcon() throws ClassNotFoundException, FileNotFoundException {
879         // Create the layout pull parser.
880         String layout =
881                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
882                         "              android:padding=\"16dp\"\n" +
883                         "              android:orientation=\"horizontal\"\n" +
884                         "              android:layout_width=\"fill_parent\"\n" +
885                         "              android:layout_height=\"fill_parent\">\n" +
886                         "    <ImageView\n" +
887                         "             android:layout_height=\"wrap_content\"\n" +
888                         "             android:layout_width=\"wrap_content\"\n" +
889                         "             android:src=\"@drawable/adaptive\" />\n" +
890                         "</LinearLayout>\n";
891         LayoutPullParser parser = LayoutPullParser.createFromString(layout);
892         // Create LayoutLibCallback.
893         LayoutLibTestCallback layoutLibCallback =
894                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
895         layoutLibCallback.initResources();
896 
897         layoutLibCallback.setAdaptiveIconMaskPath("M50,0L100,0 100,100 0,100 0,0z");
898         SessionParams params = getSessionParamsBuilder()
899                 .setParser(parser)
900                 .setCallback(layoutLibCallback)
901                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
902                 .setRenderingMode(RenderingMode.V_SCROLL)
903                 .build();
904 
905         renderAndVerify(params, "adaptive_icon.png");
906 
907         layoutLibCallback.setAdaptiveIconMaskPath(
908                 "M50 0C77.6 0 100 22.4 100 50C100 77.6 77.6 100 50 100C22.4 100 0 77.6 0 50C0 " +
909                         "22.4 22.4 0 50 0Z");
910         params = getSessionParamsBuilder()
911                     .setParser(LayoutPullParser.createFromString(layout))
912                     .setCallback(layoutLibCallback)
913                     .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
914                     .setRenderingMode(RenderingMode.V_SCROLL)
915                     .build();
916         renderAndVerify(params, "adaptive_icon_circle.png");
917 
918         layoutLibCallback.setAdaptiveIconMaskPath(
919                 "M50,0L92,0C96.42,0 100,4.58 100 8L100,92C100, 96.42 96.42 100 92 100L8 100C4.58," +
920                         " 100 0 96.42 0 92L0 8 C 0 4.42 4.42 0 8 0L50 0Z");
921         params = getSessionParamsBuilder()
922                     .setParser(LayoutPullParser.createFromString(layout))
923                     .setCallback(layoutLibCallback)
924                     .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
925                     .setRenderingMode(RenderingMode.V_SCROLL)
926                     .build();
927         renderAndVerify(params, "adaptive_icon_rounded_corners.png");
928 
929         layoutLibCallback.setAdaptiveIconMaskPath(
930                 "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");
931         params = getSessionParamsBuilder()
932                     .setParser(LayoutPullParser.createFromString(layout))
933                     .setCallback(layoutLibCallback)
934                     .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
935                     .setRenderingMode(RenderingMode.V_SCROLL)
936                     .build();
937         renderAndVerify(params, "adaptive_icon_squircle.png");
938     }
939 
940     @Test
testTypedValue()941     public void testTypedValue() throws Exception {
942         // Setup
943         // Create the layout pull parser for our resources (empty.xml can not be part of the test
944         // app as it won't compile).
945         LayoutPullParser parser = LayoutPullParser.createFromPath("/empty.xml");
946         // Create LayoutLibCallback.
947         LayoutLibTestCallback layoutLibCallback =
948                 new LayoutLibTestCallback(RenderTestBase.getLogger(), mDefaultClassLoader);
949         layoutLibCallback.initResources();
950         SessionParams params = getSessionParamsBuilder()
951                 .setConfigGenerator(ConfigGenerator.NEXUS_4)
952                 .setParser(parser)
953                 .setCallback(layoutLibCallback)
954                 .build();
955         DisplayMetrics metrics = new DisplayMetrics();
956         Configuration configuration = RenderAction.getConfiguration(params);
957 
958         BridgeContext mContext =
959                 new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
960                         params.getAssets(), params.getLayoutlibCallback(), configuration,
961                         params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
962 
963         TypedValue outValue = new TypedValue();
964         mContext.resolveThemeAttribute(android.R.attr.colorPrimary, outValue, true);
965         assertEquals(TypedValue.TYPE_INT_COLOR_ARGB8, outValue.type);
966         assertNotEquals(0, outValue.data);
967 
968         outValue = new TypedValue();
969         mContext.resolveThemeAttribute(android.R.attr.colorError, outValue, true);
970         assertEquals(TypedValue.TYPE_INT_COLOR_RGB4, outValue.type);
971         assertEquals(-65536, outValue.data);
972 
973         outValue = new TypedValue();
974         mContext.resolveThemeAttribute(attr.colorActivatedHighlight, outValue, true);
975         assertEquals(TypedValue.TYPE_INT_COLOR_ARGB4, outValue.type);
976         assertEquals(-872349952, outValue.data);
977 
978         outValue = new TypedValue();
979         mContext.resolveThemeAttribute(android.R.attr.isLightTheme, outValue, true);
980         assertEquals(TypedValue.TYPE_INT_BOOLEAN, outValue.type);
981         assertEquals(1, outValue.data);
982         assertTrue(sRenderMessages.isEmpty());
983     }
984 
985     @Test
testColorStateList()986     public void testColorStateList() throws Exception {
987         final String STATE_LIST = "<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n" +
988                 "    <item android:state_pressed=\"true\"\n" +
989                 "          android:color=\"?android:attr/colorForeground\"/> \n" +
990                 "    <item android:state_focused=\"true\"\n" +
991                 "          android:color=\"?android:attr/colorBackground\"/> \n" +
992                 "    <item android:color=\"#a000\"/> <!-- default -->\n" + "</selector>";
993 
994         File tmpColorList = File.createTempFile("statelist", "xml");
995         try(PrintWriter output = new PrintWriter(new FileOutputStream(tmpColorList))) {
996             output.println(STATE_LIST);
997         }
998 
999         // Setup
1000         // Create the layout pull parser for our resources (empty.xml can not be part of the test
1001         // app as it won't compile).
1002         ParserFactory.setParserFactory(new XmlParserFactory() {
1003             @Override
1004             @Nullable
1005             public XmlPullParser createXmlParserForPsiFile(@NonNull String fileName) {
1006                 return null;
1007             }
1008 
1009             @Override
1010             @Nullable
1011             public XmlPullParser createXmlParserForFile(@NonNull String fileName) {
1012                 return null;
1013             }
1014 
1015             @Override
1016             @NonNull
1017             public XmlPullParser createXmlParser() {
1018                 return new KXmlParser();
1019             }
1020         });
1021 
1022         LayoutPullParser parser = LayoutPullParser.createFromPath("/empty.xml");
1023         // Create LayoutLibCallback.
1024         LayoutLibTestCallback layoutLibCallback =
1025                 new LayoutLibTestCallback(RenderTestBase.getLogger(), mDefaultClassLoader);
1026         layoutLibCallback.initResources();
1027         SessionParams params = getSessionParamsBuilder()
1028                 .setConfigGenerator(ConfigGenerator.NEXUS_4)
1029                 .setParser(parser)
1030                 .setCallback(layoutLibCallback)
1031                 .setTheme("Theme.Material", false)
1032                 .build();
1033         DisplayMetrics metrics = new DisplayMetrics();
1034         Configuration configuration = RenderAction.getConfiguration(params);
1035 
1036         BridgeContext mContext =
1037                 new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
1038                         params.getAssets(), params.getLayoutlibCallback(), configuration,
1039                         params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
1040         mContext.initResources();
1041         BridgeContext oldContext = RenderActionTestUtil.setBridgeContext(mContext);
1042 
1043         try {
1044             ColorStateList stateList = ResourceHelper.getColorStateList(
1045                     new ResourceValueImpl(
1046                             ResourceNamespace.RES_AUTO,
1047                             ResourceType.COLOR,
1048                             "test_list",
1049                             tmpColorList.getAbsolutePath()),
1050                     mContext,
1051                     null);
1052             assertNotNull(stateList);
1053             assertEquals(Color.parseColor("#ffffffff"), stateList.getColorForState(
1054                     StateSet.get(StateSet.VIEW_STATE_PRESSED),
1055                     0
1056             ));
1057             assertEquals(Color.parseColor("#ff303030"), stateList.getColorForState(
1058                     StateSet.get(StateSet.VIEW_STATE_FOCUSED),
1059                     0
1060             ));
1061             assertEquals(Color.parseColor("#AA000000"), stateList.getDefaultColor());
1062 
1063             // Now apply theme overlay and check the colors changed
1064             Resources.Theme theme = mContext.getResources().newTheme();
1065             theme.applyStyle(R.style.ThemeOverlay_Material_Light, true);
1066             stateList = ResourceHelper.getColorStateList(
1067                     new ResourceValueImpl(
1068                             ResourceNamespace.RES_AUTO,
1069                             ResourceType.COLOR,
1070                             "test_list",
1071                             tmpColorList.getAbsolutePath()),
1072                     mContext,
1073                     theme);
1074             assertNotNull(stateList);
1075             assertEquals(Color.parseColor("#ff000000"), stateList.getColorForState(
1076                     StateSet.get(StateSet.VIEW_STATE_PRESSED),
1077                     0
1078             ));
1079             assertEquals(Color.parseColor("#fffafafa"), stateList.getColorForState(
1080                     StateSet.get(StateSet.VIEW_STATE_FOCUSED),
1081                     0
1082             ));
1083             assertEquals(Color.parseColor("#AA000000"), stateList.getDefaultColor());
1084         } finally {
1085             RenderActionTestUtil.setBridgeContext(oldContext);
1086         }
1087         mContext.disposeResources();
1088     }
1089 
1090     @Test
testShadowFlagsNoShadows()1091     public void testShadowFlagsNoShadows() throws Exception {
1092         LayoutPullParser parser = createParserFromPath("shadows_test.xml");
1093         LayoutLibTestCallback layoutLibCallback =
1094                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
1095         layoutLibCallback.initResources();
1096         SessionParams params = getSessionParamsBuilder()
1097                 .setParser(parser)
1098                 .setConfigGenerator(ConfigGenerator.NEXUS_5)
1099                 .setCallback(layoutLibCallback)
1100                 .disableDecoration()
1101                 .disableShadows()
1102                 .build();
1103 
1104         renderAndVerify(params, "shadows_test_no_shadow.png");
1105         // We expect fidelity warnings for Path.isConvex. Fail for anything else.
1106         sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
1107     }
1108 
1109     @Test
testRectangleShadow()1110     public void testRectangleShadow() throws Exception {
1111         LayoutPullParser parser = createParserFromPath("shadows_test.xml");
1112         LayoutLibTestCallback layoutLibCallback =
1113                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
1114         layoutLibCallback.initResources();
1115         SessionParams params = getSessionParamsBuilder()
1116                 .setParser(parser)
1117                 .setConfigGenerator(ConfigGenerator.NEXUS_5)
1118                 .setCallback(layoutLibCallback)
1119                 .disableDecoration()
1120                 .disableHighQualityShadows()
1121                 .build();
1122 
1123         renderAndVerify(params, "shadows_test.png");
1124         // We expect fidelity warnings for Path.isConvex. Fail for anything else.
1125         sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
1126     }
1127 
1128     @Test
testResourcesGetIdentifier()1129     public void testResourcesGetIdentifier() throws Exception {
1130         // Setup
1131         // Create the layout pull parser for our resources (empty.xml can not be part of the test
1132         // app as it won't compile).
1133         LayoutPullParser parser = LayoutPullParser.createFromPath("/empty.xml");
1134         // Create LayoutLibCallback.
1135         LayoutLibTestCallback layoutLibCallback =
1136                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
1137         layoutLibCallback.initResources();
1138         SessionParams params = getSessionParamsBuilder()
1139                 .setConfigGenerator(ConfigGenerator.NEXUS_4)
1140                 .setParser(parser)
1141                 .setCallback(layoutLibCallback)
1142                 .build();
1143         AssetManager assetManager = AssetManager.getSystem();
1144         DisplayMetrics metrics = new DisplayMetrics();
1145         Configuration configuration = RenderAction.getConfiguration(params);
1146         BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
1147                 params.getAssets(), params.getLayoutlibCallback(), configuration,
1148                 params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
1149         Resources resources = Resources_Delegate.initSystem(context, assetManager, metrics,
1150                 configuration, params.getLayoutlibCallback());
1151         Integer id =
1152                 Resources_Delegate.getLayoutlibCallback(resources)
1153                         .getOrGenerateResourceId(
1154                                 new ResourceReference(
1155                                         ResourceNamespace.RES_AUTO,
1156                                         ResourceType.STRING,
1157                                         "app_name"));
1158         assertNotNull(id);
1159         assertEquals(id.intValue(),
1160                 resources.getIdentifier("com.android.layoutlib.test.myapplication:string/app_name",
1161                         null, null));
1162         assertEquals(id.intValue(), resources.getIdentifier("app_name", "string",
1163                 "com.android.layoutlib.test.myapplication"));
1164         assertEquals(0, resources.getIdentifier("string/app_name", null, null));
1165         assertEquals(0, resources.getIdentifier("string/app_name", null, "com.foo.bar"));
1166         assertEquals(0, resources.getIdentifier("string/does_not_exist", null,
1167                 "com.android.layoutlib.test.myapplication"));
1168         assertEquals(R.string.accept, resources.getIdentifier("android:string/accept", null,
1169                 null));
1170         assertEquals(R.string.accept, resources.getIdentifier("string/accept", null,
1171                 "android"));
1172         assertEquals(R.id.message, resources.getIdentifier("id/message", null,
1173                 "android"));
1174         assertEquals(R.string.accept, resources.getIdentifier("accept", "string",
1175                 "android"));
1176 
1177         context.disposeResources();
1178     }
1179 
1180     /**
1181      * If a 9patch image was in the nodpi or anydpi folder, the density of the image was 0 resulting
1182      * in a float division by 0 and thus an infinite padding
1183      * when layoutlib tries to scale the padding of the 9patch.
1184      *
1185      * http://b/37136109
1186      */
1187     @Test
test9PatchNoDPIBackground()1188     public void test9PatchNoDPIBackground() throws Exception {
1189         String layout =
1190                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
1191                         "    android:layout_width=\"match_parent\"\n" +
1192                         "    android:layout_height=\"match_parent\"\n" +
1193                         "    android:background=\"@drawable/ninepatch\"\n" +
1194                         "    android:layout_margin=\"20dp\"\n" +
1195                         "    android:orientation=\"vertical\">\n\n" +
1196                         "    <Button\n" +
1197                         "        android:layout_width=\"wrap_content\"\n" +
1198                         "        android:layout_height=\"wrap_content\"\n" +
1199                         "        android:text=\"Button\" />\n\n" +
1200                         "    <Button\n" +
1201                         "        android:layout_width=\"wrap_content\"\n" +
1202                         "        android:layout_height=\"wrap_content\"\n" +
1203                         "        android:text=\"Button\" />\n"
1204                         + "</LinearLayout>";
1205 
1206         LayoutPullParser parser = LayoutPullParser.createFromString(layout);
1207         // Create LayoutLibCallback.
1208         LayoutLibTestCallback layoutLibCallback =
1209                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
1210         layoutLibCallback.initResources();
1211 
1212         SessionParams params = getSessionParamsBuilder()
1213                 .setParser(parser)
1214                 .setCallback(layoutLibCallback)
1215                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
1216                 .setRenderingMode(RenderingMode.V_SCROLL)
1217                 .build();
1218 
1219         renderAndVerify(params, "ninepatch_background.png");
1220     }
1221 
1222     @Test
testAssetManager()1223     public void testAssetManager() throws Exception {
1224         String layout =
1225                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
1226                         "              android:padding=\"16dp\"\n" +
1227                         "              android:orientation=\"horizontal\"\n" +
1228                         "              android:layout_width=\"fill_parent\"\n" +
1229                         "              android:layout_height=\"fill_parent\">\n" +
1230                         "    <com.android.layoutlib.test.myapplication.widgets.AssetView\n" +
1231                         "             android:layout_height=\"wrap_content\"\n" +
1232                         "             android:layout_width=\"wrap_content\" />\n" +
1233                         "</LinearLayout>\n";
1234         LayoutPullParser parser = LayoutPullParser.createFromString(layout);
1235         // Create LayoutLibCallback.
1236         LayoutLibTestCallback layoutLibCallback =
1237                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
1238         layoutLibCallback.initResources();
1239 
1240         SessionParams params = getSessionParamsBuilder()
1241                 .setParser(parser)
1242                 .setCallback(layoutLibCallback)
1243                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
1244                 .setRenderingMode(RenderingMode.V_SCROLL)
1245                 .build();
1246 
1247         renderAndVerify(params, "asset.png");
1248     }
1249 
1250     /**
1251      * Tests that calling setTheme in a ContextThemeWrapper actually applies the theme
1252      *
1253      * http://b/66902070
1254      */
1255     @Test
testContextThemeWrapper()1256     public void testContextThemeWrapper() throws ClassNotFoundException {
1257         // Create the layout pull parser.
1258         LayoutPullParser parser = LayoutPullParser.createFromString(
1259                 "<com.android.layoutlib.test.myapplication.ThemableWidget " +
1260                         "xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
1261                         "              android:layout_width=\"wrap_content\"\n" +
1262                         "              android:layout_height=\"wrap_content\" />\n");
1263         // Create LayoutLibCallback.
1264         LayoutLibTestCallback layoutLibCallback =
1265                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
1266         layoutLibCallback.initResources();
1267 
1268         SessionParams params = getSessionParamsBuilder()
1269                 .setParser(parser)
1270                 .setCallback(layoutLibCallback)
1271                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
1272                 .setRenderingMode(RenderingMode.V_SCROLL)
1273                 .build();
1274 
1275         renderAndVerify(params, "context_theme_wrapper.png", TimeUnit.SECONDS.toNanos(2));
1276     }
1277 
1278     /**
1279      * Tests that a crashing view does not prevent others from working. This is meant to prevent
1280      * crashes in framework views since custom views are already handled by Android Studio by
1281      * rewriting the byte code.
1282      */
1283     @Test
testCrashes()1284     public void testCrashes() throws ClassNotFoundException {
1285         final String layout =
1286                 "<LinearLayout " +
1287                     "xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
1288                     "              android:layout_width=\"match_parent\"\n" +
1289                     "              android:layout_height=\"match_parent\">\n" +
1290                     "<com.android.layoutlib.bridge.test.widgets.HookWidget " +
1291                     "              android:layout_width=\"100dp\"\n" +
1292                     "              android:layout_height=\"200dp\" />\n" +
1293                     "<LinearLayout " +
1294                     "              android:background=\"#CBBAF0\"\n" +
1295                     "              android:layout_width=\"100dp\"\n" +
1296                     "              android:layout_height=\"200dp\" />\n" +
1297                 "</LinearLayout>";
1298         {
1299             com.android.layoutlib.bridge.test.widgets.HookWidget.setOnPreDrawHook(() -> {
1300                 throw new NullPointerException();
1301             });
1302 
1303             // Create the layout pull parser.
1304             LayoutPullParser parser = LayoutPullParser.createFromString(layout);
1305             // Create LayoutLibCallback.
1306             LayoutLibTestCallback layoutLibCallback =
1307                     new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
1308             layoutLibCallback.initResources();
1309 
1310             SessionParams params = getSessionParamsBuilder()
1311                     .setParser(parser)
1312                     .setCallback(layoutLibCallback)
1313                     .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
1314                     .setRenderingMode(RenderingMode.V_SCROLL)
1315                     .build();
1316 
1317             renderAndVerify(params, "ondraw_crash.png", TimeUnit.SECONDS.toNanos(2));
1318         }
1319 
1320         com.android.layoutlib.bridge.test.widgets.HookWidget.reset();
1321 
1322         {
1323             com.android.layoutlib.bridge.test.widgets.HookWidget.setOnPreMeasure(() -> {
1324                 throw new NullPointerException();
1325             });
1326 
1327             LayoutPullParser parser = LayoutPullParser.createFromString(layout);
1328             LayoutLibTestCallback layoutLibCallback =
1329                     new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
1330             layoutLibCallback.initResources();
1331 
1332             SessionParams params = getSessionParamsBuilder()
1333                     .setParser(parser)
1334                     .setCallback(layoutLibCallback)
1335                     .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
1336                     .setRenderingMode(RenderingMode.V_SCROLL)
1337                     .build();
1338 
1339             renderAndVerify(params, "onmeasure_crash.png", TimeUnit.SECONDS.toNanos(2));
1340         }
1341 
1342         // We expect the view error messages. Fail for anything else.
1343         sRenderMessages.removeIf(message -> message.equals("View draw failed"));
1344         sRenderMessages.removeIf(message -> message.equals("View measure failed"));
1345     }
1346 
1347     /**
1348      * Paints the borders of the given {@link ViewInfo} and its children to the passed
1349      * {@link Graphics2D} context.
1350      * The method will used the given parentLeft and parentTop as the given vInfo coordinates.
1351      * The depth is used to calculate different colors for the borders depending on the hierarchy
1352      * depth.
1353      */
paintBorders(@onNull Graphics2D g, int parentLeft, int parentTop, int depth, @NonNull ViewInfo vInfo)1354     private void paintBorders(@NonNull Graphics2D g, int parentLeft, int parentTop, int depth,
1355             @NonNull ViewInfo vInfo) {
1356         int leftMargin = Math.max(0, vInfo.getLeftMargin());
1357         int topMargin = Math.max(0, vInfo.getTopMargin());
1358         int x = parentLeft + vInfo.getLeft() + leftMargin;
1359         int y = parentTop + vInfo.getTop() + topMargin;
1360         int w = vInfo.getRight() - vInfo.getLeft();
1361         int h = vInfo.getBottom() - vInfo.getTop();
1362         g.setXORMode(java.awt.Color.decode(Integer.toString(depth * 50000)));
1363         g.drawRect(x, y, w, h);
1364 
1365         for (ViewInfo child : vInfo.getChildren()) {
1366             paintBorders(g, x, y, depth + 1, child);
1367         }
1368     }
1369 
1370     @Test
testViewBoundariesReporting()1371     public void testViewBoundariesReporting() throws Exception {
1372         String layout =
1373                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
1374                         "              android:layout_width=\"match_parent\"\n" +
1375                         "              android:layout_height=\"match_parent\"\n" +
1376                         "              android:background=\"@drawable/ninepatch\"\n" +
1377                         "              android:layout_margin=\"20dp\"\n" +
1378                         "              android:orientation=\"vertical\">\n" + "\n" +
1379                         "    <TextView\n" +
1380                         "        android:layout_width=\"150dp\"\n" +
1381                         "        android:layout_height=\"50dp\"\n" +
1382                         "        android:background=\"#FF0\"/>\n" +
1383                         "    <TextView\n" +
1384                         "        android:layout_width=\"150dp\"\n" +
1385                         "        android:layout_height=\"50dp\"\n" +
1386                         "        android:background=\"#F00\"/>\n" +
1387                         "    <LinearLayout\n" +
1388                         "        android:layout_width=\"wrap_content\"\n" +
1389                         "        android:layout_height=\"wrap_content\"\n" +
1390                         "        android:paddingLeft=\"10dp\">\n" +
1391                         "        <TextView\n" +
1392                         "            android:layout_width=\"150dp\"\n" +
1393                         "            android:layout_height=\"50dp\"\n" +
1394                         "            android:background=\"#00F\"/>\n" +
1395                         "    </LinearLayout>\n" +
1396                         "    <LinearLayout\n" +
1397                         "        android:layout_width=\"wrap_content\"\n" +
1398                         "        android:layout_height=\"wrap_content\"\n" +
1399                         "        android:layout_marginLeft=\"30dp\"\n" +
1400                         "        android:layout_marginTop=\"15dp\">\n" +
1401                         "        <TextView\n" +
1402                         "            android:layout_width=\"150dp\"\n" +
1403                         "            android:layout_height=\"50dp\"\n" +
1404                         "            android:background=\"#F0F\"/>\n" +
1405                         "    </LinearLayout>\n" +
1406                         "</LinearLayout>";
1407 
1408         LayoutPullParser parser = LayoutPullParser.createFromString(layout);
1409         // Create LayoutLibCallback.
1410         LayoutLibTestCallback layoutLibCallback =
1411                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
1412         layoutLibCallback.initResources();
1413 
1414         SessionParams params = getSessionParamsBuilder()
1415                 .setParser(parser)
1416                 .setCallback(layoutLibCallback)
1417                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
1418                 .setRenderingMode(RenderingMode.V_SCROLL)
1419                 .disableDecoration()
1420                 .build();
1421 
1422         RenderResult result = RenderTestBase.render(sBridge, params, -1);
1423         BufferedImage image = result.getImage();
1424         assertNotNull(image);
1425         Graphics2D g = (Graphics2D) image.getGraphics();
1426         g.setStroke(new BasicStroke(8));
1427         for (ViewInfo vInfo : result.getSystemViews()) {
1428             paintBorders(g, 0, 0, 0, vInfo);
1429         }
1430 
1431         RenderTestBase.verify("view_boundaries.png", image);
1432     }
1433 
1434     /**
1435      * Test rendering of strings that have mixed RTL and LTR scripts.
1436      * <p>
1437      * http://b/37510906
1438      */
1439     @Test
testMixedRtlLtrRendering()1440     public void testMixedRtlLtrRendering() throws Exception {
1441         //
1442         final String layout =
1443                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
1444                         "              android:layout_width=\"match_parent\"\n" +
1445                         "              android:layout_height=\"match_parent\"\n" +
1446                         "              android:orientation=\"vertical\">\n" + "\n" +
1447                         "    <TextView\n" +
1448                         "        android:layout_width=\"wrap_content\"\n" +
1449                         "        android:layout_height=\"wrap_content\"\n" +
1450                         "        android:textSize=\"30sp\"\n" +
1451                         "        android:background=\"#55FF0000\"\n" +
1452                         "        android:text=\"این یک رشته ایرانی است\"/>\n" +
1453                         "    <TextView\n" +
1454                         "        android:layout_width=\"wrap_content\"\n" +
1455                         "        android:layout_height=\"wrap_content\"\n" +
1456                         "        android:textSize=\"30sp\"\n" +
1457                         "        android:background=\"#55FF00FF\"\n" +
1458                         "        android:text=\"این یک رشته ایرانی است(\"/>\n" +
1459                         "    <TextView\n" +
1460                         "        android:layout_width=\"wrap_content\"\n" +
1461                         "        android:layout_height=\"wrap_content\"\n" +
1462                         "        android:textSize=\"30sp\"\n" +
1463                         "        android:background=\"#55FAF012\"\n" +
1464                         "        android:text=\")(این یک رشته ایرانی است(\"/>\n" +
1465                         "</LinearLayout>";
1466 
1467         LayoutPullParser parser = LayoutPullParser.createFromString(layout);
1468         // Create LayoutLibCallback.
1469         LayoutLibTestCallback layoutLibCallback =
1470                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
1471         layoutLibCallback.initResources();
1472 
1473         SessionParams params = getSessionParamsBuilder()
1474                 .setParser(parser)
1475                 .setCallback(layoutLibCallback)
1476                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
1477                 .setRenderingMode(RenderingMode.V_SCROLL)
1478                 .disableDecoration()
1479                 .build();
1480 
1481         renderAndVerify(params, "rtl_ltr.png", -1);
1482     }
1483 
1484     @Test
testViewStub()1485     public void testViewStub() throws Exception {
1486         //
1487         final String layout =
1488                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
1489                         "              android:layout_width=\"match_parent\"\n" +
1490                         "              android:layout_height=\"match_parent\"\n" +
1491                         "              android:orientation=\"vertical\">\n" + "\n" +
1492                         "      <ViewStub\n" +
1493                         "        xmlns:tools=\"http://schemas.android.com/tools\"\n" +
1494                         "        android:layout_width=\"match_parent\"\n" +
1495                         "        android:layout_height=\"match_parent\"\n" +
1496                         "        android:layout=\"@layout/four_corners\"\n" +
1497                         "        tools:visibility=\"visible\" />" +
1498                         "</LinearLayout>";
1499 
1500         // Create the layout pull parser.
1501         LayoutPullParser parser = LayoutPullParser.createFromString(layout);
1502 
1503         // Create LayoutLibCallback.
1504         LayoutLibTestCallback layoutLibCallback =
1505                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
1506         layoutLibCallback.initResources();
1507 
1508         SessionParams params = getSessionParamsBuilder()
1509                 .setParser(parser)
1510                 .setCallback(layoutLibCallback)
1511                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
1512                 .setRenderingMode(RenderingMode.V_SCROLL)
1513                 .disableDecoration()
1514                 .build();
1515 
1516         renderAndVerify(params, "view_stub.png", -1);
1517     }
1518 
1519     @Test
testImageResize()1520     public void testImageResize() throws ClassNotFoundException {
1521         LayoutLibTestCallback layoutLibCallback =
1522                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
1523         layoutLibCallback.initResources();
1524 
1525         LayoutPullParser parser = LayoutPullParser.createFromString(
1526                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
1527                         "    android:layout_width=\"match_parent\"\n" +
1528                         "    android:layout_height=\"match_parent\"\n" +
1529                         "    android:background=\"@drawable/ninepatch\"\n" +
1530                         "    android:layout_margin=\"20dp\"\n" +
1531                         "    android:orientation=\"vertical\">\n\n" +
1532                         "    <Button\n" +
1533                         "        android:layout_width=\"wrap_content\"\n" +
1534                         "        android:layout_height=\"wrap_content\"\n" +
1535                         "        android:text=\"Button\" />\n\n" +
1536                         "    <Button\n" +
1537                         "        android:layout_width=\"wrap_content\"\n" +
1538                         "        android:layout_height=\"wrap_content\"\n" +
1539                         "        android:text=\"Button\" />\n"
1540                         + "</LinearLayout>");
1541 
1542         // Ask for an image that it's 1/10th the size of the actual device image
1543         SessionParams params = getSessionParamsBuilder()
1544                 .setParser(parser)
1545                 .setCallback(layoutLibCallback)
1546                 .setImageFactory((width, height) ->
1547                         new BufferedImage(width / 10, height / 10,
1548                         BufferedImage.TYPE_INT_ARGB))
1549                 .setFlag(RenderParamsFlags.FLAG_KEY_RESULT_IMAGE_AUTO_SCALE, true)
1550                 .disableShadows()
1551                 .build();
1552 
1553         renderAndVerify(params, "auto-scale-image.png");
1554     }
1555 
1556     @Test
testTranslation()1557     public void testTranslation() throws ClassNotFoundException, FileNotFoundException {
1558         RenderResult res = renderAndVerify("translate_test.xml", "translate_test.png", false);
1559         ViewInfo rootInfo = res.getRootViews().get(0);
1560         ViewInfo buttonInfo = rootInfo.getChildren().get(0);
1561         assertEquals(100, buttonInfo.getLeft());
1562     }
1563 
1564     /**
1565      * Test a vector drawable that uses trimStart and trimEnd. It also tests all the primitives
1566      * for vector drawables (lines, moves and cubic and quadratic curves).
1567      */
1568     @Test
testCanvas()1569     public void testCanvas() throws ClassNotFoundException {
1570         // Create the layout pull parser.
1571         LayoutPullParser parser = LayoutPullParser.createFromString(
1572                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
1573                         "              android:padding=\"16dp\"\n" +
1574                         "              android:orientation=\"horizontal\"\n" +
1575                         "              android:layout_width=\"fill_parent\"\n" +
1576                         "              android:layout_height=\"fill_parent\">\n" +
1577                         "    <com.android.layoutlib.test.myapplication.widgets.CanvasTestView\n" +
1578                         "             android:layout_height=\"fill_parent\"\n" +
1579                         "             android:layout_width=\"fill_parent\"\n" +
1580                         "             android:src=\"@drawable/android\" />\n" + "\n" +
1581                         "</LinearLayout>");
1582         // Create LayoutLibCallback.
1583         LayoutLibTestCallback layoutLibCallback =
1584                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
1585         layoutLibCallback.initResources();
1586 
1587         SessionParams params = getSessionParamsBuilder()
1588                 .setParser(parser)
1589                 .setCallback(layoutLibCallback)
1590                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
1591                 .setRenderingMode(RenderingMode.V_SCROLL)
1592                 .disableDecoration()
1593                 .build();
1594 
1595         renderAndVerify(params, "canvas.png", TimeUnit.SECONDS.toNanos(2));
1596     }
1597 
1598     @Test
testTypedArrays()1599     public void testTypedArrays() throws ClassNotFoundException, FileNotFoundException {
1600         renderAndVerify("typed_array.xml", "typed_arrays.png", false);
1601     }
1602 
1603     /**
1604      * Tests that the gradients are correctly displayed when using transparent colors
1605      * and a wide range of offset values.
1606      * <p/>
1607      * http://b/112759140
1608      */
1609     @Test
testAnimatedVectorDrawableWithColorInterpolator()1610     public void testAnimatedVectorDrawableWithColorInterpolator() throws ClassNotFoundException {
1611         // Create the layout pull parser.
1612         LayoutPullParser parser = LayoutPullParser.createFromString(
1613                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
1614                         "              android:padding=\"16dp\"\n" +
1615                         "              android:orientation=\"horizontal\"\n" +
1616                         "              android:layout_width=\"match_parent\"\n" +
1617                         "              android:layout_height=\"match_parent\">\n" +
1618                         "    <ImageView\n" +
1619                         "             android:layout_height=\"match_parent\"\n" +
1620                         "             android:layout_width=\"match_parent\"\n" +
1621                         "             android:src=\"@drawable/avd_color_interpolator\" />\n\n" +
1622                         "</LinearLayout>");
1623         // Create LayoutLibCallback.
1624         LayoutLibTestCallback layoutLibCallback =
1625                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
1626         layoutLibCallback.initResources();
1627 
1628         SessionParams params = getSessionParamsBuilder()
1629                 .setParser(parser)
1630                 .setCallback(layoutLibCallback)
1631                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
1632                 .setRenderingMode(RenderingMode.V_SCROLL)
1633                 .disableDecoration()
1634                 .build();
1635 
1636         renderAndVerify(params, "color_interpolation.png",
1637                 TimeUnit.SECONDS.toNanos(2));
1638     }
1639 
1640     @Test
testManyLineBreaks()1641     public void testManyLineBreaks() throws Exception {
1642         String layout =
1643                 "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
1644                         "              android:layout_width=\"match_parent\"\n" +
1645                         "              android:layout_height=\"match_parent\">\n" + "\n" +
1646                         "    <EditText\n" +
1647                         "        android:layout_width=\"match_parent\"\n" +
1648                         "        android:layout_height=\"wrap_content\"\n" +
1649                         "        android:fallbackLineSpacing=\"true\"\n" +
1650                         "        android:text=\"A very very very very very very very very very " +
1651                         "very very very very very very very very very very very very very very " +
1652                         "very very very very very very very very very very very very very very " +
1653                         "very very very very very very very very very very very very very very " +
1654                         "very very very very very very very very very very very very very very " +
1655                         "very very very very very very very very very very very very very very " +
1656                         "very very very very very very very very very very very very very very " +
1657                         "very very very very very very very very very very very very very very " +
1658                         "very very very very very very very very very very very very very very " +
1659                         "very very very very very very very very very very very very very very " +
1660                         "very very very very very very very very very very very very very very " +
1661                         "very very very very very very very very very very very very very very " +
1662                         "very very very very very very very long text\"/>\n" +
1663                         "</FrameLayout>";
1664 
1665         LayoutPullParser parser = LayoutPullParser.createFromString(layout);
1666         // Create LayoutLibCallback.
1667         LayoutLibTestCallback layoutLibCallback =
1668                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
1669         layoutLibCallback.initResources();
1670 
1671         SessionParams params = getSessionParamsBuilder()
1672                 .setParser(parser)
1673                 .setCallback(layoutLibCallback)
1674                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
1675                 .setRenderingMode(RenderingMode.V_SCROLL)
1676                 .disableDecoration()
1677                 .build();
1678 
1679         renderAndVerify(params, "many_line_breaks.png",
1680                 TimeUnit.SECONDS.toNanos(2));
1681     }
1682 
1683     @Test
testHighQualityShadowWidgetWithScroll()1684     public void testHighQualityShadowWidgetWithScroll() throws Exception {
1685         LayoutPullParser parser = createParserFromPath("shadows_scrollview.xml");
1686         LayoutLibTestCallback layoutLibCallback =
1687                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
1688         layoutLibCallback.initResources();
1689         SessionParams params = getSessionParamsBuilder()
1690                 .setParser(parser)
1691                 .setCallback(layoutLibCallback)
1692                 .build();
1693 
1694         renderAndVerify(params, "shadow_scrollview_test_high_quality.png");
1695         // We expect fidelity warnings for Path.isConvex. Fail for anything else.
1696         sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
1697         sRenderMessages.removeIf(message -> message.equals("Font$Builder.nAddAxis is not supported."));
1698     }
1699 
1700     @Test
testContentId()1701     public void testContentId() throws ClassNotFoundException {
1702         final String layout =
1703                 "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
1704                         "              android:layout_width=\"match_parent\"\n" +
1705                         "              android:layout_height=\"match_parent\">\n" + "\n" +
1706                         "    <com.android.layoutlib.bridge.test.widgets.ContentWidget\n" +
1707                         "        android:layout_width=\"match_parent\"\n" +
1708                         "        android:layout_height=\"wrap_content\"/>\n" +
1709                         "</FrameLayout>";
1710 
1711         {
1712             // Create the layout pull parser.
1713             LayoutPullParser parser = LayoutPullParser.createFromString(layout);
1714             // Create LayoutLibCallback.
1715             LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
1716             layoutLibCallback.initResources();
1717 
1718             SessionParams params = getSessionParamsBuilder()
1719                     .setParser(parser)
1720                     .setCallback(layoutLibCallback)
1721                     .build();
1722 
1723             RenderResult result = render(sBridge, params, TimeUnit.SECONDS.toNanos(2));
1724             BufferedImage image = result.getImage();
1725             assertNotNull(image);
1726         }
1727 
1728         {
1729             // Create the layout pull parser.
1730             LayoutPullParser parser = LayoutPullParser.createFromString(layout);
1731             // Create LayoutLibCallback.
1732             LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
1733             layoutLibCallback.initResources();
1734 
1735             SessionParams params = getSessionParamsBuilder()
1736                     .setParser(parser)
1737                     .setCallback(layoutLibCallback)
1738                     .disableDecoration()
1739                     .build();
1740 
1741             RenderResult result = render(sBridge, params, TimeUnit.SECONDS.toNanos(2));
1742             BufferedImage image = result.getImage();
1743             assertNotNull(image);
1744         }
1745     }
1746 
1747     /**
1748      * Tests that the TextClock widget renders without error
1749      * <p/>
1750      * http://b/150151293
1751      */
1752     @Test
testTextClock()1753     public void testTextClock() throws ClassNotFoundException {
1754         String layout =
1755                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
1756                         "              android:padding=\"16dp\"\n" +
1757                         "              android:orientation=\"horizontal\"\n" +
1758                         "              android:layout_width=\"fill_parent\"\n" +
1759                         "              android:layout_height=\"fill_parent\">\n" +
1760                         "    <TextClock\n" +
1761                         "             android:layout_height=\"wrap_content\"\n" +
1762                         "             android:layout_width=\"wrap_content\"\n" +
1763                         "             android:text=\"12:34\"" +
1764                         "             android:textSize=\"18sp\" />\n" +
1765                         "</LinearLayout>\n";
1766         LayoutPullParser parser = LayoutPullParser.createFromString(layout);
1767         // Create LayoutLibCallback.
1768         LayoutLibTestCallback layoutLibCallback =
1769                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
1770         layoutLibCallback.initResources();
1771         SessionParams params = getSessionParamsBuilder()
1772                 .setParser(parser)
1773                 .setCallback(layoutLibCallback)
1774                 .disableDecoration()
1775                 .build();
1776 
1777         renderAndVerify(params, "textclock.png");
1778     }
1779 }
1780