Adrian Roos | 7da889d | 2016-03-16 18:38:58 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 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 | |
Kirill Grouchnikov | 8376f9e | 2016-07-08 11:27:03 -0400 | [diff] [blame] | 17 | package android.widget; |
Adrian Roos | 7da889d | 2016-03-16 18:38:58 -0700 | [diff] [blame] | 18 | |
Sunny Goyal | 43c9704 | 2018-08-23 15:21:26 -0700 | [diff] [blame^] | 19 | import static org.junit.Assert.assertArrayEquals; |
Sunny Goyal | 56333a8 | 2017-08-29 13:46:29 -0700 | [diff] [blame] | 20 | import static org.junit.Assert.assertEquals; |
| 21 | import static org.junit.Assert.assertSame; |
| 22 | import static org.junit.Assert.assertTrue; |
| 23 | |
Sunny Goyal | 43c9704 | 2018-08-23 15:21:26 -0700 | [diff] [blame^] | 24 | import android.app.ActivityOptions; |
Sunny Goyal | 5b7689f | 2017-09-21 11:08:34 -0700 | [diff] [blame] | 25 | import android.app.PendingIntent; |
Sunny Goyal | 43c9704 | 2018-08-23 15:21:26 -0700 | [diff] [blame^] | 26 | import android.appwidget.AppWidgetHostView; |
Adrian Roos | 7da889d | 2016-03-16 18:38:58 -0700 | [diff] [blame] | 27 | import android.content.Context; |
Sunny Goyal | 5b7689f | 2017-09-21 11:08:34 -0700 | [diff] [blame] | 28 | import android.content.Intent; |
Adrian Roos | 7da889d | 2016-03-16 18:38:58 -0700 | [diff] [blame] | 29 | import android.graphics.Bitmap; |
| 30 | import android.graphics.drawable.BitmapDrawable; |
| 31 | import android.graphics.drawable.Drawable; |
Sunny Goyal | 7b0e2c7 | 2016-11-03 14:48:05 -0700 | [diff] [blame] | 32 | import android.os.AsyncTask; |
Adrian Roos | fb92184 | 2017-10-26 14:49:56 +0200 | [diff] [blame] | 33 | import android.os.Binder; |
Sunny Goyal | 5d8bcdf | 2016-10-27 16:11:29 -0700 | [diff] [blame] | 34 | import android.os.Parcel; |
Adrian Roos | 7da889d | 2016-03-16 18:38:58 -0700 | [diff] [blame] | 35 | import android.support.test.InstrumentationRegistry; |
| 36 | import android.support.test.filters.SmallTest; |
| 37 | import android.support.test.runner.AndroidJUnit4; |
Kirill Grouchnikov | 8376f9e | 2016-07-08 11:27:03 -0400 | [diff] [blame] | 38 | import android.view.View; |
Sunny Goyal | 7b0e2c7 | 2016-11-03 14:48:05 -0700 | [diff] [blame] | 39 | import android.view.ViewGroup; |
Adrian Roos | 7da889d | 2016-03-16 18:38:58 -0700 | [diff] [blame] | 40 | |
| 41 | import com.android.frameworks.coretests.R; |
| 42 | |
Kirill Grouchnikov | 8376f9e | 2016-07-08 11:27:03 -0400 | [diff] [blame] | 43 | import org.junit.Before; |
| 44 | import org.junit.Rule; |
| 45 | import org.junit.Test; |
| 46 | import org.junit.rules.ExpectedException; |
| 47 | import org.junit.runner.RunWith; |
| 48 | |
Sunny Goyal | 7b0e2c7 | 2016-11-03 14:48:05 -0700 | [diff] [blame] | 49 | import java.util.ArrayList; |
| 50 | import java.util.Arrays; |
| 51 | import java.util.concurrent.CountDownLatch; |
| 52 | |
Adrian Roos | 7da889d | 2016-03-16 18:38:58 -0700 | [diff] [blame] | 53 | /** |
| 54 | * Tests for RemoteViews. |
| 55 | */ |
| 56 | @RunWith(AndroidJUnit4.class) |
| 57 | @SmallTest |
| 58 | public class RemoteViewsTest { |
| 59 | |
Sunny Goyal | 5d8bcdf | 2016-10-27 16:11:29 -0700 | [diff] [blame] | 60 | // This can point to any other package which exists on the device. |
| 61 | private static final String OTHER_PACKAGE = "com.android.systemui"; |
| 62 | |
Adrian Roos | 7da889d | 2016-03-16 18:38:58 -0700 | [diff] [blame] | 63 | @Rule |
| 64 | public final ExpectedException exception = ExpectedException.none(); |
| 65 | |
| 66 | private Context mContext; |
| 67 | private String mPackage; |
| 68 | private LinearLayout mContainer; |
| 69 | |
| 70 | @Before |
| 71 | public void setup() { |
| 72 | mContext = InstrumentationRegistry.getContext(); |
Adrian Roos | 4d10db7 | 2016-05-23 14:32:39 -0700 | [diff] [blame] | 73 | mPackage = mContext.getPackageName(); |
Adrian Roos | 7da889d | 2016-03-16 18:38:58 -0700 | [diff] [blame] | 74 | mContainer = new LinearLayout(mContext); |
| 75 | } |
| 76 | |
| 77 | @Test |
| 78 | public void clone_doesNotCopyBitmap() { |
| 79 | RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test); |
| 80 | Bitmap bitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888); |
| 81 | |
| 82 | original.setImageViewBitmap(R.id.image, bitmap); |
| 83 | RemoteViews clone = original.clone(); |
| 84 | View inflated = clone.apply(mContext, mContainer); |
| 85 | |
| 86 | Drawable drawable = ((ImageView) inflated.findViewById(R.id.image)).getDrawable(); |
| 87 | assertSame(bitmap, ((BitmapDrawable)drawable).getBitmap()); |
| 88 | } |
| 89 | |
| 90 | @Test |
| 91 | public void clone_originalCanStillBeApplied() { |
| 92 | RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test); |
| 93 | |
| 94 | RemoteViews clone = original.clone(); |
| 95 | |
| 96 | clone.apply(mContext, mContainer); |
| 97 | } |
| 98 | |
| 99 | @Test |
| 100 | public void clone_clones() { |
| 101 | RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test); |
| 102 | |
| 103 | RemoteViews clone = original.clone(); |
| 104 | original.setTextViewText(R.id.text, "test"); |
| 105 | View inflated = clone.apply(mContext, mContainer); |
| 106 | |
| 107 | TextView textView = (TextView) inflated.findViewById(R.id.text); |
| 108 | assertEquals("", textView.getText()); |
| 109 | } |
| 110 | |
| 111 | @Test |
| 112 | public void clone_child_fails() { |
| 113 | RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test); |
| 114 | RemoteViews child = new RemoteViews(mPackage, R.layout.remote_views_test); |
| 115 | |
| 116 | original.addView(R.id.layout, child); |
| 117 | |
| 118 | exception.expect(IllegalStateException.class); |
| 119 | RemoteViews clone = child.clone(); |
| 120 | } |
| 121 | |
| 122 | @Test |
| 123 | public void clone_repeatedly() { |
| 124 | RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test); |
| 125 | |
| 126 | original.clone(); |
| 127 | original.clone(); |
| 128 | |
| 129 | original.apply(mContext, mContainer); |
| 130 | } |
| 131 | |
| 132 | @Test |
| 133 | public void clone_chained() { |
| 134 | RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test); |
| 135 | |
| 136 | RemoteViews clone = original.clone().clone(); |
| 137 | |
| 138 | clone.apply(mContext, mContainer); |
| 139 | } |
| 140 | |
Sunny Goyal | 5d8bcdf | 2016-10-27 16:11:29 -0700 | [diff] [blame] | 141 | @Test |
| 142 | public void parcelSize_nestedViews() { |
| 143 | RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test); |
| 144 | // We don't care about the actual layout id. |
| 145 | RemoteViews child = new RemoteViews(mPackage, 33); |
| 146 | int expectedSize = getParcelSize(original) + getParcelSize(child); |
| 147 | original.addView(R.id.layout, child); |
| 148 | |
| 149 | // The application info will get written only once. |
| 150 | assertTrue(getParcelSize(original) < expectedSize); |
| 151 | assertEquals(getParcelSize(original), getParcelSize(original.clone())); |
| 152 | |
| 153 | original = new RemoteViews(mPackage, R.layout.remote_views_test); |
| 154 | child = new RemoteViews(OTHER_PACKAGE, 33); |
| 155 | expectedSize = getParcelSize(original) + getParcelSize(child); |
| 156 | original.addView(R.id.layout, child); |
| 157 | |
| 158 | // Both the views will get written completely along with an additional view operation |
| 159 | assertTrue(getParcelSize(original) > expectedSize); |
| 160 | assertEquals(getParcelSize(original), getParcelSize(original.clone())); |
| 161 | } |
| 162 | |
| 163 | @Test |
| 164 | public void parcelSize_differentOrientation() { |
| 165 | RemoteViews landscape = new RemoteViews(mPackage, R.layout.remote_views_test); |
| 166 | RemoteViews portrait = new RemoteViews(mPackage, 33); |
| 167 | |
| 168 | // The application info will get written only once. |
| 169 | RemoteViews views = new RemoteViews(landscape, portrait); |
| 170 | assertTrue(getParcelSize(views) < (getParcelSize(landscape) + getParcelSize(portrait))); |
| 171 | assertEquals(getParcelSize(views), getParcelSize(views.clone())); |
| 172 | } |
| 173 | |
| 174 | private int getParcelSize(RemoteViews view) { |
| 175 | Parcel parcel = Parcel.obtain(); |
| 176 | view.writeToParcel(parcel, 0); |
| 177 | int size = parcel.dataSize(); |
| 178 | parcel.recycle(); |
| 179 | return size; |
| 180 | } |
Sunny Goyal | 7b0e2c7 | 2016-11-03 14:48:05 -0700 | [diff] [blame] | 181 | |
| 182 | @Test |
| 183 | public void asyncApply_fail() throws Exception { |
| 184 | RemoteViews views = new RemoteViews(mPackage, R.layout.remote_view_test_bad_1); |
| 185 | ViewAppliedListener listener = new ViewAppliedListener(); |
| 186 | views.applyAsync(mContext, mContainer, AsyncTask.THREAD_POOL_EXECUTOR, listener); |
| 187 | |
Sunny Goyal | 692f8c9 | 2016-11-11 09:19:14 -0800 | [diff] [blame] | 188 | exception.expect(Exception.class); |
| 189 | listener.waitAndGetView(); |
Sunny Goyal | 7b0e2c7 | 2016-11-03 14:48:05 -0700 | [diff] [blame] | 190 | } |
| 191 | |
| 192 | @Test |
| 193 | public void asyncApply() throws Exception { |
| 194 | RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test); |
| 195 | views.setTextViewText(R.id.text, "Dummy"); |
| 196 | |
| 197 | View syncView = views.apply(mContext, mContainer); |
| 198 | |
| 199 | ViewAppliedListener listener = new ViewAppliedListener(); |
| 200 | views.applyAsync(mContext, mContainer, AsyncTask.THREAD_POOL_EXECUTOR, listener); |
| 201 | View asyncView = listener.waitAndGetView(); |
| 202 | |
| 203 | verifyViewTree(syncView, asyncView, "Dummy"); |
| 204 | } |
| 205 | |
| 206 | @Test |
| 207 | public void asyncApply_viewStub() throws Exception { |
| 208 | RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_viewstub); |
| 209 | views.setInt(R.id.viewStub, "setLayoutResource", R.layout.remote_views_text); |
| 210 | // This will cause the view to be inflated |
| 211 | views.setViewVisibility(R.id.viewStub, View.INVISIBLE); |
| 212 | views.setTextViewText(R.id.stub_inflated, "Dummy"); |
| 213 | |
| 214 | View syncView = views.apply(mContext, mContainer); |
| 215 | |
| 216 | ViewAppliedListener listener = new ViewAppliedListener(); |
| 217 | views.applyAsync(mContext, mContainer, AsyncTask.THREAD_POOL_EXECUTOR, listener); |
| 218 | View asyncView = listener.waitAndGetView(); |
| 219 | |
| 220 | verifyViewTree(syncView, asyncView, "Dummy"); |
| 221 | } |
| 222 | |
| 223 | @Test |
| 224 | public void asyncApply_nestedViews() throws Exception { |
| 225 | RemoteViews views = new RemoteViews(mPackage, R.layout.remote_view_host); |
| 226 | views.removeAllViews(R.id.container); |
| 227 | views.addView(R.id.container, createViewChained(1, "row1-c1", "row1-c2", "row1-c3")); |
| 228 | views.addView(R.id.container, createViewChained(5, "row2-c1", "row2-c2")); |
| 229 | views.addView(R.id.container, createViewChained(2, "row3-c1", "row3-c2")); |
| 230 | |
| 231 | View syncView = views.apply(mContext, mContainer); |
| 232 | |
| 233 | ViewAppliedListener listener = new ViewAppliedListener(); |
| 234 | views.applyAsync(mContext, mContainer, AsyncTask.THREAD_POOL_EXECUTOR, listener); |
| 235 | View asyncView = listener.waitAndGetView(); |
| 236 | |
| 237 | verifyViewTree(syncView, asyncView, |
| 238 | "row1-c1", "row1-c2", "row1-c3", "row2-c1", "row2-c2", "row3-c1", "row3-c2"); |
| 239 | } |
| 240 | |
| 241 | @Test |
| 242 | public void asyncApply_viewstub_nestedViews() throws Exception { |
| 243 | RemoteViews viewstub = new RemoteViews(mPackage, R.layout.remote_views_viewstub); |
| 244 | viewstub.setInt(R.id.viewStub, "setLayoutResource", R.layout.remote_view_host); |
| 245 | // This will cause the view to be inflated |
| 246 | viewstub.setViewVisibility(R.id.viewStub, View.INVISIBLE); |
| 247 | viewstub.addView(R.id.stub_inflated, createViewChained(1, "row1-c1", "row1-c2", "row1-c3")); |
| 248 | |
| 249 | RemoteViews views = new RemoteViews(mPackage, R.layout.remote_view_host); |
| 250 | views.removeAllViews(R.id.container); |
| 251 | views.addView(R.id.container, viewstub); |
| 252 | views.addView(R.id.container, createViewChained(5, "row2-c1", "row2-c2")); |
| 253 | |
| 254 | View syncView = views.apply(mContext, mContainer); |
| 255 | |
| 256 | ViewAppliedListener listener = new ViewAppliedListener(); |
| 257 | views.applyAsync(mContext, mContainer, AsyncTask.THREAD_POOL_EXECUTOR, listener); |
| 258 | View asyncView = listener.waitAndGetView(); |
| 259 | |
| 260 | verifyViewTree(syncView, asyncView, "row1-c1", "row1-c2", "row1-c3", "row2-c1", "row2-c2"); |
| 261 | } |
| 262 | |
| 263 | private RemoteViews createViewChained(int depth, String... texts) { |
| 264 | RemoteViews result = new RemoteViews(mPackage, R.layout.remote_view_host); |
| 265 | |
| 266 | // Create depth |
| 267 | RemoteViews parent = result; |
| 268 | while(depth > 0) { |
| 269 | depth--; |
| 270 | RemoteViews child = new RemoteViews(mPackage, R.layout.remote_view_host); |
| 271 | parent.addView(R.id.container, child); |
| 272 | parent = child; |
| 273 | } |
| 274 | |
| 275 | // Add texts |
| 276 | for (String text : texts) { |
| 277 | RemoteViews child = new RemoteViews(mPackage, R.layout.remote_views_text); |
| 278 | child.setTextViewText(R.id.text, text); |
| 279 | parent.addView(R.id.container, child); |
| 280 | } |
| 281 | return result; |
| 282 | } |
| 283 | |
| 284 | private void verifyViewTree(View v1, View v2, String... texts) { |
| 285 | ArrayList<String> expectedTexts = new ArrayList<>(Arrays.asList(texts)); |
| 286 | verifyViewTreeRecur(v1, v2, expectedTexts); |
| 287 | // Verify that all expected texts were found |
| 288 | assertEquals(0, expectedTexts.size()); |
| 289 | } |
| 290 | |
| 291 | private void verifyViewTreeRecur(View v1, View v2, ArrayList<String> expectedTexts) { |
| 292 | assertEquals(v1.getClass(), v2.getClass()); |
| 293 | |
| 294 | if (v1 instanceof TextView) { |
| 295 | String text = ((TextView) v1).getText().toString(); |
| 296 | assertEquals(text, ((TextView) v2).getText().toString()); |
| 297 | // Verify that the text was one of the expected texts and remove it from the list |
| 298 | assertTrue(expectedTexts.remove(text)); |
| 299 | } else if (v1 instanceof ViewGroup) { |
| 300 | ViewGroup vg1 = (ViewGroup) v1; |
| 301 | ViewGroup vg2 = (ViewGroup) v2; |
| 302 | assertEquals(vg1.getChildCount(), vg2.getChildCount()); |
| 303 | for (int i = vg1.getChildCount() - 1; i >= 0; i--) { |
| 304 | verifyViewTreeRecur(vg1.getChildAt(i), vg2.getChildAt(i), expectedTexts); |
| 305 | } |
| 306 | } |
| 307 | } |
| 308 | |
| 309 | private class ViewAppliedListener implements RemoteViews.OnViewAppliedListener { |
| 310 | |
| 311 | private final CountDownLatch mLatch = new CountDownLatch(1); |
| 312 | private View mView; |
| 313 | private Exception mError; |
| 314 | |
| 315 | @Override |
| 316 | public void onViewApplied(View v) { |
| 317 | mView = v; |
| 318 | mLatch.countDown(); |
| 319 | } |
| 320 | |
| 321 | @Override |
| 322 | public void onError(Exception e) { |
| 323 | mError = e; |
| 324 | mLatch.countDown(); |
| 325 | } |
| 326 | |
| 327 | public View waitAndGetView() throws Exception { |
| 328 | mLatch.await(); |
| 329 | |
| 330 | if (mError != null) { |
| 331 | throw new Exception(mError); |
| 332 | } |
| 333 | return mView; |
| 334 | } |
| 335 | } |
Sunny Goyal | 692f8c9 | 2016-11-11 09:19:14 -0800 | [diff] [blame] | 336 | |
| 337 | @Test |
| 338 | public void nestedAddViews() { |
| 339 | RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test); |
| 340 | for (int i = 0; i < 10; i++) { |
| 341 | RemoteViews parent = new RemoteViews(mPackage, R.layout.remote_views_test); |
| 342 | parent.addView(R.id.layout, views); |
| 343 | views = parent; |
| 344 | } |
Sunny Goyal | 56333a8 | 2017-08-29 13:46:29 -0700 | [diff] [blame] | 345 | // Both clone and parcel/unparcel work, |
Sunny Goyal | 692f8c9 | 2016-11-11 09:19:14 -0800 | [diff] [blame] | 346 | views.clone(); |
Sunny Goyal | 56333a8 | 2017-08-29 13:46:29 -0700 | [diff] [blame] | 347 | parcelAndRecreate(views); |
Sunny Goyal | 692f8c9 | 2016-11-11 09:19:14 -0800 | [diff] [blame] | 348 | |
| 349 | views = new RemoteViews(mPackage, R.layout.remote_views_test); |
| 350 | for (int i = 0; i < 11; i++) { |
| 351 | RemoteViews parent = new RemoteViews(mPackage, R.layout.remote_views_test); |
| 352 | parent.addView(R.id.layout, views); |
| 353 | views = parent; |
| 354 | } |
Sunny Goyal | 56333a8 | 2017-08-29 13:46:29 -0700 | [diff] [blame] | 355 | // Clone works but parcel/unparcel fails |
Sunny Goyal | 692f8c9 | 2016-11-11 09:19:14 -0800 | [diff] [blame] | 356 | views.clone(); |
Sunny Goyal | 56333a8 | 2017-08-29 13:46:29 -0700 | [diff] [blame] | 357 | exception.expect(IllegalArgumentException.class); |
| 358 | parcelAndRecreate(views); |
Sunny Goyal | 692f8c9 | 2016-11-11 09:19:14 -0800 | [diff] [blame] | 359 | } |
| 360 | |
| 361 | @Test |
| 362 | public void nestedLandscapeViews() { |
| 363 | RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test); |
| 364 | for (int i = 0; i < 10; i++) { |
| 365 | views = new RemoteViews(views, |
| 366 | new RemoteViews(mPackage, R.layout.remote_views_test)); |
| 367 | } |
Sunny Goyal | 56333a8 | 2017-08-29 13:46:29 -0700 | [diff] [blame] | 368 | // Both clone and parcel/unparcel work, |
Sunny Goyal | 692f8c9 | 2016-11-11 09:19:14 -0800 | [diff] [blame] | 369 | views.clone(); |
Sunny Goyal | 56333a8 | 2017-08-29 13:46:29 -0700 | [diff] [blame] | 370 | parcelAndRecreate(views); |
Sunny Goyal | 692f8c9 | 2016-11-11 09:19:14 -0800 | [diff] [blame] | 371 | |
| 372 | views = new RemoteViews(mPackage, R.layout.remote_views_test); |
| 373 | for (int i = 0; i < 11; i++) { |
Sunny Goyal | 56333a8 | 2017-08-29 13:46:29 -0700 | [diff] [blame] | 374 | views = new RemoteViews(views, |
| 375 | new RemoteViews(mPackage, R.layout.remote_views_test)); |
Sunny Goyal | 692f8c9 | 2016-11-11 09:19:14 -0800 | [diff] [blame] | 376 | } |
Sunny Goyal | 56333a8 | 2017-08-29 13:46:29 -0700 | [diff] [blame] | 377 | // Clone works but parcel/unparcel fails |
Sunny Goyal | 692f8c9 | 2016-11-11 09:19:14 -0800 | [diff] [blame] | 378 | views.clone(); |
Sunny Goyal | 56333a8 | 2017-08-29 13:46:29 -0700 | [diff] [blame] | 379 | exception.expect(IllegalArgumentException.class); |
| 380 | parcelAndRecreate(views); |
| 381 | } |
| 382 | |
Adrian Roos | fb92184 | 2017-10-26 14:49:56 +0200 | [diff] [blame] | 383 | private RemoteViews parcelAndRecreate(RemoteViews views) { |
| 384 | return parcelAndRecreateWithPendingIntentCookie(views, null); |
| 385 | } |
Sunny Goyal | 56333a8 | 2017-08-29 13:46:29 -0700 | [diff] [blame] | 386 | |
Adrian Roos | fb92184 | 2017-10-26 14:49:56 +0200 | [diff] [blame] | 387 | private RemoteViews parcelAndRecreateWithPendingIntentCookie(RemoteViews views, Object cookie) { |
| 388 | Parcel p = Parcel.obtain(); |
| 389 | try { |
| 390 | views.writeToParcel(p, 0); |
| 391 | p.setDataPosition(0); |
| 392 | |
| 393 | if (cookie != null) { |
| 394 | p.setClassCookie(PendingIntent.class, cookie); |
| 395 | } |
| 396 | |
| 397 | return RemoteViews.CREATOR.createFromParcel(p); |
| 398 | } finally { |
| 399 | p.recycle(); |
| 400 | } |
Sunny Goyal | 692f8c9 | 2016-11-11 09:19:14 -0800 | [diff] [blame] | 401 | } |
Sunny Goyal | 5b7689f | 2017-09-21 11:08:34 -0700 | [diff] [blame] | 402 | |
| 403 | @Test |
| 404 | public void copyWithBinders() throws Exception { |
| 405 | RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test); |
| 406 | for (int i = 1; i < 10; i++) { |
| 407 | PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, |
| 408 | new Intent("android.widget.RemoteViewsTest_" + i), PendingIntent.FLAG_ONE_SHOT); |
| 409 | views.setOnClickPendingIntent(i, pi); |
| 410 | } |
| 411 | try { |
| 412 | new RemoteViews(views); |
| 413 | } catch (Throwable t) { |
| 414 | throw new Exception(t); |
| 415 | } |
| 416 | } |
Adrian Roos | fb92184 | 2017-10-26 14:49:56 +0200 | [diff] [blame] | 417 | |
| 418 | @Test |
| 419 | public void copy_keepsPendingIntentWhitelistToken() throws Exception { |
| 420 | Binder whitelistToken = new Binder(); |
| 421 | |
| 422 | RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test); |
| 423 | PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, |
| 424 | new Intent("test"), PendingIntent.FLAG_ONE_SHOT); |
| 425 | views.setOnClickPendingIntent(1, pi); |
| 426 | RemoteViews withCookie = parcelAndRecreateWithPendingIntentCookie(views, whitelistToken); |
| 427 | |
| 428 | RemoteViews cloned = new RemoteViews(withCookie); |
| 429 | |
| 430 | PendingIntent found = extractAnyPendingIntent(cloned); |
| 431 | assertEquals(whitelistToken, found.getWhitelistToken()); |
| 432 | } |
| 433 | |
| 434 | private PendingIntent extractAnyPendingIntent(RemoteViews cloned) { |
| 435 | PendingIntent[] found = new PendingIntent[1]; |
| 436 | Parcel p = Parcel.obtain(); |
| 437 | try { |
| 438 | PendingIntent.setOnMarshaledListener((intent, parcel, flags) -> { |
| 439 | if (parcel == p) { |
| 440 | found[0] = intent; |
| 441 | } |
| 442 | }); |
| 443 | cloned.writeToParcel(p, 0); |
| 444 | } finally { |
| 445 | p.recycle(); |
| 446 | PendingIntent.setOnMarshaledListener(null); |
| 447 | } |
| 448 | return found[0]; |
| 449 | } |
Sunny Goyal | 43c9704 | 2018-08-23 15:21:26 -0700 | [diff] [blame^] | 450 | |
| 451 | @Test |
| 452 | public void sharedElement_pendingIntent_notifyParent() throws Exception { |
| 453 | RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test); |
| 454 | PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, |
| 455 | new Intent("android.widget.RemoteViewsTest_shared_element"), |
| 456 | PendingIntent.FLAG_ONE_SHOT); |
| 457 | views.setOnClickResponse(R.id.image, RemoteViews.RemoteResponse.fromPendingIntent(pi) |
| 458 | .addSharedElement(0, "e0") |
| 459 | .addSharedElement(1, "e1") |
| 460 | .addSharedElement(2, "e2")); |
| 461 | |
| 462 | WidgetContainer container = new WidgetContainer(mContext); |
| 463 | container.addView(new RemoteViews(views).apply(mContext, container)); |
| 464 | container.findViewById(R.id.image).performClick(); |
| 465 | |
| 466 | assertArrayEquals(container.mSharedViewIds, new int[] {0, 1, 2}); |
| 467 | assertArrayEquals(container.mSharedViewNames, new String[] {"e0", "e1", "e2"}); |
| 468 | } |
| 469 | |
| 470 | private class WidgetContainer extends AppWidgetHostView { |
| 471 | int[] mSharedViewIds; |
| 472 | String[] mSharedViewNames; |
| 473 | |
| 474 | WidgetContainer(Context context) { |
| 475 | super(context); |
| 476 | } |
| 477 | |
| 478 | @Override |
| 479 | public ActivityOptions createSharedElementActivityOptions( |
| 480 | int[] sharedViewIds, String[] sharedViewNames, Intent fillInIntent) { |
| 481 | mSharedViewIds = sharedViewIds; |
| 482 | mSharedViewNames = sharedViewNames; |
| 483 | return null; |
| 484 | } |
| 485 | } |
Adrian Roos | 7da889d | 2016-03-16 18:38:58 -0700 | [diff] [blame] | 486 | } |