• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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 android.widget.cts;
18 
19 import static android.appwidget.flags.Flags.drawDataParcel;
20 import static android.util.TypedValue.COMPLEX_UNIT_DIP;
21 import static android.util.TypedValue.COMPLEX_UNIT_PX;
22 import static android.view.View.MeasureSpec.makeMeasureSpec;
23 import static android.view.inputmethod.Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR;
24 import static android.widget.RemoteViews.MARGIN_BOTTOM;
25 import static android.widget.RemoteViews.MARGIN_END;
26 import static android.widget.RemoteViews.MARGIN_LEFT;
27 import static android.widget.RemoteViews.MARGIN_RIGHT;
28 import static android.widget.RemoteViews.MARGIN_START;
29 import static android.widget.RemoteViews.MARGIN_TOP;
30 
31 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
32 import static com.android.compatibility.common.util.TestUtils.waitUntil;
33 
34 import static junit.framework.Assert.fail;
35 
36 import static org.junit.Assert.assertEquals;
37 import static org.junit.Assert.assertFalse;
38 import static org.junit.Assert.assertNotNull;
39 import static org.junit.Assert.assertNull;
40 import static org.junit.Assert.assertSame;
41 import static org.junit.Assert.assertThrows;
42 import static org.junit.Assert.assertTrue;
43 
44 import android.Manifest;
45 import android.app.Activity;
46 import android.app.Instrumentation;
47 import android.app.Instrumentation.ActivityMonitor;
48 import android.app.PendingIntent;
49 import android.content.BroadcastReceiver;
50 import android.content.Context;
51 import android.content.Intent;
52 import android.content.IntentFilter;
53 import android.content.res.ColorStateList;
54 import android.graphics.Bitmap;
55 import android.graphics.BitmapFactory;
56 import android.graphics.BlendMode;
57 import android.graphics.Canvas;
58 import android.graphics.Color;
59 import android.graphics.drawable.BitmapDrawable;
60 import android.graphics.drawable.Icon;
61 import android.net.Uri;
62 import android.os.Bundle;
63 import android.os.Parcel;
64 import android.os.Parcelable;
65 import android.os.SystemClock;
66 import android.platform.test.annotations.RequiresFlagsEnabled;
67 import android.platform.test.flag.junit.CheckFlagsRule;
68 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
69 import android.text.TextUtils;
70 import android.util.ArrayMap;
71 import android.util.DisplayMetrics;
72 import android.util.SizeF;
73 import android.util.TypedValue;
74 import android.view.MotionEvent;
75 import android.view.View;
76 import android.view.ViewGroup;
77 import android.widget.AbsoluteLayout;
78 import android.widget.AnalogClock;
79 import android.widget.Button;
80 import android.widget.CheckBox;
81 import android.widget.Chronometer;
82 import android.widget.CompoundButton;
83 import android.widget.DatePicker;
84 import android.widget.EditText;
85 import android.widget.FrameLayout;
86 import android.widget.GridLayout;
87 import android.widget.GridView;
88 import android.widget.ImageButton;
89 import android.widget.ImageView;
90 import android.widget.LinearLayout;
91 import android.widget.ListView;
92 import android.widget.NumberPicker;
93 import android.widget.ProgressBar;
94 import android.widget.RadioButton;
95 import android.widget.RadioGroup;
96 import android.widget.RatingBar;
97 import android.widget.RelativeLayout;
98 import android.widget.RemoteViews;
99 import android.widget.RemoteViews.ActionException;
100 import android.widget.SeekBar;
101 import android.widget.StackView;
102 import android.widget.Switch;
103 import android.widget.TextClock;
104 import android.widget.TextView;
105 import android.widget.ViewFlipper;
106 import android.widget.cts.util.RemoteViewsUtil;
107 import android.widget.cts.util.TestUtils;
108 
109 import androidx.test.InstrumentationRegistry;
110 import androidx.test.filters.LargeTest;
111 import androidx.test.filters.MediumTest;
112 import androidx.test.rule.ActivityTestRule;
113 
114 import com.android.compatibility.common.util.AdoptShellPermissionsRule;
115 import com.android.compatibility.common.util.ThrowingRunnable;
116 import com.android.compatibility.common.util.WidgetTestUtils;
117 
118 import org.junit.After;
119 import org.junit.Assert;
120 import org.junit.Before;
121 import org.junit.Rule;
122 import org.junit.Test;
123 import org.junit.rules.ExpectedException;
124 import org.junit.runner.RunWith;
125 import org.junit.runners.Parameterized;
126 
127 import java.io.File;
128 import java.io.FileOutputStream;
129 import java.io.IOException;
130 import java.io.InputStream;
131 import java.io.OutputStream;
132 import java.util.Arrays;
133 import java.util.Collections;
134 import java.util.List;
135 import java.util.Map;
136 import java.util.concurrent.CountDownLatch;
137 import java.util.concurrent.TimeUnit;
138 import java.util.function.Consumer;
139 import java.util.function.Function;
140 
141 /** Test {@link RemoteViews}. */
142 @MediumTest
143 @RunWith(Parameterized.class)
144 public class RemoteViewsTest {
145     private static final String PACKAGE_NAME = "android.widget.cts";
146 
147     private static final int INVALID_ID = -1;
148 
149     private static final long TEST_TIMEOUT = 5000;
150 
151     @Rule(order = 0)
152     public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule(
153             androidx.test.platform.app.InstrumentationRegistry
154                     .getInstrumentation().getUiAutomation(),
155             Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX);
156 
157     @Rule(order = 1)
158     public ActivityTestRule<RemoteViewsCtsActivity> mActivityRule =
159             new ActivityTestRule<>(RemoteViewsCtsActivity.class);
160 
161     @Rule
162     public ExpectedException mExpectedException = ExpectedException.none();
163 
164     @Rule
165     public final CheckFlagsRule mCheckFlagsRule =
166             DeviceFlagsValueProvider.createCheckFlagsRule();
167 
168     @Parameterized.Parameters(name = "isProtoTest={0}")
parameters()169     public static Object[] parameters() {
170         return new Object[] {false, true};
171     }
172 
173     /**
174      * When this parameter is true, the test serializes and deserializes the RemoteViews to/from
175      * proto before applying. This ensures that proto serialization does not cause a change in the
176      * structure or function of RemoteViews, apart from PendingIntent based APIs.
177      */
178     @Parameterized.Parameter(0)
179     public boolean isProtoTest;
180 
181     private Instrumentation mInstrumentation;
182 
183     private Context mContext;
184 
185     private RemoteViews mRemoteViews;
186 
187     private View mResult;
188 
189     private String mInitialNightMode;
190 
191     @Before
setUp()192     public void setUp() throws Throwable {
193         RemoteViewsUtil.checkRemoteViewsProtoFlag(isProtoTest);
194         // Ensure the UI is currently NOT in night mode.
195         mInitialNightMode = changeNightMode(false);
196 
197         mActivityRule.runOnUiThread(() -> {
198             mInstrumentation = InstrumentationRegistry.getInstrumentation();
199             mContext = mInstrumentation.getTargetContext();
200             mRemoteViews = new RemoteViews(PACKAGE_NAME, R.layout.remoteviews_good);
201             mResult = mRemoteViews.apply(mContext, null);
202 
203             // Add our host view to the activity behind this test. This is similar to how launchers
204             // add widgets to the on-screen UI.
205             ViewGroup root = (ViewGroup) mActivityRule.getActivity().findViewById(
206                     R.id.remoteView_host);
207             FrameLayout.MarginLayoutParams lp = new FrameLayout.MarginLayoutParams(
208                     ViewGroup.LayoutParams.MATCH_PARENT,
209                     ViewGroup.LayoutParams.MATCH_PARENT);
210             mResult.setLayoutParams(lp);
211 
212             root.addView(mResult);
213         });
214     }
215 
216     @After
tearDown()217     public void tearDown() {
218         runShellCommand("cmd uimode night " + mInitialNightMode);
219     }
220 
221     @Test
testConstructor()222     public void testConstructor() {
223         new RemoteViews(PACKAGE_NAME, R.layout.remoteviews_good);
224 
225         new RemoteViews(Parcel.obtain());
226     }
227 
228     @Test
testGetPackage()229     public void testGetPackage() {
230         assertEquals(PACKAGE_NAME, mRemoteViews.getPackage());
231 
232         mRemoteViews = new RemoteViews(null, R.layout.remoteviews_good);
233         assertNull(mRemoteViews.getPackage());
234     }
235 
236     @Test
testGetLayoutId()237     public void testGetLayoutId() {
238         assertEquals(R.layout.remoteviews_good, mRemoteViews.getLayoutId());
239 
240         mRemoteViews = new RemoteViews(PACKAGE_NAME, R.layout.listview_layout);
241         assertEquals(R.layout.listview_layout, mRemoteViews.getLayoutId());
242 
243         mRemoteViews = new RemoteViews(PACKAGE_NAME, INVALID_ID);
244         assertEquals(INVALID_ID, mRemoteViews.getLayoutId());
245 
246         mRemoteViews = new RemoteViews(PACKAGE_NAME, 0);
247         assertEquals(0, mRemoteViews.getLayoutId());
248     }
249 
250     @Test
testSetContentDescription()251     public void testSetContentDescription() throws Throwable {
252         View view = mResult.findViewById(R.id.remoteView_frame);
253 
254         assertNull(view.getContentDescription());
255 
256         CharSequence contentDescription = mContext.getString(R.string.remote_content_description);
257         mRemoteViews.setContentDescription(R.id.remoteView_frame, contentDescription);
258         reapplyRemoteViews();
259         assertTrue(TextUtils.equals(contentDescription, view.getContentDescription()));
260     }
261 
262     @Test
testSetViewVisibility()263     public void testSetViewVisibility() throws Throwable {
264         View view = mResult.findViewById(R.id.remoteView_chronometer);
265         assertEquals(View.VISIBLE, view.getVisibility());
266 
267         mRemoteViews.setViewVisibility(R.id.remoteView_chronometer, View.INVISIBLE);
268         reapplyRemoteViews();
269         assertEquals(View.INVISIBLE, view.getVisibility());
270 
271         mRemoteViews.setViewVisibility(R.id.remoteView_chronometer, View.GONE);
272         reapplyRemoteViews();
273         assertEquals(View.GONE, view.getVisibility());
274 
275         mRemoteViews.setViewVisibility(R.id.remoteView_chronometer, View.VISIBLE);
276         reapplyRemoteViews();
277         assertEquals(View.VISIBLE, view.getVisibility());
278     }
279 
280     @Test
testSetTextViewText()281     public void testSetTextViewText() throws Throwable {
282         TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
283         assertEquals("", textView.getText().toString());
284 
285         String expected = "This is content";
286         mRemoteViews.setTextViewText(R.id.remoteView_text, expected);
287         reapplyRemoteViews();
288         assertEquals(expected, textView.getText().toString());
289 
290         mRemoteViews.setTextViewText(R.id.remoteView_text, null);
291         reapplyRemoteViews();
292         assertEquals("", textView.getText().toString());
293 
294         mRemoteViews.setTextViewText(R.id.remoteView_absolute, "");
295         assertThrowsOnReapply(ActionException.class);
296     }
297 
298     @Test
testSetTextViewTextSize()299     public void testSetTextViewTextSize() throws Throwable {
300         TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
301 
302         mRemoteViews.setTextViewTextSize(R.id.remoteView_text, TypedValue.COMPLEX_UNIT_SP, 18);
303         reapplyRemoteViews();
304         float expectedTextSizePx = TypedValue.convertDimensionToPixels(
305                 TypedValue.COMPLEX_UNIT_SP,
306                 18,
307                 mContext.getResources().getDisplayMetrics()
308         );
309         assertEquals(expectedTextSizePx, textView.getTextSize(), 0.001f);
310 
311         mRemoteViews.setTextViewTextSize(R.id.remoteView_absolute, TypedValue.COMPLEX_UNIT_SP, 20);
312         assertThrowsOnReapply(Throwable.class);
313     }
314 
315     @Test
testSetIcon()316     public void testSetIcon() throws Throwable {
317         ImageView image = (ImageView) mResult.findViewById(R.id.remoteView_image);
318         assertNull(image.getDrawable());
319 
320         Icon iconBlack = Icon.createWithResource(mContext, R.drawable.icon_black);
321         mRemoteViews.setIcon(R.id.remoteView_image, "setImageIcon", iconBlack);
322         reapplyRemoteViews();
323         assertNotNull(image.getDrawable());
324         BitmapDrawable dBlack = (BitmapDrawable) mContext.getDrawable(R.drawable.icon_black);
325         WidgetTestUtils.assertEquals(dBlack.getBitmap(),
326                 ((BitmapDrawable) image.getDrawable()).getBitmap());
327     }
328 
329     @Test
testSetIcon_nightMode()330     public void testSetIcon_nightMode() throws Throwable {
331         ImageView image = (ImageView) mResult.findViewById(R.id.remoteView_image);
332         Icon iconLight = Icon.createWithResource(mContext, R.drawable.icon_green);
333         Icon iconDark = Icon.createWithResource(mContext, R.drawable.icon_blue);
334         mRemoteViews.setIcon(R.id.remoteView_image, "setImageIcon", iconLight, iconDark);
335 
336         applyNightModeThenReapplyAndTest(false, () -> {
337             assertNotNull(image.getDrawable());
338             BitmapDrawable dLight = (BitmapDrawable) mContext.getDrawable(R.drawable.icon_green);
339             WidgetTestUtils.assertEquals(dLight.getBitmap(),
340                     ((BitmapDrawable) image.getDrawable()).getBitmap());
341         });
342         applyNightModeThenReapplyAndTest(true, () -> {
343             assertNotNull(image.getDrawable());
344             BitmapDrawable dDark = (BitmapDrawable) mContext.getDrawable(R.drawable.icon_blue);
345             WidgetTestUtils.assertEquals(dDark.getBitmap(),
346                     ((BitmapDrawable) image.getDrawable()).getBitmap());
347         });
348     }
349 
350     @Test
testSetImageViewIcon()351     public void testSetImageViewIcon() throws Throwable {
352         ImageView image = (ImageView) mResult.findViewById(R.id.remoteView_image);
353         assertNull(image.getDrawable());
354 
355         Icon iconBlue = Icon.createWithResource(mContext, R.drawable.icon_blue);
356         mRemoteViews.setImageViewIcon(R.id.remoteView_image, iconBlue);
357         reapplyRemoteViews();
358         assertNotNull(image.getDrawable());
359         BitmapDrawable dBlue = (BitmapDrawable) mContext.getDrawable(R.drawable.icon_blue);
360         WidgetTestUtils.assertEquals(dBlue.getBitmap(),
361                 ((BitmapDrawable) image.getDrawable()).getBitmap());
362 
363     }
364 
365     @Test
testSetImageViewResource()366     public void testSetImageViewResource() throws Throwable {
367         ImageView image = (ImageView) mResult.findViewById(R.id.remoteView_image);
368         assertNull(image.getDrawable());
369 
370         mRemoteViews.setImageViewResource(R.id.remoteView_image, R.drawable.testimage);
371         reapplyRemoteViews();
372         assertNotNull(image.getDrawable());
373         BitmapDrawable d = (BitmapDrawable) mContext.getDrawable(R.drawable.testimage);
374         WidgetTestUtils.assertEquals(d.getBitmap(),
375                 ((BitmapDrawable) image.getDrawable()).getBitmap());
376 
377         mRemoteViews.setImageViewResource(R.id.remoteView_absolute, R.drawable.testimage);
378         assertThrowsOnReapply(ActionException.class);
379     }
380 
381     @Test
testSetImageViewUri()382     public void testSetImageViewUri() throws Throwable {
383         String path = getTestImagePath();
384         File imageFile = new File(path);
385 
386         try {
387             createSampleImage(imageFile, R.raw.testimage);
388 
389             Uri uri = Uri.parse(path);
390             ImageView image = (ImageView) mResult.findViewById(R.id.remoteView_image);
391             assertNull(image.getDrawable());
392 
393             mRemoteViews.setImageViewUri(R.id.remoteView_image, uri);
394             reapplyRemoteViews();
395 
396             Bitmap imageViewBitmap = ((BitmapDrawable) image.getDrawable()).getBitmap();
397             Bitmap expectedBitmap = WidgetTestUtils.getUnscaledAndDitheredBitmap(
398                     mContext.getResources(), R.raw.testimage, imageViewBitmap.getConfig());
399             WidgetTestUtils.assertEquals(expectedBitmap, imageViewBitmap);
400         } finally {
401             imageFile.delete();
402         }
403     }
404 
405     /**
406      * Returns absolute file path of location where test image should be stored
407      */
getTestImagePath()408     private String getTestImagePath() {
409         return mContext.getFilesDir() + "/test.jpg";
410     }
411 
412     @Test
testSetChronometer()413     public void testSetChronometer() throws Throwable {
414         long base1 = 50;
415         long base2 = -50;
416         Chronometer chronometer = (Chronometer) mResult.findViewById(R.id.remoteView_chronometer);
417 
418         mRemoteViews.setChronometer(R.id.remoteView_chronometer, base1, "HH:MM:SS",
419                 false);
420         reapplyRemoteViews();
421         assertEquals(base1, chronometer.getBase());
422         assertEquals("HH:MM:SS", chronometer.getFormat());
423 
424         mRemoteViews.setChronometer(R.id.remoteView_chronometer, base2, "HH:MM:SS",
425                 false);
426         reapplyRemoteViews();
427         assertEquals(base2, chronometer.getBase());
428         assertEquals("HH:MM:SS", chronometer.getFormat());
429 
430         mRemoteViews.setChronometer(R.id.remoteView_chronometer, base1, "invalid",
431                 true);
432         reapplyRemoteViews();
433         assertEquals(base1, chronometer.getBase());
434         assertEquals("invalid", chronometer.getFormat());
435 
436         mRemoteViews.setChronometer(R.id.remoteView_absolute, base1, "invalid", true);
437         assertThrowsOnReapply(ActionException.class);
438     }
439 
440     @Test
testSetChronometerCountDown()441     public void testSetChronometerCountDown() throws Throwable {
442         Chronometer chronometer = (Chronometer) mResult.findViewById(R.id.remoteView_chronometer);
443 
444         mRemoteViews.setChronometerCountDown(R.id.remoteView_chronometer, true);
445         reapplyRemoteViews();
446         assertTrue(chronometer.isCountDown());
447 
448         mRemoteViews.setChronometerCountDown(R.id.remoteView_chronometer, false);
449         reapplyRemoteViews();
450         assertFalse(chronometer.isCountDown());
451 
452         mRemoteViews.setChronometerCountDown(R.id.remoteView_absolute, true);
453         assertThrowsOnReapply(ActionException.class);
454     }
455 
456     @Test
testSetProgressBar()457     public void testSetProgressBar() throws Throwable {
458         ProgressBar progress = (ProgressBar) mResult.findViewById(R.id.remoteView_progress);
459         assertEquals(100, progress.getMax());
460         assertEquals(0, progress.getProgress());
461         // the view uses style progressBarHorizontal, so the default is false
462         assertFalse(progress.isIndeterminate());
463 
464         mRemoteViews.setProgressBar(R.id.remoteView_progress, 80, 50, true);
465         reapplyRemoteViews();
466         // make the bar indeterminate will not affect max and progress
467         assertEquals(100, progress.getMax());
468         assertEquals(0, progress.getProgress());
469         assertTrue(progress.isIndeterminate());
470 
471         mRemoteViews.setProgressBar(R.id.remoteView_progress, 60, 50, false);
472         reapplyRemoteViews();
473         assertEquals(60, progress.getMax());
474         assertEquals(50, progress.getProgress());
475         assertFalse(progress.isIndeterminate());
476 
477         mRemoteViews.setProgressBar(R.id.remoteView_relative, 60, 50, false);
478         assertThrowsOnReapply(ActionException.class);
479     }
480 
481     @Test
testApply()482     public void testApply() {
483         assertNotNull(mResult);
484         assertNotNull(mResult.findViewById(R.id.remoteViews_good));
485         assertNotNull(mResult.findViewById(R.id.remoteView_absolute));
486         assertNotNull(mResult.findViewById(R.id.remoteView_chronometer));
487         assertNotNull(mResult.findViewById(R.id.remoteView_frame));
488         assertNotNull(mResult.findViewById(R.id.remoteView_image));
489         assertNotNull(mResult.findViewById(R.id.remoteView_linear));
490         assertNotNull(mResult.findViewById(R.id.remoteView_progress));
491         assertNotNull(mResult.findViewById(R.id.remoteView_relative));
492         assertNotNull(mResult.findViewById(R.id.remoteView_text));
493     }
494 
495     @Test
testReapply()496     public void testReapply() throws Throwable {
497         ImageView image = (ImageView) mResult.findViewById(R.id.remoteView_image);
498         assertNull(image.getDrawable());
499 
500         mRemoteViews.setImageViewResource(R.id.remoteView_image, R.drawable.testimage);
501         reapplyRemoteViews();
502         assertNotNull(image.getDrawable());
503         BitmapDrawable d = (BitmapDrawable) mContext
504                 .getResources().getDrawable(R.drawable.testimage);
505         WidgetTestUtils.assertEquals(d.getBitmap(),
506                 ((BitmapDrawable) image.getDrawable()).getBitmap());
507     }
508 
509     @Test
testApplyWithDrawInstructions()510     public void testApplyWithDrawInstructions() throws Throwable {
511         if (!drawDataParcel()) {
512             return;
513         }
514         final RemoteViews.DrawInstructions drawInstructions =
515                 getDrawInstructions(mContext, R.raw.widget);
516         final String key = "mykey";
517         final String action = "myaction";
518         final MockBroadcastReceiver receiver = new MockBroadcastReceiver();
519         mContext.registerReceiver(receiver, new IntentFilter(action), Context.RECEIVER_EXPORTED);
520 
521         final Intent intent = new Intent(action).setPackage(mContext.getPackageName());
522         final PendingIntent pendingIntent =
523                 PendingIntent.getBroadcast(
524                         mContext,
525                         0,
526                         intent,
527                         PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
528         final ActivityMonitor am = mInstrumentation.addMonitor(
529                 MockURLSpanTestActivity.class.getName(), null, false);
530         Activity newActivity = am.waitForActivityWithTimeout(TEST_TIMEOUT);
531         assertNull(newActivity);
532 
533         // Proto serialization removes PendingIntents from the RemoteViews.
534         if (isProtoTest) return;
535 
536         final RemoteViews remoteViews = new RemoteViews(drawInstructions);
537         remoteViews.setPendingIntentTemplate(1, pendingIntent);
538         remoteViews.setOnClickFillInIntent(1, new Intent().putExtra(key, 1));
539         final View result = remoteViews.apply(mContext, null);
540         mActivityRule.runOnUiThread(
541                 () -> {
542                     final ViewGroup root =
543                             (ViewGroup)
544                                     mActivityRule.getActivity().findViewById(R.id.remoteView_host);
545                     root.removeAllViews();
546                     root.addView(result);
547                 });
548         final int offset = 2;
549         verifyClick(result, receiver, offset, offset, 1);
550     }
551 
552     @Test
testDrawInstructionsBlend()553     public void testDrawInstructionsBlend() throws Throwable {
554         if (!drawDataParcel()) {
555             return;
556         }
557         testSingleDrawInstruction("blend_clear",
558                 R.raw.blend_clear, R.drawable.blend_clear);
559         testSingleDrawInstruction("blend_color",
560                 R.raw.blend_color, R.drawable.blend_color);
561         testSingleDrawInstruction("blend_color_burn",
562                 R.raw.blend_color_burn, R.drawable.blend_color_burn);
563         testSingleDrawInstruction("blend_color_dodge",
564                 R.raw.blend_color_dodge, R.drawable.blend_color_dodge);
565         testSingleDrawInstruction("blend_darken",
566                 R.raw.blend_darken, R.drawable.blend_darken);
567         testSingleDrawInstruction("blend_difference",
568                 R.raw.blend_difference, R.drawable.blend_difference);
569         testSingleDrawInstruction("blend_dst",
570                 R.raw.blend_dst, R.drawable.blend_dst);
571         testSingleDrawInstruction("blend_dst_atop",
572                 R.raw.blend_dst_atop, R.drawable.blend_dst_atop);
573         testSingleDrawInstruction("blend_dst_in",
574                 R.raw.blend_dst_in, R.drawable.blend_dst_in);
575         testSingleDrawInstruction("blend_dst_out",
576                 R.raw.blend_dst_out, R.drawable.blend_dst_out);
577         testSingleDrawInstruction("blend_dst_over",
578                 R.raw.blend_dst_over, R.drawable.blend_dst_over);
579         testSingleDrawInstruction("blend_exclusion",
580                 R.raw.blend_exclusion, R.drawable.blend_exclusion);
581         testSingleDrawInstruction("blend_hard_light",
582                 R.raw.blend_hard_light, R.drawable.blend_hard_light);
583         testSingleDrawInstruction("blend_hue",
584                 R.raw.blend_hue, R.drawable.blend_hue);
585         testSingleDrawInstruction("blend_lighten",
586                 R.raw.blend_lighten, R.drawable.blend_lighten);
587         testSingleDrawInstruction("blend_luminosity",
588                 R.raw.blend_luminosity, R.drawable.blend_luminosity);
589         testSingleDrawInstruction("blend_modulate",
590                 R.raw.blend_modulate, R.drawable.blend_modulate);
591         testSingleDrawInstruction("blend_multiply",
592                 R.raw.blend_multiply, R.drawable.blend_multiply);
593         testSingleDrawInstruction("blend_overlay",
594                 R.raw.blend_overlay, R.drawable.blend_overlay);
595         testSingleDrawInstruction("blend_plus",
596                 R.raw.blend_plus, R.drawable.blend_plus);
597         testSingleDrawInstruction("blend_saturation",
598                 R.raw.blend_saturation, R.drawable.blend_saturation);
599         testSingleDrawInstruction("blend_screen",
600                 R.raw.blend_screen, R.drawable.blend_screen);
601         testSingleDrawInstruction("blend_soft_light",
602                 R.raw.blend_soft_light, R.drawable.blend_soft_light);
603         testSingleDrawInstruction("blend_src",
604                 R.raw.blend_src, R.drawable.blend_src);
605         testSingleDrawInstruction("blend_src_atop",
606                 R.raw.blend_src_atop, R.drawable.blend_src_atop);
607         testSingleDrawInstruction("blend_src_in",
608                 R.raw.blend_src_in, R.drawable.blend_src_in);
609         testSingleDrawInstruction("blend_src_out",
610                 R.raw.blend_src_out, R.drawable.blend_src_out);
611         testSingleDrawInstruction("blend_src_over",
612                 R.raw.blend_src_over, R.drawable.blend_src_over);
613         testSingleDrawInstruction("blend_xor",
614                 R.raw.blend_xor, R.drawable.blend_xor);
615     }
616 
617     @Test
testDrawInstructionsClip()618     public void testDrawInstructionsClip() throws Throwable {
619         if (!drawDataParcel()) {
620             return;
621         }
622         testSingleDrawInstruction("clip_path",
623                 R.raw.clip_path, R.drawable.clip_path);
624         testSingleDrawInstruction("clip_rect",
625                 R.raw.clip_rect, R.drawable.clip_rect);
626     }
627 
628     @Test
testDrawInstructionsText()629     public void testDrawInstructionsText() throws Throwable {
630         if (!drawDataParcel()) {
631             return;
632         }
633         testSingleDrawInstruction("draw_text",
634                 R.raw.draw_text, R.drawable.draw_text);
635         testSingleDrawInstruction("text_font",
636                 R.raw.text_font, R.drawable.text_font);
637         testSingleDrawInstruction("text_font_monospace",
638                 R.raw.text_font_monospace, R.drawable.text_font_monospace);
639         testSingleDrawInstruction("text_font_san_serif",
640                 R.raw.text_font_san_serif, R.drawable.text_font_san_serif);
641         testSingleDrawInstruction("text_font_san_serif_800",
642                 R.raw.text_font_san_serif_800, R.drawable.text_font_san_serif_800);
643         testSingleDrawInstruction("text_font_serif",
644                 R.raw.text_font_serif, R.drawable.text_font_serif);
645     }
646 
647     @Test
testDrawInstructionsGradient()648     public void testDrawInstructionsGradient() throws Throwable {
649         if (!drawDataParcel()) {
650             return;
651         }
652         testSingleDrawInstruction("linear_gradient_1",
653                 R.raw.linear_gradient_1, R.drawable.linear_gradient_1);
654         testSingleDrawInstruction("linear_gradient_2",
655                 R.raw.linear_gradient_2, R.drawable.linear_gradient_2);
656         testSingleDrawInstruction("radial_gradient_1",
657                 R.raw.radial_gradient_1, R.drawable.radial_gradient_1);
658         testSingleDrawInstruction("radial_gradient_2",
659                 R.raw.radial_gradient_2, R.drawable.radial_gradient_2);
660         testSingleDrawInstruction("sweep_gradient_1",
661                 R.raw.sweep_gradient_1, R.drawable.sweep_gradient_1);
662         testSingleDrawInstruction("sweep_gradient_2",
663                 R.raw.sweep_gradient_2, R.drawable.sweep_gradient_2);
664     }
665 
666     @Test
testDrawInstructionsMatrix()667     public void testDrawInstructionsMatrix() throws Throwable {
668         if (!drawDataParcel()) {
669             return;
670         }
671         testSingleDrawInstruction("matrix_rotate_1",
672                 R.raw.matrix_rotate_1, R.drawable.matrix_rotate_1);
673         testSingleDrawInstruction("matrix_rotate_2",
674                 R.raw.matrix_rotate_2, R.drawable.matrix_rotate_2);
675         testSingleDrawInstruction("matrix_scale_1",
676                 R.raw.matrix_scale_1, R.drawable.matrix_scale_1);
677         testSingleDrawInstruction("matrix_scale_2",
678                 R.raw.matrix_scale_2, R.drawable.matrix_scale_2);
679         testSingleDrawInstruction("matrix_skew",
680                 R.raw.matrix_skew, R.drawable.matrix_skew);
681         testSingleDrawInstruction("matrix_translate",
682                 R.raw.matrix_translate, R.drawable.matrix_translate);
683     }
684 
685     @Test
testDrawInstructionsMulti()686     public void testDrawInstructionsMulti() throws Throwable {
687         if (!drawDataParcel()) {
688             return;
689         }
690         testSingleDrawInstruction("multi_path",
691                 R.raw.multi_path, R.drawable.multi_path);
692         testSingleDrawInstruction("multiple_draw_commands",
693                 R.raw.multiple_draw_commands, R.drawable.multiple_draw_commands);
694     }
695 
696     @Test
testDrawInstructionsPaint()697     public void testDrawInstructionsPaint() throws Throwable {
698         if (!drawDataParcel()) {
699             return;
700         }
701         testSingleDrawInstruction("paint_filter_bitmap_1",
702                 R.raw.paint_filter_bitmap_1, R.drawable.paint_filter_bitmap_1);
703         testSingleDrawInstruction("paint_filter_bitmap_2",
704                 R.raw.paint_filter_bitmap_2, R.drawable.paint_filter_bitmap_2);
705         testSingleDrawInstruction("paint_filter_bitmap_3",
706                 R.raw.paint_filter_bitmap_3, R.drawable.paint_filter_bitmap_3);
707         testSingleDrawInstruction("paint_set_color",
708                 R.raw.paint_set_color, R.drawable.paint_set_color);
709         testSingleDrawInstruction("paint_set_stroke",
710                 R.raw.paint_set_stroke, R.drawable.paint_set_stroke);
711         testSingleDrawInstruction("paint_stroke_miter",
712                 R.raw.paint_stroke_miter, R.drawable.paint_stroke_miter);
713     }
714 
715     @Test
testDrawInstructionsPath()716     public void testDrawInstructionsPath() throws Throwable {
717         if (!drawDataParcel()) {
718             return;
719         }
720         testSingleDrawInstruction("path_cubic_to",
721                 R.raw.path_cubic_to, R.drawable.path_cubic_to);
722         testSingleDrawInstruction("path_line_to",
723                 R.raw.path_line_to, R.drawable.path_line_to);
724         testSingleDrawInstruction("path_quad_to",
725                 R.raw.path_quad_to, R.drawable.path_quad_to);
726         testSingleDrawInstruction("path_tween_test_1",
727                 R.raw.path_tween_test_1, R.drawable.path_tween_test_1);
728         testSingleDrawInstruction("path_tween_test_2",
729                 R.raw.path_tween_test_2, R.drawable.path_tween_test_2);
730         testSingleDrawInstruction("paint_stroke_miter",
731                 R.raw.paint_stroke_miter, R.drawable.paint_stroke_miter);
732     }
733 
734     @Test
testDrawInstructionsPorter()735     public void testDrawInstructionsPorter() throws Throwable {
736         if (!drawDataParcel()) {
737             return;
738         }
739         testSingleDrawInstruction("porter_add",
740                 R.raw.porter_add, R.drawable.porter_add);
741         testSingleDrawInstruction("porter_clear",
742                 R.raw.porter_clear, R.drawable.porter_clear);
743         testSingleDrawInstruction("porter_darken",
744                 R.raw.porter_darken, R.drawable.porter_darken);
745         testSingleDrawInstruction("porter_dst",
746                 R.raw.porter_dst, R.drawable.porter_dst);
747         testSingleDrawInstruction("porter_dst_atop",
748                 R.raw.porter_dst_atop, R.drawable.porter_dst_atop);
749         testSingleDrawInstruction("porter_dst_in",
750                 R.raw.porter_dst_in, R.drawable.porter_dst_in);
751         testSingleDrawInstruction("porter_dst_out",
752                 R.raw.porter_dst_out, R.drawable.porter_dst_out);
753         testSingleDrawInstruction("porter_dst_over",
754                 R.raw.porter_dst_over, R.drawable.porter_dst_over);
755         testSingleDrawInstruction("porter_lighten",
756                 R.raw.porter_lighten, R.drawable.porter_lighten);
757         testSingleDrawInstruction("porter_multiply",
758                 R.raw.porter_multiply, R.drawable.porter_multiply);
759         testSingleDrawInstruction("porter_overlay",
760                 R.raw.porter_overlay, R.drawable.porter_overlay);
761         testSingleDrawInstruction("porter_screen",
762                 R.raw.porter_screen, R.drawable.porter_screen);
763         testSingleDrawInstruction("porter_src",
764                 R.raw.porter_src, R.drawable.porter_src);
765         testSingleDrawInstruction("porter_src_atop",
766                 R.raw.porter_src_atop, R.drawable.porter_src_atop);
767         testSingleDrawInstruction("porter_src_in",
768                 R.raw.porter_src_in, R.drawable.porter_src_in);
769         testSingleDrawInstruction("porter_src_out",
770                 R.raw.porter_src_out, R.drawable.porter_src_out);
771         testSingleDrawInstruction("porter_src_over",
772                 R.raw.porter_src_over, R.drawable.porter_src_over);
773         testSingleDrawInstruction("porter_xor",
774                 R.raw.porter_xor, R.drawable.porter_xor);
775     }
776 
testSingleDrawInstruction(final String resourceName, final int docId, final int bitmapId)777     private void testSingleDrawInstruction(final String resourceName, final int docId,
778                 final int bitmapId) throws Throwable {
779         final RemoteViews.DrawInstructions drawInstructions =
780                 getDrawInstructions(mContext, docId);
781         final Bitmap expectedBitmap = getBitmapFromFile(mContext, bitmapId);
782         mRemoteViews = new RemoteViews(drawInstructions);
783         applyRemoteViews();
784         verifyBitmap(expectedBitmap.getWidth(), expectedBitmap.getHeight(), (actualBitmap) -> {
785             final float rmse = compareImages(expectedBitmap, actualBitmap, resourceName);
786             // reject if root-square-mean-error is above threshold and saves screenshots for debug
787             if (rmse > 8.0f) {
788                 try {
789                     final String actualPath = saveBitmapToFile(
790                             actualBitmap, resourceName + "_actual");
791                     final String expectedPath = saveBitmapToFile(
792                             expectedBitmap, resourceName + "_expected");
793                     Assert.fail("Failed validating " + resourceName + " rmse=" + rmse
794                             + ". Bitmaps saved as " + actualPath + " and " + expectedPath);
795                 } catch (IOException e) {
796                     throw new RuntimeException(e);
797                 }
798             }
799         });
800     }
801 
getDrawInstructions( final Context context, final int resourceId)802     private static RemoteViews.DrawInstructions getDrawInstructions(
803             final Context context, final int resourceId) throws IOException {
804         try (InputStream is = context.getResources().openRawResource(resourceId)
805         ) {
806             final byte[] bytes = new byte[(int) is.available()];
807             final int result = is.read(bytes);
808             return new RemoteViews.DrawInstructions.Builder(
809                     Collections.singletonList(bytes)).build();
810         }
811     }
812 
getBitmapFromFile(final Context context, final int resourceId)813     private static Bitmap getBitmapFromFile(final Context context, final int resourceId) {
814         final BitmapFactory.Options opts = new BitmapFactory.Options();
815         opts.inScaled = false;
816         return BitmapFactory.decodeResource(context.getResources(), resourceId, opts);
817     }
818 
saveBitmapToFile(final Bitmap image, final String resourceName)819     private String saveBitmapToFile(final Bitmap image, final String resourceName)
820             throws IOException {
821         final int quality = 100;
822         final File dir = mContext.getFilesDir();
823         if (!dir.exists()) {
824             dir.mkdirs();
825         }
826         final File dest = new File(dir, resourceName + "_" + System.currentTimeMillis() + ".png");
827         dest.createNewFile();
828         try (FileOutputStream fos = new FileOutputStream(dest)) {
829             image.compress(Bitmap.CompressFormat.PNG, quality, fos);
830         }
831         return dest.getAbsolutePath();
832     }
833 
verifyBitmap(final int width, final int height, final Consumer<Bitmap> cb)834     private void verifyBitmap(final int width, final int height, final Consumer<Bitmap> cb)
835             throws Throwable {
836         final Bitmap bitmap = blank(width, height);
837         mActivityRule.runOnUiThread(() -> {
838             mResult.measure(makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
839                     makeMeasureSpec(height, View.MeasureSpec.EXACTLY));
840             mResult.layout(0, 0, width, height);
841             mResult.draw(new Canvas(bitmap));
842         });
843         cb.accept(bitmap);
844         bitmap.recycle();
845     }
846 
blank(final int width, final int height)847     private Bitmap blank(final int width, final int height) {
848         final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
849         bitmap.eraseColor(0xFFAABBCC);
850         return bitmap;
851     }
852 
compareImages(Bitmap bitmap1, Bitmap bitmap2, String testLabel)853     private static float compareImages(Bitmap bitmap1, Bitmap bitmap2, String testLabel) {
854         if (bitmap1.getWidth() != bitmap2.getWidth()
855                 || bitmap1.getHeight() != bitmap2.getHeight()) {
856             throw new IllegalArgumentException("Size mismatches when running " + testLabel
857                     + " expected:[w=" + bitmap1.getWidth() + ",h=" + bitmap1.getHeight() + "]"
858                     + " actual:[w=" + bitmap2.getWidth() + ",h=" + bitmap2.getHeight() + "]");
859         }
860         float sqr_sum = 0;
861         int count = 0;
862         for (int y = 1; y < bitmap1.getHeight() - 1; y++) {
863             for (int x = 1; x < bitmap1.getWidth() - 1; x++) {
864                 int pix1 = bitmap1.getPixel(x, y);
865                 if (pix1 != bitmap1.getPixel(x, y - 1)
866                         || pix1 != bitmap1.getPixel(x, y + 1)
867                         || pix1 != bitmap1.getPixel(x - 1, y - 1)
868                         || pix1 != bitmap1.getPixel(x - 1, y - 1)) {
869                     // Skips if a pixel's color doesn't match its neighboring pixel
870                     continue;
871                 }
872                 // Ignores least significant bit
873                 float r1 = (pix1 & 0xFF0000) >> 17;
874                 float g1 = (pix1 & 0xFF00) >> 9;
875                 float b1 = (pix1 & 0xFF) >> 1;
876                 int pix2 = bitmap2.getPixel(x, y);
877                 float r2 = (pix2 & 0xFF0000) >> 17;
878                 float g2 = (pix2 & 0xFF00) >> 9;
879                 float b2 = (pix2 & 0xFF) >> 1;
880                 sqr_sum += (r1 - r2) * (r1 - r2);
881                 sqr_sum += (g1 - g2) * (g1 - g2);
882                 sqr_sum += (b1 - b2) * (b1 - b2);
883                 count += 3;
884             }
885         }
886         return (float) Math.sqrt(sqr_sum / count);
887     }
888 
verifyColorsOnFourCorners(final int expectedColor, final Bitmap bitmap)889     private static void verifyColorsOnFourCorners(final int expectedColor, final Bitmap bitmap) {
890         final int w = bitmap.getWidth();
891         final int h = bitmap.getHeight();
892         final int offset = 2;
893         assertEquals(expectedColor, bitmap.getPixel(offset, offset));
894         assertEquals(expectedColor, bitmap.getPixel(w - offset, offset));
895         assertEquals(expectedColor, bitmap.getPixel(offset, h - offset));
896         assertEquals(expectedColor, bitmap.getPixel(w - offset, h - offset));
897     }
898 
verifyClick( final View result, final MockBroadcastReceiver receiver, final int x, final int y, final int expectedValue)899     private void verifyClick(
900             final View result,
901             final MockBroadcastReceiver receiver,
902             final int x,
903             final int y,
904             final int expectedValue)
905             throws Throwable {
906         mActivityRule.runOnUiThread(() -> performClick(result, x, y));
907         final Intent intent = receiver.awaitIntent();
908         assertNotNull(intent);
909         assertEquals(expectedValue, intent.getIntExtra("mykey", 0));
910     }
911 
performClick(final View v, final int x, final int y)912     private static void performClick(final View v, final int x, final int y) {
913         final long ts = SystemClock.uptimeMillis();
914         final MotionEvent down = MotionEvent.obtain(
915                 ts,
916                 ts + 100,
917                 MotionEvent.ACTION_DOWN, x, y, 0);
918         final MotionEvent up = MotionEvent.obtain(
919                 ts + 200,
920                 ts + 300,
921                 MotionEvent.ACTION_UP, x, y, 0);
922         v.dispatchTouchEvent(down);
923         v.dispatchTouchEvent(up);
924     }
925 
926     @Test
testOnLoadClass()927     public void testOnLoadClass() {
928         mRemoteViews = new RemoteViews(Parcel.obtain());
929 
930         assertTrue(mRemoteViews.onLoadClass(AbsoluteLayout.class));
931         assertTrue(mRemoteViews.onLoadClass(AnalogClock.class));
932         assertTrue(mRemoteViews.onLoadClass(Button.class));
933         assertTrue(mRemoteViews.onLoadClass(CheckBox.class));
934         assertTrue(mRemoteViews.onLoadClass(Chronometer.class));
935         assertTrue(mRemoteViews.onLoadClass(FrameLayout.class));
936         assertTrue(mRemoteViews.onLoadClass(GridLayout.class));
937         assertTrue(mRemoteViews.onLoadClass(GridView.class));
938         assertTrue(mRemoteViews.onLoadClass(ImageButton.class));
939         assertTrue(mRemoteViews.onLoadClass(ImageView.class));
940         assertTrue(mRemoteViews.onLoadClass(LinearLayout.class));
941         assertTrue(mRemoteViews.onLoadClass(ListView.class));
942         assertTrue(mRemoteViews.onLoadClass(ProgressBar.class));
943         assertTrue(mRemoteViews.onLoadClass(RadioButton.class));
944         assertTrue(mRemoteViews.onLoadClass(RadioGroup.class));
945         assertTrue(mRemoteViews.onLoadClass(RelativeLayout.class));
946         assertTrue(mRemoteViews.onLoadClass(StackView.class));
947         assertTrue(mRemoteViews.onLoadClass(Switch.class));
948         assertTrue(mRemoteViews.onLoadClass(TextClock.class));
949         assertTrue(mRemoteViews.onLoadClass(TextView.class));
950         assertTrue(mRemoteViews.onLoadClass(ViewFlipper.class));
951 
952         // those classes without annotation @RemoteView
953         assertFalse(mRemoteViews.onLoadClass(EditText.class));
954         assertFalse(mRemoteViews.onLoadClass(DatePicker.class));
955         assertFalse(mRemoteViews.onLoadClass(NumberPicker.class));
956         assertFalse(mRemoteViews.onLoadClass(RatingBar.class));
957         assertFalse(mRemoteViews.onLoadClass(SeekBar.class));
958     }
959 
960     @Test
testDescribeContents()961     public void testDescribeContents() {
962         mRemoteViews = new RemoteViews(Parcel.obtain());
963         mRemoteViews.describeContents();
964     }
965 
966     @Test
testWriteToParcel()967     public void testWriteToParcel() throws Throwable {
968         mRemoteViews.setTextViewText(R.id.remoteView_text, "This is content");
969         mRemoteViews.setViewVisibility(R.id.remoteView_frame, View.GONE);
970 
971         // the package and layout are successfully written into parcel
972         mRemoteViews = parcelAndUnparcel(mRemoteViews);
973         applyRemoteViews();
974         assertEquals(PACKAGE_NAME, mRemoteViews.getPackage());
975         assertEquals(R.layout.remoteviews_good, mRemoteViews.getLayoutId());
976         assertEquals(
977                 "This is content",
978                 ((TextView) mResult.findViewById(R.id.remoteView_text)).getText().toString());
979         assertEquals(View.GONE, mResult.findViewById(R.id.remoteView_frame).getVisibility());
980 
981         // currently the flag is not used
982         parcelAndUnparcel(mRemoteViews, /* flags= */ -1);
983 
984         RemoteViews[] remote = RemoteViews.CREATOR.newArray(1);
985         assertNotNull(remote);
986         assertEquals(1, remote.length);
987     }
988 
989     @Test(expected=NullPointerException.class)
testWriteNullToParcel()990     public void testWriteNullToParcel() {
991         mRemoteViews.writeToParcel(null, 0);
992     }
993 
994     @Test
testWriteToParcel_landscapePortrait()995     public void testWriteToParcel_landscapePortrait() throws Throwable {
996         RemoteViews landscape = new RemoteViews(PACKAGE_NAME, R.layout.remoteviews_good);
997         landscape.setTextViewText(R.id.remoteView_text, "Hello world");
998         RemoteViews portrait = new RemoteViews(PACKAGE_NAME, R.layout.remoteviews_good);
999         portrait.setTextViewText(R.id.remoteView_text, "Hello world");
1000         int landscapeParcelledSize = getParcelledSize(landscape);
1001         mRemoteViews = new RemoteViews(landscape, portrait);
1002 
1003         mRemoteViews = parcelAndUnparcel(mRemoteViews);
1004         assertEquals(PACKAGE_NAME, mRemoteViews.getPackage());
1005         applyRemoteViews();
1006         assertEquals(
1007                 "Hello world",
1008                 ((TextView) mResult.findViewById(R.id.remoteView_text)).getText().toString());
1009 
1010         // The ApplicationInfo should only have been written once, so this should be much smaller
1011         // than twice the size of parcelling one RemoteViews.
1012         assertLessThan(landscapeParcelledSize * 2, getParcelledSize(mRemoteViews));
1013     }
1014 
1015     @Test
testWriteToParcel_sizeMap()1016     public void testWriteToParcel_sizeMap() throws Throwable {
1017         List<SizeF> sizes =
1018                 Arrays.asList(new SizeF(50, 50), new SizeF(100, 100), new SizeF(100, 200));
1019         Map<SizeF, RemoteViews> sizeMap = new ArrayMap<>();
1020         int singelParcelledSize = 0;
1021         for (SizeF size : sizes) {
1022             RemoteViews views = new RemoteViews(PACKAGE_NAME, R.layout.remoteviews_good);
1023             views.setTextViewText(R.id.remoteView_text, "Hello world");
1024             sizeMap.put(size, views);
1025             singelParcelledSize = getParcelledSize(views);
1026         }
1027         mRemoteViews = new RemoteViews(sizeMap);
1028 
1029         mRemoteViews = parcelAndUnparcel(mRemoteViews);
1030         assertEquals(PACKAGE_NAME, mRemoteViews.getPackage());
1031         applyRemoteViews();
1032         assertEquals(
1033                 "Hello world",
1034                 ((TextView) mResult.findViewById(R.id.remoteView_text)).getText().toString());
1035 
1036         // The ApplicationInfo should only have been written once, so this should be much smaller
1037         // than thrice the size of parcelling one RemoteViews.
1038         assertLessThan(singelParcelledSize * 3, getParcelledSize(mRemoteViews));
1039     }
1040 
1041     @Test(expected=NegativeArraySizeException.class)
testCreateNegativeSizedArray()1042     public void testCreateNegativeSizedArray() {
1043         RemoteViews.CREATOR.newArray(-1);
1044     }
1045 
1046     @Test
testSetImageViewBitmap()1047     public void testSetImageViewBitmap() throws Throwable {
1048         ImageView image = (ImageView) mResult.findViewById(R.id.remoteView_image);
1049         assertNull(image.getDrawable());
1050 
1051         Bitmap bitmap =
1052                 BitmapFactory.decodeResource(mContext.getResources(), R.drawable.testimage);
1053         mRemoteViews.setImageViewBitmap(R.id.remoteView_image, bitmap);
1054         reapplyRemoteViews();
1055         assertNotNull(image.getDrawable());
1056         WidgetTestUtils.assertEquals(bitmap, ((BitmapDrawable) image.getDrawable()).getBitmap());
1057 
1058         mRemoteViews.setImageViewBitmap(R.id.remoteView_absolute, bitmap);
1059         assertThrowsOnReapply(ActionException.class);
1060     }
1061 
1062     @Test
testSetImageViewBitmap_afterCopying()1063     public void testSetImageViewBitmap_afterCopying() throws Throwable {
1064         Bitmap bitmap =
1065                 BitmapFactory.decodeResource(mContext.getResources(), R.drawable.testimage);
1066         RemoteViews original =
1067                 new RemoteViews(mContext.getPackageName(), R.layout.remoteviews_good);
1068         original.setImageViewBitmap(R.id.remoteView_image, bitmap);
1069         mRemoteViews = new RemoteViews(original);
1070 
1071         applyRemoteViews();
1072 
1073         ImageView image = mResult.findViewById(R.id.remoteView_image);
1074         assertNotNull(image.getDrawable());
1075         WidgetTestUtils.assertEquals(bitmap, ((BitmapDrawable) image.getDrawable()).getBitmap());
1076     }
1077 
1078     @Test
testSetBitmap()1079     public void testSetBitmap() throws Throwable {
1080         ImageView image = (ImageView) mResult.findViewById(R.id.remoteView_image);
1081         assertNull(image.getDrawable());
1082 
1083         Bitmap bitmap =
1084                 BitmapFactory.decodeResource(mContext.getResources(), R.drawable.testimage);
1085         mRemoteViews.setBitmap(R.id.remoteView_image, "setImageBitmap", bitmap);
1086         reapplyRemoteViews();
1087         assertNotNull(image.getDrawable());
1088         WidgetTestUtils.assertEquals(bitmap, ((BitmapDrawable) image.getDrawable()).getBitmap());
1089 
1090         mRemoteViews.setBitmap(R.id.remoteView_absolute, "setImageBitmap", bitmap);
1091         assertThrowsOnReapply(ActionException.class);
1092 
1093         assertEquals(bitmap.getAllocationByteCount(), mRemoteViews.estimateMemoryUsage());
1094     }
1095 
1096     @Test
testSetBoolean()1097     public void testSetBoolean() throws Throwable {
1098         ProgressBar progress = (ProgressBar) mResult.findViewById(R.id.remoteView_progress);
1099         // the view uses style progressBarHorizontal, so the default is false
1100         assertFalse(progress.isIndeterminate());
1101 
1102         mRemoteViews.setBoolean(R.id.remoteView_progress, "setIndeterminate", true);
1103         reapplyRemoteViews();
1104         assertTrue(progress.isIndeterminate());
1105 
1106         mRemoteViews.setBoolean(R.id.remoteView_relative, "setIndeterminate", false);
1107         assertThrowsOnReapply(ActionException.class);
1108     }
1109 
1110     @Test
testSetCharSequence()1111     public void testSetCharSequence() throws Throwable {
1112         TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
1113         assertEquals("", textView.getText().toString());
1114 
1115         String expected = "test setCharSequence";
1116         mRemoteViews.setCharSequence(R.id.remoteView_text, "setText", expected);
1117         reapplyRemoteViews();
1118         assertEquals(expected, textView.getText().toString());
1119 
1120         mRemoteViews.setCharSequence(R.id.remoteView_text, "setText", null);
1121         reapplyRemoteViews();
1122         assertEquals("", textView.getText().toString());
1123 
1124         mRemoteViews.setCharSequence(R.id.remoteView_absolute, "setText", "");
1125         assertThrowsOnReapply(ActionException.class);
1126     }
1127 
1128     @Test
testSetCharSequenceAttr()1129     public void testSetCharSequenceAttr() throws Throwable {
1130         mRemoteViews.setCharSequenceAttr(R.id.remoteView_text, "setText", R.attr.themeString);
1131         applyNightModeThenApplyAndTest(false, () -> {
1132             TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
1133             assertEquals("Day", textView.getText().toString());
1134         });
1135 
1136         applyNightModeThenApplyAndTest(true, () -> {
1137             TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
1138             assertEquals("Night", textView.getText().toString());
1139         });
1140 
1141         mRemoteViews.setCharSequenceAttr(R.id.remoteView_absolute, "setText",
1142                 R.attr.themeColor);
1143         assertThrowsOnReapply(ActionException.class);
1144     }
1145 
1146     @Test
testSetInt()1147     public void testSetInt() throws Throwable {
1148         View view = mResult.findViewById(R.id.remoteView_chronometer);
1149         assertEquals(View.VISIBLE, view.getVisibility());
1150 
1151         mRemoteViews.setInt(R.id.remoteView_chronometer, "setVisibility", View.INVISIBLE);
1152         reapplyRemoteViews();
1153         assertEquals(View.INVISIBLE, view.getVisibility());
1154 
1155         mRemoteViews.setInt(R.id.remoteView_chronometer, "setVisibility", View.GONE);
1156         reapplyRemoteViews();
1157         assertEquals(View.GONE, view.getVisibility());
1158 
1159         mRemoteViews.setInt(R.id.remoteView_chronometer, "setVisibility", View.VISIBLE);
1160         reapplyRemoteViews();
1161         assertEquals(View.VISIBLE, view.getVisibility());
1162     }
1163 
1164     @Test
testSetString()1165     public void testSetString() throws Throwable {
1166         String format = "HH:MM:SS";
1167         Chronometer chronometer = (Chronometer) mResult.findViewById(R.id.remoteView_chronometer);
1168         assertNull(chronometer.getFormat());
1169 
1170         mRemoteViews.setString(R.id.remoteView_chronometer, "setFormat", format);
1171         reapplyRemoteViews();
1172         assertEquals(format, chronometer.getFormat());
1173 
1174         mRemoteViews.setString(R.id.remoteView_image, "setFormat", format);
1175         assertThrowsOnReapply(ActionException.class);
1176     }
1177 
1178     @Test
testSetUri()1179     public void testSetUri() throws Throwable {
1180         String path = getTestImagePath();
1181         File imagefile = new File(path);
1182 
1183         try {
1184             createSampleImage(imagefile, R.raw.testimage);
1185 
1186             Uri uri = Uri.parse(path);
1187             ImageView image = (ImageView) mResult.findViewById(R.id.remoteView_image);
1188             assertNull(image.getDrawable());
1189 
1190             mRemoteViews.setUri(R.id.remoteView_image, "setImageURI", uri);
1191             reapplyRemoteViews();
1192 
1193             Bitmap imageViewBitmap = ((BitmapDrawable) image.getDrawable()).getBitmap();
1194             Bitmap expectedBitmap = WidgetTestUtils.getUnscaledAndDitheredBitmap(
1195                     mContext.getResources(), R.raw.testimage, imageViewBitmap.getConfig());
1196             WidgetTestUtils.assertEquals(expectedBitmap, imageViewBitmap);
1197 
1198             mRemoteViews.setUri(R.id.remoteView_absolute, "setImageURI", uri);
1199             assertThrowsOnReapply(ActionException.class);
1200         } finally {
1201             // remove the test image file
1202             imagefile.delete();
1203         }
1204     }
1205 
1206     @Test
testSetTextColor()1207     public void testSetTextColor() throws Throwable {
1208         TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
1209 
1210         mRemoteViews.setTextColor(R.id.remoteView_text, R.color.testcolor1);
1211         reapplyRemoteViews();
1212         assertSame(ColorStateList.valueOf(R.color.testcolor1), textView.getTextColors());
1213 
1214         mRemoteViews.setTextColor(R.id.remoteView_text, R.color.testcolor2);
1215         reapplyRemoteViews();
1216         assertSame(ColorStateList.valueOf(R.color.testcolor2), textView.getTextColors());
1217 
1218         mRemoteViews.setTextColor(R.id.remoteView_absolute, R.color.testcolor1);
1219         assertThrowsOnReapply(ActionException.class);
1220     }
1221 
1222     @Test
testSetTextCompoundDrawables()1223     public void testSetTextCompoundDrawables() throws Throwable {
1224         TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
1225 
1226         TestUtils.verifyCompoundDrawables(textView, -1, -1, -1, -1);
1227 
1228         mRemoteViews.setTextViewCompoundDrawables(R.id.remoteView_text, R.drawable.start,
1229                 R.drawable.pass, R.drawable.failed, 0);
1230         reapplyRemoteViews();
1231         TestUtils.verifyCompoundDrawables(textView, R.drawable.start, R.drawable.failed,
1232                 R.drawable.pass, -1);
1233 
1234         mRemoteViews.setTextViewCompoundDrawables(R.id.remoteView_text, 0,
1235                 R.drawable.icon_black, R.drawable.icon_red, R.drawable.icon_green);
1236         reapplyRemoteViews();
1237         TestUtils.verifyCompoundDrawables(textView, -1,  R.drawable.icon_red, R.drawable.icon_black,
1238                 R.drawable.icon_green);
1239 
1240         mRemoteViews.setTextViewCompoundDrawables(R.id.remoteView_absolute, 0,
1241                 R.drawable.start, R.drawable.failed, 0);
1242         assertThrowsOnReapply(Throwable.class);
1243     }
1244 
1245     @Test
testSetTextCompoundDrawablesRelative()1246     public void testSetTextCompoundDrawablesRelative() throws Throwable {
1247         TextView textViewLtr = (TextView) mResult.findViewById(R.id.remoteView_text_ltr);
1248         TextView textViewRtl = (TextView) mResult.findViewById(R.id.remoteView_text_rtl);
1249 
1250         TestUtils.verifyCompoundDrawables(textViewLtr, -1, -1, -1, -1);
1251         TestUtils.verifyCompoundDrawables(textViewRtl, -1, -1, -1, -1);
1252 
1253         mRemoteViews.setTextViewCompoundDrawablesRelative(R.id.remoteView_text_ltr,
1254                 R.drawable.start, R.drawable.pass, R.drawable.failed, 0);
1255         mRemoteViews.setTextViewCompoundDrawablesRelative(R.id.remoteView_text_rtl,
1256                 R.drawable.start, R.drawable.pass, R.drawable.failed, 0);
1257         reapplyRemoteViews();
1258         TestUtils.verifyCompoundDrawables(textViewLtr, R.drawable.start, R.drawable.failed,
1259                 R.drawable.pass, -1);
1260         TestUtils.verifyCompoundDrawables(textViewRtl, R.drawable.failed, R.drawable.start,
1261                 R.drawable.pass, -1);
1262 
1263         mRemoteViews.setTextViewCompoundDrawablesRelative(R.id.remoteView_text_ltr, 0,
1264                 R.drawable.icon_black, R.drawable.icon_red, R.drawable.icon_green);
1265         mRemoteViews.setTextViewCompoundDrawablesRelative(R.id.remoteView_text_rtl, 0,
1266                 R.drawable.icon_black, R.drawable.icon_red, R.drawable.icon_green);
1267         reapplyRemoteViews();
1268         TestUtils.verifyCompoundDrawables(textViewLtr, -1, R.drawable.icon_red,
1269                 R.drawable.icon_black, R.drawable.icon_green);
1270         TestUtils.verifyCompoundDrawables(textViewRtl, R.drawable.icon_red, -1,
1271                 R.drawable.icon_black, R.drawable.icon_green);
1272 
1273         mRemoteViews.setTextViewCompoundDrawablesRelative(R.id.remoteView_absolute, 0,
1274                 R.drawable.start, R.drawable.failed, 0);
1275         assertThrowsOnReapply(Throwable.class);
1276     }
1277 
1278     @LargeTest
1279     @Test
testSetOnClickPendingIntent()1280     public void testSetOnClickPendingIntent() throws Throwable {
1281         Uri uri = Uri.parse("ctstest://RemoteView/test");
1282         ActivityMonitor am = mInstrumentation.addMonitor(MockURLSpanTestActivity.class.getName(),
1283                 null, false);
1284         View view = mResult.findViewById(R.id.remoteView_image);
1285         view.performClick();
1286         Activity newActivity = am.waitForActivityWithTimeout(TEST_TIMEOUT);
1287         assertNull(newActivity);
1288 
1289         Intent intent = new Intent(Intent.ACTION_VIEW, uri);
1290         PendingIntent pendingIntent =
1291                 PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);
1292         mRemoteViews.setOnClickPendingIntent(R.id.remoteView_image, pendingIntent);
1293         reapplyRemoteViews();
1294         mActivityRule.runOnUiThread(() -> view.performClick());
1295         newActivity = am.waitForActivityWithTimeout(TEST_TIMEOUT);
1296 
1297         // Proto serialization removes PendingIntents from the RemoteViews.
1298         if (isProtoTest) {
1299             assertNull(newActivity);
1300             return;
1301         }
1302         assertNotNull(newActivity);
1303         assertTrue(newActivity instanceof MockURLSpanTestActivity);
1304         newActivity.finish();
1305     }
1306 
1307     @LargeTest
1308     @Test
1309     @RequiresFlagsEnabled(FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR)
testSetOnStylusHandwritingPendingIntent()1310     public void testSetOnStylusHandwritingPendingIntent() throws Throwable {
1311         ActivityMonitor am = mInstrumentation.addMonitor(MockURLSpanTestActivity.class.getName(),
1312                 null, false);
1313 
1314         Uri uri = Uri.parse("ctstest://RemoteView/test");
1315         Intent intent = new Intent(Intent.ACTION_VIEW, uri);
1316         PendingIntent pendingIntent =
1317                 PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);
1318         mRemoteViews.setOnStylusHandwritingPendingIntent(R.id.remoteView_image, pendingIntent);
1319         reapplyRemoteViews();
1320         View view = mResult.findViewById(R.id.remoteView_image);
1321         assertNotNull(view);
1322         mActivityRule.runOnUiThread(
1323                 () -> {
1324                     Runnable callback = view.getHandwritingDelegatorCallback();
1325                     // Proto serialization removes PendingIntents from the RemoteViews.
1326                     if (isProtoTest) {
1327                         assertNull(callback);
1328                     } else {
1329                         callback.run();
1330                     }
1331                 });
1332 
1333         Activity newActivity = am.waitForActivityWithTimeout(TEST_TIMEOUT);
1334         if (isProtoTest) {
1335             assertNull(newActivity);
1336             return;
1337         }
1338         assertNotNull(newActivity);
1339         assertTrue(newActivity instanceof MockURLSpanTestActivity);
1340         newActivity.finish();
1341     }
1342 
1343     @LargeTest
1344     @Test
1345     @RequiresFlagsEnabled(FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR)
testSetOnStylusHandwritingPendingIntent_null()1346     public void testSetOnStylusHandwritingPendingIntent_null() throws Throwable {
1347         ActivityMonitor am = mInstrumentation.addMonitor(MockURLSpanTestActivity.class.getName(),
1348                 null, false);
1349 
1350         Uri uri = Uri.parse("ctstest://RemoteView/test");
1351         Intent intent = new Intent(Intent.ACTION_VIEW, uri);
1352         PendingIntent pendingIntent =
1353                 PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);
1354         mRemoteViews.setOnStylusHandwritingPendingIntent(R.id.remoteView_image, pendingIntent);
1355         reapplyRemoteViews();
1356         mRemoteViews.setOnStylusHandwritingPendingIntent(R.id.remoteView_image, null);
1357         reapplyRemoteViews();
1358 
1359         View view = mResult.findViewById(R.id.remoteView_image);
1360         assertNull(view.getHandwritingDelegatorCallback());
1361     }
1362 
1363     @Test
testSetOnCheckedChangeResponse()1364     public void testSetOnCheckedChangeResponse() throws Throwable {
1365         String action = "my-checked-change-action";
1366         MockBroadcastReceiver receiver =  new MockBroadcastReceiver();
1367         mContext.registerReceiver(receiver, new IntentFilter(action),
1368                 Context.RECEIVER_EXPORTED_UNAUDITED);
1369 
1370         Intent intent = new Intent(action).setPackage(mContext.getPackageName());
1371         PendingIntent pendingIntent =
1372                 PendingIntent.getBroadcast(
1373                         mContext,
1374                         0,
1375                         intent,
1376                         PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
1377         mRemoteViews.setOnCheckedChangeResponse(R.id.remoteView_checkBox,
1378                 RemoteViews.RemoteResponse.fromPendingIntent(pendingIntent));
1379 
1380         // View being checked to true should launch the intent with the extra set to true.
1381         CompoundButton view = mResult.findViewById(R.id.remoteView_checkBox);
1382         reapplyRemoteViews();
1383         mActivityRule.runOnUiThread(() -> view.setChecked(true));
1384         mInstrumentation.waitForIdleSync();
1385         // Proto serialization removes PendingIntents from the RemoteViews.
1386         if (isProtoTest) {
1387             assertNull(receiver.mIntent);
1388         } else {
1389             assertNotNull(receiver.mIntent);
1390             assertTrue(receiver.mIntent.getBooleanExtra(RemoteViews.EXTRA_CHECKED, false));
1391         }
1392 
1393         // Changing the checked state from a RemoteViews action should not launch the intent.
1394         receiver.mIntent = null;
1395         mRemoteViews.setCompoundButtonChecked(R.id.remoteView_checkBox, false);
1396         reapplyRemoteViews();
1397         mInstrumentation.waitForIdleSync();
1398         if (isProtoTest) {
1399             assertNull(receiver.mIntent);
1400         } else {
1401             assertFalse(view.isChecked());
1402             assertNull(receiver.mIntent);
1403         }
1404 
1405         // View being checked to false should launch the intent with the extra set to false.
1406         receiver.mIntent = null;
1407         reapplyRemoteViews();
1408         mActivityRule.runOnUiThread(() -> view.setChecked(true));
1409         mActivityRule.runOnUiThread(() -> view.setChecked(false));
1410         mInstrumentation.waitForIdleSync();
1411         if (isProtoTest) {
1412             assertNull(receiver.mIntent);
1413         } else {
1414             assertNotNull(receiver.mIntent);
1415             assertFalse(receiver.mIntent.getBooleanExtra(RemoteViews.EXTRA_CHECKED, true));
1416         }
1417     }
1418 
1419     @Test
testSetLong()1420     public void testSetLong() throws Throwable {
1421         long base1 = 50;
1422         long base2 = -50;
1423         Chronometer chronometer = (Chronometer) mResult.findViewById(R.id.remoteView_chronometer);
1424 
1425         mRemoteViews.setLong(R.id.remoteView_chronometer, "setBase", base1);
1426         reapplyRemoteViews();
1427         assertEquals(base1, chronometer.getBase());
1428 
1429         mRemoteViews.setLong(R.id.remoteView_chronometer, "setBase", base2);
1430         reapplyRemoteViews();
1431         assertEquals(base2, chronometer.getBase());
1432 
1433         mRemoteViews.setLong(R.id.remoteView_absolute, "setBase", base1);
1434         assertThrowsOnReapply(ActionException.class);
1435     }
1436 
1437     @Test
testSetFloat()1438     public void testSetFloat() throws Throwable {
1439         LinearLayout linearLayout = (LinearLayout) mResult.findViewById(R.id.remoteView_linear);
1440         assertTrue(linearLayout.getWeightSum() <= 0.0f);
1441 
1442         mRemoteViews.setFloat(R.id.remoteView_linear, "setWeightSum", 0.5f);
1443         reapplyRemoteViews();
1444         assertEquals(0.5f, linearLayout.getWeightSum(), 0.001f);
1445 
1446         mRemoteViews.setFloat(R.id.remoteView_absolute, "setWeightSum", 1.0f);
1447         assertThrowsOnReapply(ActionException.class);
1448     }
1449 
1450     @Test
testSetByte()1451     public void testSetByte() throws Throwable {
1452         MyRemotableView customView = (MyRemotableView) mResult.findViewById(R.id.remoteView_custom);
1453         assertEquals(0, customView.getByteField());
1454 
1455         byte b = 100;
1456         mRemoteViews.setByte(R.id.remoteView_custom, "setByteField", b);
1457         reapplyRemoteViews();
1458         assertEquals(b, customView.getByteField());
1459 
1460         mRemoteViews.setByte(R.id.remoteView_absolute, "setByteField", b);
1461         assertThrowsOnReapply(ActionException.class);
1462     }
1463 
1464     @Test
testSetChar()1465     public void testSetChar() throws Throwable {
1466         MyRemotableView customView = (MyRemotableView) mResult.findViewById(R.id.remoteView_custom);
1467         assertEquals('\u0000', customView.getCharField());
1468 
1469         mRemoteViews.setChar(R.id.remoteView_custom, "setCharField", 'q');
1470         reapplyRemoteViews();
1471         assertEquals('q', customView.getCharField());
1472 
1473         mRemoteViews.setChar(R.id.remoteView_absolute, "setCharField", 'w');
1474         assertThrowsOnReapply(ActionException.class);
1475     }
1476 
1477     @Test
testSetDouble()1478     public void testSetDouble() throws Throwable {
1479         MyRemotableView customView = (MyRemotableView) mResult.findViewById(R.id.remoteView_custom);
1480         assertEquals(0.0, customView.getDoubleField(), 0.0f);
1481 
1482         mRemoteViews.setDouble(R.id.remoteView_custom, "setDoubleField", 0.5);
1483         reapplyRemoteViews();
1484         assertEquals(0.5, customView.getDoubleField(), 0.001f);
1485 
1486         mRemoteViews.setDouble(R.id.remoteView_absolute, "setDoubleField", 1.0);
1487         assertThrowsOnReapply(ActionException.class);
1488     }
1489 
1490     @Test
testSetShort()1491     public void testSetShort() throws Throwable {
1492         MyRemotableView customView = (MyRemotableView) mResult.findViewById(R.id.remoteView_custom);
1493         assertEquals(0, customView.getShortField());
1494 
1495         short s = 25;
1496         mRemoteViews.setShort(R.id.remoteView_custom, "setShortField", s);
1497         reapplyRemoteViews();
1498         assertEquals(s, customView.getShortField());
1499 
1500         mRemoteViews.setShort(R.id.remoteView_absolute, "setShortField", s);
1501         assertThrowsOnReapply(ActionException.class);
1502     }
1503 
1504     @Test
testSetBundle()1505     public void testSetBundle() throws Throwable {
1506         MyRemotableView customView = (MyRemotableView) mResult.findViewById(R.id.remoteView_custom);
1507         assertNull(customView.getBundleField());
1508 
1509         final Bundle bundle = new Bundle();
1510         bundle.putString("STR", "brexit");
1511         bundle.putInt("INT", 2016);
1512         mRemoteViews.setBundle(R.id.remoteView_custom, "setBundleField", bundle);
1513         reapplyRemoteViews();
1514         final Bundle fromRemote = customView.getBundleField();
1515         // Proto serialization removes PendingIntents from the RemoteViews.
1516         if (isProtoTest) {
1517             assertNull(fromRemote);
1518         } else {
1519             assertEquals("brexit", fromRemote.getString("STR", ""));
1520             assertEquals(2016, fromRemote.getInt("INT", 0));
1521 
1522             mRemoteViews.setBundle(R.id.remoteView_absolute, "setBundleField", bundle);
1523             assertThrowsOnReapply(ActionException.class);
1524         }
1525     }
1526 
1527     @Test
testSetIntent()1528     public void testSetIntent() throws Throwable {
1529         MyRemotableView customView = (MyRemotableView) mResult.findViewById(R.id.remoteView_custom);
1530         assertNull(customView.getIntentField());
1531 
1532         final Intent intent = new Intent(mContext, SwitchCtsActivity.class);
1533         intent.putExtra("STR", "brexit");
1534         intent.putExtra("INT", 2016);
1535         mRemoteViews.setIntent(R.id.remoteView_custom, "setIntentField", intent);
1536         reapplyRemoteViews();
1537         final Intent fromRemote = customView.getIntentField();
1538         // Proto serialization removes PendingIntents from the RemoteViews.
1539         if (isProtoTest) {
1540             assertNull(fromRemote);
1541         } else {
1542             assertEquals(
1543                     SwitchCtsActivity.class.getName(), fromRemote.getComponent().getClassName());
1544             assertEquals("brexit", fromRemote.getStringExtra("STR"));
1545             assertEquals(2016, fromRemote.getIntExtra("INT", 0));
1546 
1547             mRemoteViews.setIntent(R.id.remoteView_absolute, "setIntentField", intent);
1548             assertThrowsOnReapply(ActionException.class);
1549         }
1550     }
1551 
1552     @Test
testSetBlendMode()1553     public void testSetBlendMode() throws Throwable {
1554         ImageView imageView = mResult.findViewById(R.id.remoteView_image);
1555 
1556         mRemoteViews.setBlendMode(R.id.remoteView_image, "setImageTintBlendMode", BlendMode.PLUS);
1557         reapplyRemoteViews();
1558         assertEquals(BlendMode.PLUS, imageView.getImageTintBlendMode());
1559 
1560         mRemoteViews.setBlendMode(R.id.remoteView_image, "setImageTintBlendMode", BlendMode.SRC_IN);
1561         reapplyRemoteViews();
1562         assertEquals(BlendMode.SRC_IN, imageView.getImageTintBlendMode());
1563 
1564         mRemoteViews.setBlendMode(R.id.remoteView_image, "setImageTintBlendMode", null);
1565         reapplyRemoteViews();
1566         assertNull(imageView.getImageTintBlendMode());
1567     }
1568 
1569     @Test
testRemoveAllViews()1570     public void testRemoveAllViews() throws Throwable {
1571         ViewGroup root = (ViewGroup) mResult.findViewById(R.id.remoteViews_good);
1572         assertTrue(root.getChildCount() > 0);
1573 
1574         mRemoteViews.removeAllViews(R.id.remoteViews_good);
1575         reapplyRemoteViews();
1576         assertEquals(0, root.getChildCount());
1577     }
1578 
1579     @Test
testAddView()1580     public void testAddView() throws Throwable {
1581         ViewGroup root = (ViewGroup) mResult.findViewById(R.id.remoteViews_good);
1582         int originalChildCount = root.getChildCount();
1583 
1584         assertNull(root.findViewById(R.id.remoteView_frame_extra));
1585 
1586         // Create a RemoteViews wrapper around a layout and add it to our root
1587         RemoteViews extra = new RemoteViews(PACKAGE_NAME, R.layout.remoteviews_extra);
1588         mRemoteViews.addView(R.id.remoteViews_good, extra);
1589         reapplyRemoteViews();
1590 
1591         // Verify that our root has that layout as its last (new) child
1592         assertEquals(originalChildCount + 1, root.getChildCount());
1593         assertNotNull(root.findViewById(R.id.remoteView_frame_extra));
1594         assertEquals(R.id.remoteView_frame_extra, root.getChildAt(originalChildCount).getId());
1595     }
1596 
1597     @Test
testSetLabelFor()1598     public void testSetLabelFor() throws Throwable {
1599         View labelView = mResult.findViewById(R.id.remoteView_label);
1600         assertEquals(View.NO_ID, labelView.getLabelFor());
1601 
1602         mRemoteViews.setLabelFor(R.id.remoteView_label, R.id.remoteView_text);
1603         reapplyRemoteViews();
1604         assertEquals(R.id.remoteView_text, labelView.getLabelFor());
1605     }
1606 
1607     @Test
testSetAccessibilityTraversalAfter()1608     public void testSetAccessibilityTraversalAfter() throws Throwable {
1609         View textView = mResult.findViewById(R.id.remoteView_text);
1610 
1611         mRemoteViews.setAccessibilityTraversalAfter(R.id.remoteView_text, R.id.remoteView_frame);
1612         reapplyRemoteViews();
1613         assertEquals(R.id.remoteView_frame, textView.getAccessibilityTraversalAfter());
1614 
1615         mRemoteViews.setAccessibilityTraversalAfter(R.id.remoteView_text, R.id.remoteView_linear);
1616         reapplyRemoteViews();
1617         assertEquals(R.id.remoteView_linear, textView.getAccessibilityTraversalAfter());
1618     }
1619 
1620     @Test
testSetAccessibilityTraversalBefore()1621     public void testSetAccessibilityTraversalBefore() throws Throwable {
1622         View textView = mResult.findViewById(R.id.remoteView_text);
1623 
1624         mRemoteViews.setAccessibilityTraversalBefore(R.id.remoteView_text, R.id.remoteView_frame);
1625         reapplyRemoteViews();
1626         assertEquals(R.id.remoteView_frame, textView.getAccessibilityTraversalBefore());
1627 
1628         mRemoteViews.setAccessibilityTraversalBefore(R.id.remoteView_text, R.id.remoteView_linear);
1629         reapplyRemoteViews();
1630         assertEquals(R.id.remoteView_linear, textView.getAccessibilityTraversalBefore());
1631     }
1632 
1633     @Test
testSetViewPadding()1634     public void testSetViewPadding() throws Throwable {
1635         View textView = mResult.findViewById(R.id.remoteView_text);
1636 
1637         mRemoteViews.setViewPadding(R.id.remoteView_text, 10, 20, 30, 40);
1638         reapplyRemoteViews();
1639         assertEquals(10, textView.getPaddingLeft());
1640         assertEquals(20, textView.getPaddingTop());
1641         assertEquals(30, textView.getPaddingRight());
1642         assertEquals(40, textView.getPaddingBottom());
1643 
1644         mRemoteViews.setViewPadding(R.id.remoteView_text, 40, 30, 20, 10);
1645         reapplyRemoteViews();
1646         assertEquals(40, textView.getPaddingLeft());
1647         assertEquals(30, textView.getPaddingTop());
1648         assertEquals(20, textView.getPaddingRight());
1649         assertEquals(10, textView.getPaddingBottom());
1650     }
1651 
1652     @Test
testSetViewLayoutMargin()1653     public void testSetViewLayoutMargin() throws Throwable {
1654         View textView = mResult.findViewById(R.id.remoteView_text);
1655 
1656         mRemoteViews.setViewLayoutMargin(R.id.remoteView_text, MARGIN_LEFT, 10, COMPLEX_UNIT_PX);
1657         mRemoteViews.setViewLayoutMargin(R.id.remoteView_text, MARGIN_TOP, 20, COMPLEX_UNIT_PX);
1658         mRemoteViews.setViewLayoutMargin(R.id.remoteView_text, MARGIN_RIGHT, 30, COMPLEX_UNIT_PX);
1659         mRemoteViews.setViewLayoutMargin(R.id.remoteView_text, MARGIN_BOTTOM, 40, COMPLEX_UNIT_PX);
1660         reapplyRemoteViews();
1661         assertMargins(textView, 10, 20, 30, 40);
1662 
1663         mRemoteViews.setViewLayoutMargin(R.id.remoteView_text, MARGIN_LEFT, 10, COMPLEX_UNIT_DIP);
1664         mRemoteViews.setViewLayoutMargin(R.id.remoteView_text, MARGIN_TOP, 20, COMPLEX_UNIT_DIP);
1665         mRemoteViews.setViewLayoutMargin(R.id.remoteView_text, MARGIN_RIGHT, 30, COMPLEX_UNIT_DIP);
1666         mRemoteViews.setViewLayoutMargin(R.id.remoteView_text, MARGIN_BOTTOM, 40, COMPLEX_UNIT_DIP);
1667         reapplyRemoteViews();
1668         DisplayMetrics displayMetrics = textView.getResources().getDisplayMetrics();
1669         assertMargins(
1670                 textView,
1671                 resolveDimenOffset(10, COMPLEX_UNIT_DIP, displayMetrics),
1672                 resolveDimenOffset(20, COMPLEX_UNIT_DIP, displayMetrics),
1673                 resolveDimenOffset(30, COMPLEX_UNIT_DIP, displayMetrics),
1674                 resolveDimenOffset(40, COMPLEX_UNIT_DIP, displayMetrics));
1675     }
1676 
1677     @Test
testSetViewLayoutMargin_layoutDirection()1678     public void testSetViewLayoutMargin_layoutDirection() throws Throwable {
1679         View textViewLtr = mResult.findViewById(R.id.remoteView_text_ltr);
1680         mRemoteViews.setViewLayoutMargin(textViewLtr.getId(), MARGIN_START, 10, COMPLEX_UNIT_DIP);
1681         mRemoteViews.setViewLayoutMargin(textViewLtr.getId(), MARGIN_TOP, 20, COMPLEX_UNIT_DIP);
1682         mRemoteViews.setViewLayoutMargin(textViewLtr.getId(), MARGIN_END, 30, COMPLEX_UNIT_DIP);
1683         mRemoteViews.setViewLayoutMargin(textViewLtr.getId(), MARGIN_BOTTOM, 40, COMPLEX_UNIT_DIP);
1684         reapplyRemoteViews();
1685         DisplayMetrics displayMetrics = textViewLtr.getResources().getDisplayMetrics();
1686         assertMargins(
1687                 textViewLtr,
1688                 resolveDimenOffset(10, COMPLEX_UNIT_DIP, displayMetrics),
1689                 resolveDimenOffset(20, COMPLEX_UNIT_DIP, displayMetrics),
1690                 resolveDimenOffset(30, COMPLEX_UNIT_DIP, displayMetrics),
1691                 resolveDimenOffset(40, COMPLEX_UNIT_DIP, displayMetrics));
1692 
1693         View textViewRtl = mResult.findViewById(R.id.remoteView_text_rtl);
1694         mRemoteViews.setViewLayoutMargin(textViewRtl.getId(), MARGIN_START, 10, COMPLEX_UNIT_DIP);
1695         mRemoteViews.setViewLayoutMargin(textViewRtl.getId(), MARGIN_TOP, 20, COMPLEX_UNIT_DIP);
1696         mRemoteViews.setViewLayoutMargin(textViewRtl.getId(), MARGIN_END, 30, COMPLEX_UNIT_DIP);
1697         mRemoteViews.setViewLayoutMargin(textViewRtl.getId(), MARGIN_BOTTOM, 40, COMPLEX_UNIT_DIP);
1698         reapplyRemoteViews();
1699         displayMetrics = textViewRtl.getResources().getDisplayMetrics();
1700         assertMargins(
1701                 textViewRtl,
1702                 resolveDimenOffset(30, COMPLEX_UNIT_DIP, displayMetrics),
1703                 resolveDimenOffset(20, COMPLEX_UNIT_DIP, displayMetrics),
1704                 resolveDimenOffset(10, COMPLEX_UNIT_DIP, displayMetrics),
1705                 resolveDimenOffset(40, COMPLEX_UNIT_DIP, displayMetrics));
1706     }
1707 
1708     @Test
testSetViewLayoutMarginDimen()1709     public void testSetViewLayoutMarginDimen() throws Throwable {
1710         View textView = mResult.findViewById(R.id.remoteView_text);
1711         mRemoteViews.setViewLayoutMarginDimen(
1712                 R.id.remoteView_text, MARGIN_LEFT, R.dimen.textview_padding_left);
1713         mRemoteViews.setViewLayoutMarginDimen(
1714                 R.id.remoteView_text, MARGIN_TOP, R.dimen.textview_padding_top);
1715         mRemoteViews.setViewLayoutMarginDimen(
1716                 R.id.remoteView_text, MARGIN_RIGHT, R.dimen.textview_padding_right);
1717         mRemoteViews.setViewLayoutMarginDimen(
1718                 R.id.remoteView_text, MARGIN_BOTTOM, R.dimen.textview_padding_bottom);
1719         reapplyRemoteViews();
1720         assertMargins(
1721                 textView,
1722                 textView.getResources().getDimensionPixelOffset(R.dimen.textview_padding_left),
1723                 textView.getResources().getDimensionPixelOffset(R.dimen.textview_padding_top),
1724                 textView.getResources().getDimensionPixelOffset(R.dimen.textview_padding_right),
1725                 textView.getResources().getDimensionPixelOffset(R.dimen.textview_padding_bottom));
1726     }
1727 
1728     @Test
testSetViewLayoutMarginDimen_layoutDirection()1729     public void testSetViewLayoutMarginDimen_layoutDirection() throws Throwable {
1730         View textViewLtr = mResult.findViewById(R.id.remoteView_text_ltr);
1731         mRemoteViews.setViewLayoutMarginDimen(
1732                 R.id.remoteView_text_ltr, MARGIN_START, R.dimen.textview_padding_left);
1733         mRemoteViews.setViewLayoutMarginDimen(
1734                 R.id.remoteView_text_ltr, MARGIN_TOP, R.dimen.textview_padding_top);
1735         mRemoteViews.setViewLayoutMarginDimen(
1736                 R.id.remoteView_text_ltr, MARGIN_END, R.dimen.textview_padding_right);
1737         mRemoteViews.setViewLayoutMarginDimen(
1738                 R.id.remoteView_text_ltr, MARGIN_BOTTOM, R.dimen.textview_padding_bottom);
1739         reapplyRemoteViews();
1740         assertMargins(
1741                 textViewLtr,
1742                 textViewLtr.getResources().getDimensionPixelOffset(R.dimen.textview_padding_left),
1743                 textViewLtr.getResources().getDimensionPixelOffset(R.dimen.textview_padding_top),
1744                 textViewLtr.getResources().getDimensionPixelOffset(R.dimen.textview_padding_right),
1745                 textViewLtr.getResources().getDimensionPixelOffset(
1746                         R.dimen.textview_padding_bottom));
1747 
1748         View textViewRtl = mResult.findViewById(R.id.remoteView_text_rtl);
1749         mRemoteViews.setViewLayoutMarginDimen(
1750                 R.id.remoteView_text_rtl, MARGIN_START, R.dimen.textview_padding_left);
1751         mRemoteViews.setViewLayoutMarginDimen(
1752                 R.id.remoteView_text_rtl, MARGIN_TOP, R.dimen.textview_padding_top);
1753         mRemoteViews.setViewLayoutMarginDimen(
1754                 R.id.remoteView_text_rtl, MARGIN_END, R.dimen.textview_padding_right);
1755         mRemoteViews.setViewLayoutMarginDimen(
1756                 R.id.remoteView_text_rtl, MARGIN_BOTTOM, R.dimen.textview_padding_bottom);
1757         reapplyRemoteViews();
1758         assertMargins(
1759                 textViewRtl,
1760                 textViewRtl.getResources().getDimensionPixelOffset(R.dimen.textview_padding_right),
1761                 textViewRtl.getResources().getDimensionPixelOffset(R.dimen.textview_padding_top),
1762                 textViewRtl.getResources().getDimensionPixelOffset(R.dimen.textview_padding_left),
1763                 textViewRtl.getResources().getDimensionPixelOffset(
1764                         R.dimen.textview_padding_bottom));
1765     }
1766 
1767     @Test
testSetViewLayoutMarginAttr()1768     public void testSetViewLayoutMarginAttr() throws Throwable {
1769         mRemoteViews.setViewLayoutMarginAttr(
1770                 R.id.remoteView_text, MARGIN_LEFT, R.attr.themeDimension);
1771         mRemoteViews.setViewLayoutMarginAttr(
1772                 R.id.remoteView_text, MARGIN_TOP, R.attr.themeDimension2);
1773         mRemoteViews.setViewLayoutMarginAttr(
1774                 R.id.remoteView_text, MARGIN_RIGHT, R.attr.themeDimension3);
1775         mRemoteViews.setViewLayoutMarginAttr(
1776                 R.id.remoteView_text, MARGIN_BOTTOM, R.attr.themeDimension4);
1777 
1778         applyNightModeThenApplyAndTest(false, () -> {
1779             View textView = mResult.findViewById(R.id.remoteView_text);
1780             DisplayMetrics displayMetrics = textView.getResources().getDisplayMetrics();
1781             assertMargins(
1782                     textView,
1783                     resolveDimenOffset(5.5123f, COMPLEX_UNIT_DIP, displayMetrics),
1784                     resolveDimenOffset(2.5f, COMPLEX_UNIT_DIP, displayMetrics),
1785                     resolveDimenOffset(3.5f, COMPLEX_UNIT_DIP, displayMetrics),
1786                     resolveDimenOffset(4.5f, COMPLEX_UNIT_DIP, displayMetrics));
1787         });
1788 
1789         applyNightModeThenApplyAndTest(true, () -> {
1790             View textView = mResult.findViewById(R.id.remoteView_text);
1791             DisplayMetrics displayMetrics = textView.getResources().getDisplayMetrics();
1792             assertMargins(
1793                     textView,
1794                     resolveDimenOffset(7.5123f, COMPLEX_UNIT_DIP, displayMetrics),
1795                     resolveDimenOffset(4.5f, COMPLEX_UNIT_DIP, displayMetrics),
1796                     resolveDimenOffset(5.5f, COMPLEX_UNIT_DIP, displayMetrics),
1797                     resolveDimenOffset(6.5f, COMPLEX_UNIT_DIP, displayMetrics));
1798         });
1799 
1800         // Test that zeros resolve to 0
1801         mRemoteViews.setViewLayoutMarginAttr(R.id.remoteView_text, MARGIN_LEFT, 0);
1802         mRemoteViews.setViewLayoutMarginAttr(R.id.remoteView_text, MARGIN_TOP, 0);
1803         mRemoteViews.setViewLayoutMarginAttr(R.id.remoteView_text, MARGIN_RIGHT, 0);
1804         applyNightModeThenApplyAndTest(false, () -> {
1805             View textView = mResult.findViewById(R.id.remoteView_text);
1806             DisplayMetrics displayMetrics = textView.getResources().getDisplayMetrics();
1807             assertMargins(textView, 0, 0, 0,
1808                     resolveDimenOffset(4.5f, COMPLEX_UNIT_DIP, displayMetrics));
1809         });
1810         applyNightModeThenApplyAndTest(true, () -> {
1811             View textView = mResult.findViewById(R.id.remoteView_text);
1812             DisplayMetrics displayMetrics = textView.getResources().getDisplayMetrics();
1813             assertMargins(textView, 0, 0, 0,
1814                     resolveDimenOffset(6.5f, COMPLEX_UNIT_DIP, displayMetrics));
1815         });
1816 
1817         mRemoteViews.setViewLayoutMarginAttr(
1818                 R.id.remoteView_text, MARGIN_LEFT, R.attr.themeColor);
1819         assertThrowsOnReapply(ActionException.class);
1820     }
1821 
1822     @Test
testSetViewLayoutMarginAttr_layoutDirection()1823     public void testSetViewLayoutMarginAttr_layoutDirection() throws Throwable {
1824         mRemoteViews.setViewLayoutMarginAttr(
1825                 R.id.remoteView_text_ltr, MARGIN_START, R.attr.themeDimension);
1826         mRemoteViews.setViewLayoutMarginAttr(
1827                 R.id.remoteView_text_ltr, MARGIN_TOP, R.attr.themeDimension2);
1828         mRemoteViews.setViewLayoutMarginAttr(
1829                 R.id.remoteView_text_ltr, MARGIN_END, R.attr.themeDimension3);
1830         mRemoteViews.setViewLayoutMarginAttr(
1831                 R.id.remoteView_text_ltr, MARGIN_BOTTOM, R.attr.themeDimension4);
1832 
1833         mRemoteViews.setViewLayoutMarginAttr(
1834                 R.id.remoteView_text_rtl, MARGIN_START, R.attr.themeDimension);
1835         mRemoteViews.setViewLayoutMarginAttr(
1836                 R.id.remoteView_text_rtl, MARGIN_TOP, R.attr.themeDimension2);
1837         mRemoteViews.setViewLayoutMarginAttr(
1838                 R.id.remoteView_text_rtl, MARGIN_END, R.attr.themeDimension3);
1839         mRemoteViews.setViewLayoutMarginAttr(
1840                 R.id.remoteView_text_rtl, MARGIN_BOTTOM, R.attr.themeDimension4);
1841 
1842         reapplyRemoteViews();
1843 
1844         View textViewLtr = mResult.findViewById(R.id.remoteView_text_ltr);
1845         DisplayMetrics displayMetrics = textViewLtr.getResources().getDisplayMetrics();
1846 
1847         assertMargins(
1848                 textViewLtr,
1849                 resolveDimenOffset(5.5123f, COMPLEX_UNIT_DIP, displayMetrics),
1850                 resolveDimenOffset(2.5f, COMPLEX_UNIT_DIP, displayMetrics),
1851                 resolveDimenOffset(3.5f, COMPLEX_UNIT_DIP, displayMetrics),
1852                 resolveDimenOffset(4.5f, COMPLEX_UNIT_DIP, displayMetrics));
1853 
1854         View textViewRtl = mResult.findViewById(R.id.remoteView_text_rtl);
1855         assertMargins(
1856                 textViewRtl,
1857                 resolveDimenOffset(3.5f, COMPLEX_UNIT_DIP, displayMetrics),
1858                 resolveDimenOffset(2.5f, COMPLEX_UNIT_DIP, displayMetrics),
1859                 resolveDimenOffset(5.5123f, COMPLEX_UNIT_DIP, displayMetrics),
1860                 resolveDimenOffset(4.5f, COMPLEX_UNIT_DIP, displayMetrics));
1861     }
1862 
1863     @Test
testSetViewLayoutWidth()1864     public void testSetViewLayoutWidth() throws Throwable {
1865         View textView = mResult.findViewById(R.id.remoteView_text);
1866         DisplayMetrics displayMetrics = textView.getResources().getDisplayMetrics();
1867 
1868         mRemoteViews.setViewLayoutWidth(R.id.remoteView_text, 10, COMPLEX_UNIT_PX);
1869         reapplyRemoteViews();
1870         assertEquals(10, textView.getLayoutParams().width);
1871 
1872         mRemoteViews.setViewLayoutWidth(R.id.remoteView_text, 20, COMPLEX_UNIT_DIP);
1873         reapplyRemoteViews();
1874         assertEquals(
1875                 resolveDimenSize(20, COMPLEX_UNIT_DIP, displayMetrics),
1876                 textView.getLayoutParams().width);
1877 
1878         mRemoteViews.setViewLayoutWidth(
1879                 R.id.remoteView_text, ViewGroup.LayoutParams.MATCH_PARENT, COMPLEX_UNIT_PX);
1880         reapplyRemoteViews();
1881         assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, textView.getLayoutParams().width);
1882     }
1883 
1884     @Test
testSetViewLayoutWidthDimen()1885     public void testSetViewLayoutWidthDimen() throws Throwable {
1886         View textView = mResult.findViewById(R.id.remoteView_text);
1887         mRemoteViews.setViewLayoutWidthDimen(R.id.remoteView_text, R.dimen.textview_fixed_width);
1888         reapplyRemoteViews();
1889         assertEquals(
1890                 textView.getResources().getDimensionPixelSize(R.dimen.textview_fixed_width),
1891                 textView.getLayoutParams().width);
1892 
1893         mRemoteViews.setViewLayoutWidthDimen(R.id.remoteView_text, 0);
1894         reapplyRemoteViews();
1895         assertEquals(0, textView.getLayoutParams().width);
1896     }
1897 
1898     @Test
testSetViewLayoutWidthAttr()1899     public void testSetViewLayoutWidthAttr() throws Throwable {
1900         View textView = mResult.findViewById(R.id.remoteView_text);
1901         mRemoteViews.setViewLayoutWidthAttr(R.id.remoteView_text, R.attr.themeDimension);
1902         reapplyRemoteViews();
1903         assertEquals(
1904                 resolveDimenSize(5.5123f, COMPLEX_UNIT_DIP,
1905                         textView.getResources().getDisplayMetrics()),
1906                 textView.getLayoutParams().width);
1907 
1908         mRemoteViews.setViewLayoutWidthAttr(R.id.remoteView_text, 0);
1909         reapplyRemoteViews();
1910         assertEquals(0, textView.getLayoutParams().width);
1911 
1912         mRemoteViews.setViewLayoutWidthAttr(R.id.remoteView_text, R.attr.themeColor);
1913         assertThrowsOnReapply(ActionException.class);
1914     }
1915 
1916     @Test
testSetViewLayoutHeight()1917     public void testSetViewLayoutHeight() throws Throwable {
1918         View textView = mResult.findViewById(R.id.remoteView_text);
1919         DisplayMetrics displayMetrics = textView.getResources().getDisplayMetrics();
1920 
1921         mRemoteViews.setViewLayoutHeight(R.id.remoteView_text, 10, COMPLEX_UNIT_PX);
1922         reapplyRemoteViews();
1923         assertEquals(10, textView.getLayoutParams().height);
1924 
1925         mRemoteViews.setViewLayoutHeight(R.id.remoteView_text, 20, COMPLEX_UNIT_DIP);
1926         reapplyRemoteViews();
1927         assertEquals(
1928                 resolveDimenSize(20, COMPLEX_UNIT_DIP, displayMetrics),
1929                 textView.getLayoutParams().height);
1930 
1931         mRemoteViews.setViewLayoutHeight(
1932                 R.id.remoteView_text, ViewGroup.LayoutParams.MATCH_PARENT, COMPLEX_UNIT_PX);
1933         reapplyRemoteViews();
1934         assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, textView.getLayoutParams().height);
1935     }
1936 
1937     @Test
testSetViewLayoutHeightDimen()1938     public void testSetViewLayoutHeightDimen() throws Throwable {
1939         View textView = mResult.findViewById(R.id.remoteView_text);
1940         mRemoteViews.setViewLayoutHeightDimen(R.id.remoteView_text, R.dimen.textview_fixed_height);
1941         reapplyRemoteViews();
1942         assertEquals(
1943                 textView.getResources().getDimensionPixelSize(R.dimen.textview_fixed_height),
1944                 textView.getLayoutParams().height);
1945 
1946         mRemoteViews.setViewLayoutHeightDimen(R.id.remoteView_text, 0);
1947         reapplyRemoteViews();
1948         assertEquals(0, textView.getLayoutParams().height);
1949     }
1950 
1951     @Test
testSetViewLayoutHeightAttr()1952     public void testSetViewLayoutHeightAttr() throws Throwable {
1953         View textView = mResult.findViewById(R.id.remoteView_text);
1954         mRemoteViews.setViewLayoutHeightAttr(R.id.remoteView_text, R.attr.themeDimension);
1955         reapplyRemoteViews();
1956         assertEquals(
1957                 resolveDimenSize(5.5123f, COMPLEX_UNIT_DIP,
1958                         textView.getResources().getDisplayMetrics()),
1959                 textView.getLayoutParams().height);
1960 
1961         mRemoteViews.setViewLayoutHeightAttr(R.id.remoteView_text, 0);
1962         reapplyRemoteViews();
1963         assertEquals(0, textView.getLayoutParams().height);
1964 
1965         mRemoteViews.setViewLayoutHeightAttr(
1966                 R.id.remoteView_text, R.attr.themeColor);
1967         assertThrowsOnReapply(ActionException.class);
1968     }
1969 
1970     @Test
testSetIntDimen_fromResources()1971     public void testSetIntDimen_fromResources() throws Throwable {
1972         TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
1973         int expectedValue = mContext.getResources().getDimensionPixelSize(R.dimen.popup_row_height);
1974 
1975         mRemoteViews.setIntDimen(R.id.remoteView_text, "setCompoundDrawablePadding",
1976                 R.dimen.popup_row_height);
1977         reapplyRemoteViews();
1978         assertEquals(expectedValue, textView.getCompoundDrawablePadding());
1979 
1980         // test that passing 0 for the dimen sets 0 on the method.
1981         mRemoteViews.setIntDimen(R.id.remoteView_text, "setCompoundDrawablePadding", 0);
1982         reapplyRemoteViews();
1983         assertEquals(0, textView.getCompoundDrawablePadding());
1984 
1985         mRemoteViews.setIntDimen(R.id.remoteView_text, "setCompoundDrawablePadding",
1986                 R.color.testcolor1);
1987         assertThrowsOnReapply(ActionException.class);
1988     }
1989 
1990     @Test
testSetIntDimen_fromUnitDimension()1991     public void testSetIntDimen_fromUnitDimension() throws Throwable {
1992         TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
1993         DisplayMetrics displayMetrics = textView.getResources().getDisplayMetrics();
1994 
1995         mRemoteViews.setIntDimen(R.id.remoteView_text, "setCompoundDrawablePadding",
1996                 12f, COMPLEX_UNIT_DIP);
1997         reapplyRemoteViews();
1998         assertEquals(resolveDimenSize(12f, COMPLEX_UNIT_DIP, displayMetrics),
1999                 textView.getCompoundDrawablePadding());
2000 
2001         mRemoteViews.setIntDimen(R.id.remoteView_text, "setCompoundDrawablePadding",
2002                 12f, TypedValue.COMPLEX_UNIT_SP);
2003         reapplyRemoteViews();
2004         assertEquals(resolveDimenSize(12f, TypedValue.COMPLEX_UNIT_SP, displayMetrics),
2005                 textView.getCompoundDrawablePadding());
2006 
2007         mRemoteViews.setIntDimen(R.id.remoteView_text, "setCompoundDrawablePadding",
2008                 12f, TypedValue.COMPLEX_UNIT_PX);
2009         reapplyRemoteViews();
2010         assertEquals(resolveDimenSize(12f, TypedValue.COMPLEX_UNIT_PX, displayMetrics),
2011                 textView.getCompoundDrawablePadding());
2012 
2013         mRemoteViews.setIntDimen(R.id.remoteView_text, "setCompoundDrawablePadding",
2014                 12f, 123456);
2015         assertThrowsOnReapply(ActionException.class);
2016     }
2017 
2018     @Test
testSetIntDimenAttr()2019     public void testSetIntDimenAttr() throws Throwable {
2020         mRemoteViews.setIntDimenAttr(R.id.remoteView_text, "setCompoundDrawablePadding",
2021                 R.attr.themeDimension);
2022         applyNightModeThenApplyAndTest(false, () -> {
2023             TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
2024             assertEquals(resolveDimenSize(5.5123f, COMPLEX_UNIT_DIP,
2025                     textView.getResources().getDisplayMetrics()),
2026                     textView.getCompoundDrawablePadding());
2027         });
2028 
2029         applyNightModeThenApplyAndTest(true, () -> {
2030             TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
2031             assertEquals(resolveDimenSize(7.5123f, COMPLEX_UNIT_DIP,
2032                     textView.getResources().getDisplayMetrics()),
2033                     textView.getCompoundDrawablePadding());
2034         });
2035 
2036         // test that 0 resolves to 0
2037         mRemoteViews.setIntDimenAttr(R.id.remoteView_text, "setCompoundDrawablePadding", 0);
2038         applyNightModeThenApplyAndTest(false, () -> {
2039             TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
2040             assertEquals(0, textView.getCompoundDrawablePadding());
2041         });
2042 
2043         applyNightModeThenApplyAndTest(true, () -> {
2044             TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
2045             assertEquals(0, textView.getCompoundDrawablePadding());
2046         });
2047 
2048         mRemoteViews.setIntDimenAttr(R.id.remoteView_text, "setCompoundDrawablePadding",
2049                 R.attr.themeColor);
2050         assertThrowsOnReapply(ActionException.class);
2051     }
2052 
2053 
2054     @Test
testSetFloatDimen_fromResources()2055     public void testSetFloatDimen_fromResources() throws Throwable {
2056         TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
2057 
2058         mRemoteViews.setFloatDimen(R.id.remoteView_text, "setTextScaleX",
2059                 R.dimen.remoteviews_float_dimen);
2060         reapplyRemoteViews();
2061         assertEquals(textView.getResources().getDimension(R.dimen.remoteviews_float_dimen),
2062                 textView.getTextScaleX(), 1e-4f);
2063 
2064         // test that passing 0 for the dimen sets 0f on the method.
2065         mRemoteViews.setFloatDimen(R.id.remoteView_text, "setTextScaleX", 0);
2066         reapplyRemoteViews();
2067         assertEquals(0f, textView.getTextScaleX(), 1e-4f);
2068 
2069         mRemoteViews.setFloatDimen(R.id.remoteView_text, "setTextScaleX", R.color.testcolor1);
2070         assertThrowsOnReapply(ActionException.class);
2071     }
2072 
2073     @Test
testSetFloatDimen_fromUnitDimension()2074     public void testSetFloatDimen_fromUnitDimension() throws Throwable {
2075         TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
2076         DisplayMetrics displayMetrics = textView.getResources().getDisplayMetrics();
2077 
2078         mRemoteViews.setFloatDimen(R.id.remoteView_text, "setTextScaleX",
2079                 3.5f, COMPLEX_UNIT_DIP);
2080         reapplyRemoteViews();
2081         assertEquals(TypedValue.applyDimension(COMPLEX_UNIT_DIP, 3.5f, displayMetrics),
2082                 textView.getTextScaleX(), 1e-4f);
2083 
2084         mRemoteViews.setFloatDimen(R.id.remoteView_text, "setTextScaleX",
2085                 3.5f, TypedValue.COMPLEX_UNIT_SP);
2086         reapplyRemoteViews();
2087         assertEquals(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 3.5f, displayMetrics),
2088                 textView.getTextScaleX(), 1e-4f);
2089 
2090         mRemoteViews.setFloatDimen(R.id.remoteView_text, "setTextScaleX",
2091                 3.5f, TypedValue.COMPLEX_UNIT_PX);
2092         reapplyRemoteViews();
2093         assertEquals(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 3.5f, displayMetrics),
2094                 textView.getTextScaleX(), 1e-4f);
2095 
2096         mRemoteViews.setFloatDimen(R.id.remoteView_text, "setTextScaleX",
2097                 3.5f, 123456);
2098         assertThrowsOnReapply(ActionException.class);
2099     }
2100 
2101     @Test
testSetFloatDimenAttr()2102     public void testSetFloatDimenAttr() throws Throwable {
2103         mRemoteViews.setFloatDimenAttr(R.id.remoteView_text, "setTextScaleX",
2104                 R.attr.themeDimension);
2105         applyNightModeThenApplyAndTest(false, () -> {
2106             TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
2107             assertEquals(TypedValue.applyDimension(COMPLEX_UNIT_DIP, 5.5123f,
2108                     textView.getResources().getDisplayMetrics()), textView.getTextScaleX(), 1e-4f);
2109         });
2110 
2111         applyNightModeThenApplyAndTest(true, () -> {
2112             TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
2113             assertEquals(TypedValue.applyDimension(COMPLEX_UNIT_DIP, 7.5123f,
2114                     textView.getResources().getDisplayMetrics()), textView.getTextScaleX(), 1e-4f);
2115         });
2116 
2117         // test that 0 resolves to 0
2118         mRemoteViews.setFloatDimenAttr(R.id.remoteView_text, "setTextScaleX", 0);
2119         applyNightModeThenApplyAndTest(false, () -> {
2120             TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
2121             assertEquals(0f, textView.getTextScaleX(), 1e-4f);
2122         });
2123 
2124         applyNightModeThenApplyAndTest(true, () -> {
2125             TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
2126             assertEquals(0f, textView.getTextScaleX(), 1e-4f);
2127         });
2128 
2129         mRemoteViews.setFloatDimenAttr(R.id.remoteView_text, "setTextScaleX",
2130                 R.attr.themeColor);
2131         assertThrowsOnReapply(ActionException.class);
2132     }
2133 
2134     @Test
testSetColor()2135     public void testSetColor() throws Throwable {
2136         TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
2137         int expectedValue = mContext.getColor(R.color.testcolor1);
2138 
2139         mRemoteViews.setColor(R.id.remoteView_text, "setTextColor", R.color.testcolor1);
2140         reapplyRemoteViews();
2141         assertSameColorStateList(ColorStateList.valueOf(expectedValue), textView.getTextColors());
2142 
2143         mRemoteViews.setColor(R.id.remoteView_text, "setTextColor", 0);
2144         reapplyRemoteViews();
2145         assertSameColorStateList(ColorStateList.valueOf(0), textView.getTextColors());
2146 
2147         mRemoteViews.setColor(R.id.remoteView_text, "setTextColor", R.dimen.popup_row_height);
2148         assertThrowsOnReapply(ActionException.class);
2149     }
2150 
2151     @Test
testSetColorAttr()2152     public void testSetColorAttr() throws Throwable {
2153         // Ensure the configuration is "light"
2154         mRemoteViews.setColorAttr(R.id.remoteView_text, "setTextColor", R.attr.themeColor);
2155 
2156         applyNightModeThenApplyAndTest(false, () -> {
2157             TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
2158             assertSameColorStateList(ColorStateList.valueOf(0x0f00ff00), textView.getTextColors());
2159         });
2160 
2161         // Switch to night mode
2162         applyNightModeThenApplyAndTest(true, () -> {
2163             TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
2164             assertSameColorStateList(ColorStateList.valueOf(0x0f00ffff), textView.getTextColors());
2165         });
2166 
2167         // Set to 0 and test that the colors are set to 0
2168         mRemoteViews.setColorAttr(R.id.remoteView_text, "setTextColor", 0);
2169 
2170         applyNightModeThenApplyAndTest(false, () -> {
2171             TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
2172             assertSameColorStateList(ColorStateList.valueOf(0), textView.getTextColors());
2173         });
2174 
2175         // Switch to night mode
2176         applyNightModeThenApplyAndTest(true, () -> {
2177             TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
2178             assertSameColorStateList(ColorStateList.valueOf(0), textView.getTextColors());
2179         });
2180 
2181         mRemoteViews.setColorAttr(R.id.remoteView_text, "setTextColor", R.attr.themeDimension);
2182         assertThrowsOnReapply(ActionException.class);
2183     }
2184 
2185     @Test
testSetColorStateList()2186     public void testSetColorStateList() throws Throwable {
2187         ProgressBar progressBar = mResult.findViewById(R.id.remoteView_progress);
2188 
2189         ColorStateList tintList = new ColorStateList(
2190                 new int[][] {{android.R.attr.state_checked}, {}},
2191                 new int[] {Color.BLACK, Color.WHITE});
2192         mRemoteViews.setColorStateList(R.id.remoteView_progress, "setProgressTintList", tintList);
2193         reapplyRemoteViews();
2194         assertSameColorStateList(tintList, progressBar.getProgressTintList());
2195 
2196         mRemoteViews.setColorStateList(R.id.remoteView_progress, "setProgressTintList", null);
2197         reapplyRemoteViews();
2198         assertNull(progressBar.getProgressTintList());
2199 
2200         TextView textView = mResult.findViewById(R.id.remoteView_text);
2201         mRemoteViews.setColorStateList(R.id.remoteView_text, "setTextColor", tintList);
2202         reapplyRemoteViews();
2203         assertSameColorStateList(tintList, textView.getTextColors());
2204 
2205         ColorStateList solid = ColorStateList.valueOf(Color.RED);
2206         mRemoteViews.setColorStateList(R.id.remoteView_text, "setBackgroundTintList", solid);
2207         reapplyRemoteViews();
2208         assertSameColorStateList(solid, textView.getBackgroundTintList());
2209     }
2210 
2211     @Test
testSetColorStateListAttr()2212     public void testSetColorStateListAttr() throws Throwable {
2213         mRemoteViews.setColorStateListAttr(R.id.remoteView_progress, "setProgressTintList",
2214                 R.attr.themeColor);
2215         applyNightModeThenApplyAndTest(false, () -> {
2216             ProgressBar progressBar = mResult.findViewById(R.id.remoteView_progress);
2217             assertSameColorStateList(ColorStateList.valueOf(0x0f00ff00),
2218                     progressBar.getProgressTintList());
2219         });
2220 
2221         applyNightModeThenApplyAndTest(true, () -> {
2222             ProgressBar progressBar = mResult.findViewById(R.id.remoteView_progress);
2223             assertSameColorStateList(ColorStateList.valueOf(0x0f00ffff),
2224                     progressBar.getProgressTintList());
2225         });
2226 
2227         mRemoteViews.setColorStateListAttr(R.id.remoteView_progress, "setProgressTintList", 0);
2228         applyNightModeThenApplyAndTest(false, () -> {
2229             ProgressBar progressBar = mResult.findViewById(R.id.remoteView_progress);
2230             assertSameColorStateList(null, progressBar.getProgressTintList());
2231         });
2232 
2233         applyNightModeThenApplyAndTest(true, () -> {
2234             ProgressBar progressBar = mResult.findViewById(R.id.remoteView_progress);
2235             assertSameColorStateList(null, progressBar.getProgressTintList());
2236         });
2237 
2238         mRemoteViews.setColorAttr(R.id.remoteView_progress,
2239                 "setProgressTintList", R.attr.themeDimension);
2240         assertThrowsOnReapply(ActionException.class);
2241     }
2242 
2243     @Test
testSetColorStateInt_nightMode()2244     public void testSetColorStateInt_nightMode() throws Throwable {
2245         TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
2246         mRemoteViews.setColorInt(R.id.remoteView_text, "setTextColor", Color.BLACK, Color.WHITE);
2247 
2248         applyNightModeThenReapplyAndTest(
2249                 false,
2250                 () -> assertSameColorStateList(ColorStateList.valueOf(Color.BLACK),
2251                         textView.getTextColors())
2252         );
2253         applyNightModeThenReapplyAndTest(
2254                 true,
2255                 () -> assertSameColorStateList(ColorStateList.valueOf(Color.WHITE),
2256                         textView.getTextColors())
2257         );
2258     }
2259 
2260     @Test
testSetColorStateList_fromResources()2261     public void testSetColorStateList_fromResources() throws Throwable {
2262         ProgressBar progressBar = (ProgressBar) mResult.findViewById(R.id.remoteView_progress);
2263         ColorStateList expectedValue = mContext.getColorStateList(R.color.testcolorstatelist1);
2264 
2265         mRemoteViews.setColorStateList(R.id.remoteView_progress, "setProgressTintList",
2266                 R.color.testcolorstatelist1);
2267         reapplyRemoteViews();
2268         assertSameColorStateList(expectedValue, progressBar.getProgressTintList());
2269 
2270         mRemoteViews.setColorStateList(R.id.remoteView_progress, "setProgressTintList",
2271                 R.color.testcolor1);
2272         reapplyRemoteViews();
2273         expectedValue = mContext.getResources().getColorStateList(R.color.testcolor1,
2274                 mContext.getTheme());
2275         assertSameColorStateList(expectedValue, progressBar.getProgressTintList());
2276 
2277         // 0 should resolve to null
2278         mRemoteViews.setColorStateList(R.id.remoteView_progress, "setProgressTintList", 0);
2279         reapplyRemoteViews();
2280         assertSameColorStateList(null, progressBar.getProgressTintList());
2281 
2282         mRemoteViews.setColorStateList(R.id.remoteView_progress, "setProgressTintList",
2283                 R.dimen.popup_row_height);
2284         assertThrowsOnReapply(ActionException.class);
2285     }
2286 
2287     @Test
testSetColorStateList_nightMode()2288     public void testSetColorStateList_nightMode() throws Throwable {
2289         TextView textView = (TextView) mResult.findViewById(R.id.remoteView_text);
2290         ColorStateList lightMode = ColorStateList.valueOf(Color.BLACK);
2291         ColorStateList darkMode = ColorStateList.valueOf(Color.WHITE);
2292         mRemoteViews.setColorStateList(R.id.remoteView_text, "setTextColor", lightMode, darkMode);
2293 
2294         applyNightModeThenReapplyAndTest(false,
2295                 () -> assertSameColorStateList(lightMode, textView.getTextColors()));
2296         applyNightModeThenReapplyAndTest(true,
2297                 () -> assertSameColorStateList(darkMode, textView.getTextColors()));
2298     }
2299 
2300     @Test
testSetViewOutlinePreferredRadius()2301     public void testSetViewOutlinePreferredRadius() throws Throwable {
2302         View root = mResult.findViewById(R.id.remoteViews_good);
2303         DisplayMetrics displayMetrics = root.getResources().getDisplayMetrics();
2304 
2305         mRemoteViews.setViewOutlinePreferredRadius(
2306                 R.id.remoteViews_good, 8, COMPLEX_UNIT_DIP);
2307         reapplyRemoteViews();
2308         assertEquals(
2309                 TypedValue.applyDimension(COMPLEX_UNIT_DIP, 8, displayMetrics),
2310                 ((RemoteViews.RemoteViewOutlineProvider) root.getOutlineProvider()).getRadius(),
2311                 0.1 /* delta */);
2312 
2313         mRemoteViews.setViewOutlinePreferredRadius(
2314                 R.id.remoteViews_good, 16, COMPLEX_UNIT_PX);
2315         reapplyRemoteViews();
2316         assertEquals(
2317                 16,
2318                 ((RemoteViews.RemoteViewOutlineProvider) root.getOutlineProvider()).getRadius(),
2319                 0.1 /* delta */);
2320     }
2321 
2322     @Test
testSetViewOutlinePreferredRadiusDimen()2323     public void testSetViewOutlinePreferredRadiusDimen() throws Throwable {
2324         View root = mResult.findViewById(R.id.remoteViews_good);
2325 
2326         mRemoteViews.setViewOutlinePreferredRadiusDimen(
2327                 R.id.remoteViews_good, R.dimen.popup_row_height);
2328         reapplyRemoteViews();
2329         assertEquals(
2330                 root.getResources().getDimension(R.dimen.popup_row_height),
2331                 ((RemoteViews.RemoteViewOutlineProvider) root.getOutlineProvider()).getRadius(),
2332                 0.1 /* delta */);
2333 
2334         mRemoteViews.setViewOutlinePreferredRadiusDimen(
2335                 R.id.remoteViews_good, 0);
2336         reapplyRemoteViews();
2337         assertEquals(
2338                 0,
2339                 ((RemoteViews.RemoteViewOutlineProvider) root.getOutlineProvider()).getRadius(),
2340                 0.1 /* delta */);
2341     }
2342 
2343     @Test
testSetViewOutlinePreferredRadiusAttr()2344     public void testSetViewOutlinePreferredRadiusAttr() throws Throwable {
2345         mRemoteViews.setViewOutlinePreferredRadiusAttr(
2346                 R.id.remoteViews_good, R.attr.themeDimension);
2347 
2348         applyNightModeThenApplyAndTest(false,
2349                 () -> {
2350                     View root = mResult.findViewById(R.id.remoteViews_good);
2351                     assertEquals(
2352                             TypedValue.applyDimension(COMPLEX_UNIT_DIP, 5.5123f,
2353                                     root.getResources().getDisplayMetrics()),
2354                             ((RemoteViews.RemoteViewOutlineProvider)
2355                                     root.getOutlineProvider()).getRadius(),
2356                             0.1 /* delta */);
2357                 });
2358 
2359         applyNightModeThenApplyAndTest(true,
2360                 () -> {
2361                     View root = mResult.findViewById(R.id.remoteViews_good);
2362                     assertEquals(
2363                             TypedValue.applyDimension(COMPLEX_UNIT_DIP, 7.5123f,
2364                                     root.getResources().getDisplayMetrics()),
2365                             ((RemoteViews.RemoteViewOutlineProvider)
2366                                     root.getOutlineProvider()).getRadius(),
2367                             0.1 /* delta */);
2368                 });
2369 
2370         mRemoteViews.setViewOutlinePreferredRadiusAttr(
2371                 R.id.remoteViews_good, R.attr.themeColor);
2372         assertThrowsOnReapply(ActionException.class);
2373     }
2374 
2375     @Test
testSetSwitchChecked()2376     public void testSetSwitchChecked() throws Throwable {
2377         Switch toggle = mResult.findViewById(R.id.remoteView_switch);
2378 
2379         mRemoteViews.setCompoundButtonChecked(R.id.remoteView_switch, true);
2380         reapplyRemoteViews();
2381         assertTrue(toggle.isChecked());
2382 
2383         mRemoteViews.setCompoundButtonChecked(R.id.remoteView_switch, false);
2384         reapplyRemoteViews();
2385         assertFalse(toggle.isChecked());
2386     }
2387 
2388     @Test
testSetCheckBoxChecked()2389     public void testSetCheckBoxChecked() throws Throwable {
2390         CheckBox checkBox = mResult.findViewById(R.id.remoteView_checkBox);
2391 
2392         mRemoteViews.setCompoundButtonChecked(R.id.remoteView_checkBox, true);
2393         reapplyRemoteViews();
2394         assertTrue(checkBox.isChecked());
2395 
2396         mRemoteViews.setCompoundButtonChecked(R.id.remoteView_checkBox, false);
2397         reapplyRemoteViews();
2398         assertFalse(checkBox.isChecked());
2399     }
2400 
2401     @Test
testSetRadioButtonChecked()2402     public void testSetRadioButtonChecked() throws Throwable {
2403         RadioButton radioButton = mResult.findViewById(R.id.remoteView_radioButton1);
2404 
2405         mRemoteViews.setCompoundButtonChecked(R.id.remoteView_radioButton1, true);
2406         reapplyRemoteViews();
2407         assertTrue(radioButton.isChecked());
2408 
2409         mRemoteViews.setCompoundButtonChecked(R.id.remoteView_radioButton1, false);
2410         reapplyRemoteViews();
2411         assertFalse(radioButton.isChecked());
2412     }
2413 
2414     @Test
testSetRadioGroupChecked()2415     public void testSetRadioGroupChecked() throws Throwable {
2416         RadioGroup radioGroup = mResult.findViewById(R.id.remoteView_radioGroup);
2417         RadioButton button1 = mResult.findViewById(R.id.remoteView_radioButton1);
2418         RadioButton button2 = mResult.findViewById(R.id.remoteView_radioButton2);
2419 
2420         mRemoteViews.setRadioGroupChecked(R.id.remoteView_radioGroup, R.id.remoteView_radioButton1);
2421         reapplyRemoteViews();
2422         assertTrue(button1.isChecked());
2423         assertFalse(button2.isChecked());
2424 
2425         mRemoteViews.setRadioGroupChecked(R.id.remoteView_radioGroup, R.id.remoteView_radioButton2);
2426         reapplyRemoteViews();
2427         assertFalse(button1.isChecked());
2428         assertTrue(button2.isChecked());
2429 
2430         mRemoteViews.setRadioGroupChecked(R.id.remoteView_radioGroup, -1);
2431         reapplyRemoteViews();
2432         assertFalse(button1.isChecked());
2433         assertFalse(button2.isChecked());
2434     }
2435 
2436     @Test
testCanRecycleView()2437     public void testCanRecycleView() throws Throwable {
2438         mRemoteViews =
2439                 new RemoteViews(
2440                         PACKAGE_NAME, R.layout.remoteviews_textview, R.id.view2 /* viewId */);
2441 
2442         applyRemoteViews();
2443 
2444         mRemoteViews =
2445                 new RemoteViews(
2446                         PACKAGE_NAME, R.layout.remoteviews_textview, R.id.view3 /* viewId */);
2447         assertFalse(mRemoteViews.canRecycleView(mResult));
2448 
2449         mRemoteViews = new RemoteViews(PACKAGE_NAME, R.layout.remoteviews_textview);
2450         assertFalse(mRemoteViews.canRecycleView(mResult));
2451 
2452         mRemoteViews =
2453                 new RemoteViews(
2454                         PACKAGE_NAME, R.layout.remoteviews_textview, R.id.view2 /* viewId */);
2455         assertTrue(mRemoteViews.canRecycleView(mResult));
2456 
2457         mRemoteViews =
2458                 new RemoteViews(PACKAGE_NAME, R.layout.listview_layout, R.id.view2 /* viewId */);
2459         assertFalse(mRemoteViews.canRecycleView(mResult));
2460 
2461         assertFalse(mRemoteViews.canRecycleView(null));
2462         assertFalse(mRemoteViews.canRecycleView(new View(mContext)));
2463     }
2464 
2465     @Test
testSameBitmapIsCached_landscapePortrait()2466     public void testSameBitmapIsCached_landscapePortrait() {
2467         Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.testimage);
2468         mRemoteViews.setBitmap(R.id.remoteView_image, "setImageBitmap", bitmap);
2469         long originalMemoryUsage = mRemoteViews.estimateMemoryUsage();
2470         assertEquals(bitmap.getAllocationByteCount(), originalMemoryUsage);
2471 
2472         // Create a remoteviews with the same bitmap in landscape and portrait, and confirm
2473         // that the cache is reconstructed after deserialization.
2474         RemoteViews landscape = new RemoteViews(PACKAGE_NAME, R.layout.remoteviews_good);
2475         landscape.setBitmap(R.id.remoteView_image, "setImageBitmap", bitmap);
2476         RemoteViews portrait = new RemoteViews(PACKAGE_NAME, R.layout.remoteviews_good);
2477         portrait.setBitmap(R.id.remoteView_image, "setImageBitmap", bitmap);
2478         mRemoteViews = new RemoteViews(portrait, landscape);
2479         assertEquals(originalMemoryUsage, mRemoteViews.estimateMemoryUsage());
2480 
2481         RemoteViews recreated;
2482         if (isProtoTest) {
2483             recreated = RemoteViewsUtil.recreateFromProto(mContext, mRemoteViews);
2484         } else {
2485             recreated = parcelAndUnparcel(mRemoteViews);
2486         }
2487         assertEquals(originalMemoryUsage, recreated.estimateMemoryUsage());
2488     }
2489 
2490     @Test
testSameBitmapIsCached_sizedViews()2491     public void testSameBitmapIsCached_sizedViews() {
2492         Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.testimage);
2493         mRemoteViews.setBitmap(R.id.remoteView_image, "setImageBitmap", bitmap);
2494         long originalMemoryUsage = mRemoteViews.estimateMemoryUsage();
2495         assertEquals(bitmap.getAllocationByteCount(), originalMemoryUsage);
2496 
2497         // Create a remoteviews with the same bitmap in different sized views, and confirm
2498         // that the cache is reconstructed after deserialization.
2499         RemoteViews child = new RemoteViews(PACKAGE_NAME, R.layout.remoteviews_good);
2500         child.setBitmap(R.id.remoteView_image, "setImageBitmap", bitmap);
2501         Map<SizeF, RemoteViews> map = new ArrayMap<>();
2502         map.put(new SizeF(0, 0), child);
2503         map.put(new SizeF(100, 100), child);
2504         map.put(new SizeF(200, 200), child);
2505         mRemoteViews = new RemoteViews(map);
2506         assertEquals(originalMemoryUsage, mRemoteViews.estimateMemoryUsage());
2507 
2508         RemoteViews recreated;
2509         if (isProtoTest) {
2510             recreated = RemoteViewsUtil.recreateFromProto(mContext, mRemoteViews);
2511         } else {
2512             recreated = parcelAndUnparcel(mRemoteViews);
2513         }
2514         assertEquals(originalMemoryUsage, recreated.estimateMemoryUsage());
2515     }
2516 
2517     @Test
testSameBitmapIsCached_nestedViews()2518     public void testSameBitmapIsCached_nestedViews() {
2519         Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.testimage);
2520         mRemoteViews.setBitmap(R.id.remoteView_image, "setImageBitmap", bitmap);
2521         long originalMemoryUsage = mRemoteViews.estimateMemoryUsage();
2522         assertEquals(bitmap.getAllocationByteCount(), originalMemoryUsage);
2523 
2524         // Create a remoteviews with the same bitmap in nested views, and confirm that the cache is
2525         // reconstructed after deserialization.
2526         RemoteViews child = new RemoteViews(PACKAGE_NAME, R.layout.remoteviews_good);
2527         child.setBitmap(R.id.remoteView_image, "setImageBitmap", bitmap);
2528         mRemoteViews.addView(R.id.remoteViews_good, child);
2529         mRemoteViews.addView(R.id.remoteViews_good, child);
2530         mRemoteViews.addView(R.id.remoteViews_good, child);
2531         assertEquals(originalMemoryUsage, mRemoteViews.estimateMemoryUsage());
2532 
2533         RemoteViews recreated;
2534         if (isProtoTest) {
2535             recreated = RemoteViewsUtil.recreateFromProto(mContext, mRemoteViews);
2536         } else {
2537             recreated  = parcelAndUnparcel(mRemoteViews);
2538         }
2539         assertEquals(originalMemoryUsage, recreated.estimateMemoryUsage());
2540     }
2541 
createSampleImage(File imagefile, int resid)2542     private void createSampleImage(File imagefile, int resid) throws IOException {
2543         try (InputStream source = mContext.getResources().openRawResource(resid);
2544              OutputStream target = new FileOutputStream(imagefile)) {
2545 
2546             byte[] buffer = new byte[1024];
2547             for (int len = source.read(buffer); len > 0; len = source.read(buffer)) {
2548                 target.write(buffer, 0, len);
2549             }
2550         }
2551     }
2552 
2553     /**
2554      * Sets the night mode, reapplies the remote views, runs test, and then restores the previous
2555      * night mode.
2556      */
applyNightModeThenReapplyAndTest(boolean nightMode, ThrowingRunnable test)2557     private void applyNightModeThenReapplyAndTest(boolean nightMode, ThrowingRunnable test)
2558             throws Throwable {
2559         applyNightModeAndTest(
2560                 nightMode,
2561                 () -> {
2562                     try {
2563                         reapplyRemoteViews();
2564                     } catch (Throwable e) {
2565                         throw new RuntimeException(e);
2566                     }
2567                 },
2568                 test);
2569     }
2570 
2571     /**
2572      * Sets the night mode, reapplies the remote views, runs test, and then restores the previous
2573      * night mode.
2574      */
applyNightModeThenApplyAndTest( boolean nightMode, ThrowingRunnable test)2575     private void applyNightModeThenApplyAndTest(
2576             boolean nightMode, ThrowingRunnable test) throws Throwable {
2577         applyNightModeAndTest(
2578                 nightMode,
2579                 () -> {
2580                     try {
2581                         applyRemoteViews();
2582                     } catch (Throwable e) {
2583                         throw new RuntimeException(e);
2584                     }
2585                 },
2586                 test);
2587     }
2588 
applyNightModeAndTest( boolean nightMode, ThrowingRunnable uiThreadSetup, ThrowingRunnable test)2589     private void applyNightModeAndTest(
2590             boolean nightMode, ThrowingRunnable uiThreadSetup, ThrowingRunnable test)
2591             throws Throwable {
2592         final String initialNightMode = changeNightMode(nightMode);
2593         try {
2594             mActivityRule.runOnUiThread(
2595                     () -> {
2596                         try {
2597                             uiThreadSetup.run();
2598                         } catch (Throwable e) {
2599                             throw new RuntimeException(e);
2600                         }
2601                     });
2602             test.run();
2603         } finally {
2604             runShellCommand("cmd uimode night " + initialNightMode);
2605         }
2606     }
2607 
assertMargins(View view, int left, int top, int right, int bottom)2608     private static void assertMargins(View view, int left, int top, int right, int bottom) {
2609         ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
2610         if (!(layoutParams instanceof ViewGroup.MarginLayoutParams)) {
2611             fail("View doesn't have MarginLayoutParams");
2612         }
2613 
2614         ViewGroup.MarginLayoutParams margins = (ViewGroup.MarginLayoutParams) layoutParams;
2615         assertEquals("[left margin]", left, margins.leftMargin);
2616         assertEquals("[top margin]", top, margins.topMargin);
2617         assertEquals("[right margin]", right, margins.rightMargin);
2618         assertEquals("[bottom margin]", bottom, margins.bottomMargin);
2619     }
2620 
resolveDimenOffset(float value, int unit, DisplayMetrics displayMetrics)2621     private static int resolveDimenOffset(float value, int unit, DisplayMetrics displayMetrics) {
2622         return TypedValue.complexToDimensionPixelOffset(
2623                 TypedValue.createComplexDimension(value, unit), displayMetrics);
2624     }
2625 
resolveDimenSize(float value, int unit, DisplayMetrics displayMetrics)2626     private static int resolveDimenSize(float value, int unit, DisplayMetrics displayMetrics) {
2627         return TypedValue.complexToDimensionPixelSize(
2628                 TypedValue.createComplexDimension(value, unit), displayMetrics);
2629     }
2630 
2631     private static final class MockBroadcastReceiver extends BroadcastReceiver {
2632 
2633         Intent mIntent;
2634 
2635         private CountDownLatch mCountDownLatch;
2636 
2637         @Override
onReceive(Context context, Intent intent)2638         public synchronized void onReceive(Context context, Intent intent) {
2639             mIntent = intent;
2640             if (mCountDownLatch != null) {
2641                 mCountDownLatch.countDown();
2642                 mCountDownLatch = null;
2643             }
2644         }
2645 
2646         /** Waits for an intent to be received and returns it. */
awaitIntent()2647         public Intent awaitIntent() {
2648             CountDownLatch countDownLatch;
2649             synchronized (this) {
2650                 // If we already have an intent, don't wait and just return it now.
2651                 if (mIntent != null) return getIntentAndResetLocked();
2652 
2653                 countDownLatch = new CountDownLatch(1);
2654                 mCountDownLatch = countDownLatch;
2655             }
2656 
2657             try {
2658                 // Note: if the latch already counted down, this will return true immediately.
2659                 countDownLatch.await(20, TimeUnit.SECONDS);
2660             } catch (InterruptedException e) {
2661                 throw new RuntimeException(e);
2662             }
2663 
2664             synchronized (this) {
2665                 if (mIntent == null) {
2666                     Assert.fail("Expected to receive a broadcast within 20 seconds");
2667                 }
2668 
2669                 return getIntentAndResetLocked();
2670             }
2671         }
2672 
getIntentAndResetLocked()2673         private Intent getIntentAndResetLocked() {
2674             final Intent intent = mIntent;
2675             mIntent = null;
2676             return intent;
2677         }
2678     }
2679 
assertSameColorStateList(ColorStateList expected, ColorStateList actual)2680     private void assertSameColorStateList(ColorStateList expected, ColorStateList actual) {
2681         assertEquals(expected == null ? null : expected.toString(),
2682                 actual == null ? null : actual.toString());
2683     }
2684 
assertThrowsOnReapply(Class<T> klass)2685     private <T extends Throwable> void assertThrowsOnReapply(Class<T> klass) throws Throwable {
2686         assertThrows(klass, this::reapplyRemoteViews);
2687     }
2688 
getCurrentNightMode()2689     private String getCurrentNightMode() {
2690         final String nightModeText = runShellCommand("cmd uimode night");
2691         final String[] nightModeSplit = nightModeText.split(":");
2692         if (nightModeSplit.length != 2) {
2693             fail("Failed to get initial night mode value from " + nightModeText);
2694         }
2695         return nightModeSplit[1].trim();
2696     }
2697 
2698     // Change the night mode and return the previous mode
changeNightMode(boolean nightMode)2699     private String changeNightMode(boolean nightMode) throws Exception {
2700         String previousMode = getCurrentNightMode();
2701         String wantedNightMode = nightMode ? "yes" : "no";
2702         runShellCommand("cmd uimode night " + wantedNightMode);
2703         waitUntil(
2704                 /* message= */ "Night mode did not change to " + wantedNightMode,
2705                 /* timeoutSecond= */ 5,
2706                 /* predicate= */ () -> wantedNightMode.equals(getCurrentNightMode())
2707         );
2708         return previousMode;
2709     }
2710 
applyRemoteViews()2711     private void applyRemoteViews() throws Throwable {
2712         mResult =
2713                 RemoteViewsUtil.applyRemoteViews(
2714                         mActivityRule, mContext, mRemoteViews, isProtoTest);
2715     }
2716 
reapplyRemoteViews()2717     private void reapplyRemoteViews() throws Throwable {
2718         RemoteViewsUtil.reapplyRemoteViews(
2719                 mActivityRule, mContext, mRemoteViews, mResult, isProtoTest);
2720     }
2721 
parcelAndUnparcel(RemoteViews views)2722     private static RemoteViews parcelAndUnparcel(RemoteViews views) {
2723         return parcelAndUnparcel(views, /* flags= */ 0);
2724     }
2725 
2726     /**
2727      * Returns the result of writing {@code views} to a {@link Parcel} and then creating a new
2728      * {@link RemoteViews} from the parcel.
2729      */
parcelAndUnparcel(RemoteViews views, int flags)2730     private static RemoteViews parcelAndUnparcel(RemoteViews views, int flags) {
2731         return parcelAndRun(views, flags, RemoteViews.CREATOR::createFromParcel);
2732     }
2733 
2734     /** Returns the data size from writing {@code parcelable} to a {@link Parcel}. */
getParcelledSize(Parcelable parcelable)2735     private static int getParcelledSize(Parcelable parcelable) {
2736         return parcelAndRun(parcelable, /* flags= */ 0, Parcel::dataSize);
2737     }
2738 
parcelAndRun( Parcelable parcelable, int flags, Function<Parcel, T> function)2739     private static <T> T parcelAndRun(
2740             Parcelable parcelable,
2741             int flags,
2742             Function<Parcel, T> function) {
2743         Parcel parcel = Parcel.obtain();
2744         parcelable.writeToParcel(parcel, flags);
2745         parcel.setDataPosition(0);
2746         try {
2747             return function.apply(parcel);
2748         } finally {
2749             parcel.recycle();
2750         }
2751     }
2752 
assertLessThan(int expected, int actual)2753     private static void assertLessThan(int expected, int actual) {
2754         if (actual >= expected) {
2755             Assert.fail("Expected to be less than " + expected + ", but was " + actual);
2756         }
2757     }
2758 }
2759