1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.appwidget.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertSame; 24 import static org.junit.Assert.assertTrue; 25 import static org.junit.Assert.fail; 26 import static org.mockito.Matchers.any; 27 import static org.mockito.Matchers.eq; 28 import static org.mockito.Matchers.same; 29 import static org.mockito.Mockito.atLeastOnce; 30 import static org.mockito.Mockito.doAnswer; 31 import static org.mockito.Mockito.inOrder; 32 import static org.mockito.Mockito.mock; 33 import static org.mockito.Mockito.times; 34 import static org.mockito.Mockito.verify; 35 import static org.mockito.hamcrest.MockitoHamcrest.argThat; 36 37 import android.appwidget.AppWidgetHost; 38 import android.appwidget.AppWidgetHostView; 39 import android.appwidget.AppWidgetManager; 40 import android.appwidget.AppWidgetProviderInfo; 41 import android.appwidget.cts.provider.AppWidgetProviderCallbacks; 42 import android.appwidget.cts.provider.AppWidgetProviderWithFeatures; 43 import android.appwidget.cts.provider.FirstAppWidgetProvider; 44 import android.appwidget.cts.provider.SecondAppWidgetProvider; 45 import android.appwidget.cts.service.MyAppWidgetService; 46 import android.content.ComponentName; 47 import android.content.Context; 48 import android.content.Intent; 49 import android.content.pm.PackageManager; 50 import android.graphics.drawable.Drawable; 51 import android.net.Uri; 52 import android.os.Bundle; 53 import android.os.Process; 54 import android.os.SystemClock; 55 import android.os.UserHandle; 56 import android.os.UserManager; 57 import android.platform.test.annotations.AppModeFull; 58 import android.platform.test.annotations.AppModeInstant; 59 import android.text.TextUtils; 60 import android.util.DisplayMetrics; 61 import android.widget.RemoteViews; 62 import android.widget.RemoteViewsService.RemoteViewsFactory; 63 64 import org.hamcrest.BaseMatcher; 65 import org.hamcrest.Description; 66 import org.junit.Assume; 67 import org.junit.Before; 68 import org.junit.Test; 69 import org.mockito.InOrder; 70 import org.mockito.invocation.InvocationOnMock; 71 import org.mockito.stubbing.Answer; 72 73 import java.util.ArrayList; 74 import java.util.Arrays; 75 import java.util.List; 76 import java.util.concurrent.atomic.AtomicInteger; 77 78 public class AppWidgetTest extends AppWidgetTestCase { 79 80 private static final long OPERATION_TIMEOUT = 20 * 1000; // 20 sec 81 82 private final Object mLock = new Object(); 83 84 @Before 85 public void setUpDexmaker() throws Exception { 86 // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2 87 // Dexmaker is used by mockito. 88 System.setProperty("dexmaker.dexcache", getInstrumentation() 89 .getTargetContext().getCacheDir().getPath()); 90 } 91 92 private static final String GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND = 93 "appwidget grantbind --package android.appwidget.cts --user 0"; 94 95 private static final String REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND = 96 "appwidget revokebind --package android.appwidget.cts --user 0"; 97 98 @AppModeInstant(reason = "Instant apps cannot provide or host app widgets") 99 @Test 100 public void testInstantAppsCannotProvideAppWidgets() { 101 Assume.assumeTrue(getInstrumentation().getTargetContext() 102 .getPackageManager().isInstantApp()); 103 assertNull(getFirstAppWidgetProviderInfo()); 104 } 105 106 @AppModeInstant(reason = "Instant apps cannot provide or host app widgets") 107 @Test 108 public void testInstantAppsCannotHostAppWidgets() { 109 Assume.assumeTrue(getInstrumentation().getTargetContext() 110 .getPackageManager().isInstantApp()); 111 // Create a host and start listening. 112 AppWidgetHost host = new AppWidgetHost(getInstrumentation().getTargetContext(), 0); 113 // Allocate an app widget id to bind. 114 assertSame(AppWidgetManager.INVALID_APPWIDGET_ID, host.allocateAppWidgetId()); 115 } 116 117 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 118 @Test 119 public void testGetAppInstalledProvidersForCurrentUserLegacy() throws Exception { 120 // By default we should get only providers for the current user. 121 List<AppWidgetProviderInfo> providers = getAppWidgetManager().getInstalledProviders(); 122 123 // Make sure we have our two providers in the list. 124 assertExpectedInstalledProviders(providers); 125 } 126 127 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 128 @Test 129 public void testGetAppInstalledProvidersForCurrentUserNewCurrentProfile() throws Exception { 130 // We ask only for providers for the current user. 131 List<AppWidgetProviderInfo> providers = getAppWidgetManager() 132 .getInstalledProvidersForProfile(Process.myUserHandle()); 133 134 // Make sure we have our two providers in the list. 135 assertExpectedInstalledProviders(providers); 136 } 137 138 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 139 @Test 140 public void testGetAppInstalledProvidersForCurrentUserNewAllProfiles() throws Exception { 141 // We ask only for providers for all current user's profiles 142 UserManager userManager = (UserManager) getInstrumentation() 143 .getTargetContext().getSystemService(Context.USER_SERVICE); 144 145 List<AppWidgetProviderInfo> allProviders = new ArrayList<>(); 146 147 List<UserHandle> profiles = userManager.getUserProfiles(); 148 final int profileCount = profiles.size(); 149 for (int i = 0; i < profileCount; i++) { 150 UserHandle profile = profiles.get(i); 151 List<AppWidgetProviderInfo> providers = getAppWidgetManager() 152 .getInstalledProvidersForProfile(profile); 153 allProviders.addAll(providers); 154 } 155 156 // Make sure we have our two providers in the list. 157 assertExpectedInstalledProviders(allProviders); 158 } 159 160 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 161 @Test 162 public void testBindAppWidget() throws Exception { 163 // Create a host and start listening. 164 AppWidgetHost host = new AppWidgetHost(getInstrumentation().getTargetContext(), 0); 165 host.deleteHost(); 166 host.startListening(); 167 168 // Allocate an app widget id to bind. 169 final int appWidgetId = host.allocateAppWidgetId(); 170 171 // Grab a provider we defined to be bound. 172 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 173 174 // Bind the widget. 175 boolean widgetBound = getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId, 176 provider.getProfile(), provider.provider, null); 177 assertFalse(widgetBound); 178 179 // Well, app do not have this permission unless explicitly granted 180 // by the user. Now we will pretend for the user and grant it. 181 grantBindAppWidgetPermission(); 182 183 try { 184 // Bind the widget as we have a permission for that. 185 widgetBound = getAppWidgetManager().bindAppWidgetIdIfAllowed( 186 appWidgetId, provider.getProfile(), provider.provider, null); 187 assertTrue(widgetBound); 188 189 // Deallocate the app widget id. 190 host.deleteAppWidgetId(appWidgetId); 191 } finally { 192 // Clean up. 193 host.deleteAppWidgetId(appWidgetId); 194 host.deleteHost(); 195 revokeBindAppWidgetPermission(); 196 } 197 } 198 199 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 200 @Test 201 public void testGetAppWidgetIdsForHost() throws Exception { 202 AppWidgetHost host1 = new AppWidgetHost(getInstrumentation().getTargetContext(), 1); 203 AppWidgetHost host2 = new AppWidgetHost(getInstrumentation().getTargetContext(), 2); 204 205 host1.deleteHost(); 206 host2.deleteHost(); 207 208 assertTrue(Arrays.equals(host1.getAppWidgetIds(), new int[]{})); 209 assertTrue(Arrays.equals(host2.getAppWidgetIds(), new int[]{})); 210 211 int id1 = host1.allocateAppWidgetId(); 212 assertTrue(Arrays.equals(host1.getAppWidgetIds(), new int[]{id1})); 213 assertTrue(Arrays.equals(host2.getAppWidgetIds(), new int[]{})); 214 215 int id2 = host1.allocateAppWidgetId(); 216 assertTrue(Arrays.equals(host1.getAppWidgetIds(), new int[]{id1, id2})); 217 assertTrue(Arrays.equals(host2.getAppWidgetIds(), new int[]{})); 218 219 int id3 = host2.allocateAppWidgetId(); 220 assertTrue(Arrays.equals(host1.getAppWidgetIds(), new int[]{id1, id2})); 221 assertTrue(Arrays.equals(host2.getAppWidgetIds(), new int[]{id3})); 222 223 host1.deleteHost(); 224 assertTrue(Arrays.equals(host1.getAppWidgetIds(), new int[]{})); 225 assertTrue(Arrays.equals(host2.getAppWidgetIds(), new int[]{id3})); 226 227 host2.deleteHost(); 228 } 229 230 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 231 @Test 232 public void testAppWidgetProviderCallbacks() throws Exception { 233 AtomicInteger invocationCounter = new AtomicInteger(); 234 235 // Set a mock to intercept provider callbacks. 236 AppWidgetProviderCallbacks callbacks = createAppWidgetProviderCallbacks(invocationCounter); 237 FirstAppWidgetProvider.setCallbacks(callbacks); 238 239 int firstAppWidgetId = 0; 240 int secondAppWidgetId = 0; 241 242 final Bundle firstOptions; 243 final Bundle secondOptions; 244 245 // Create a host and start listening. 246 AppWidgetHost host = new AppWidgetHost(getInstrumentation().getTargetContext(), 0); 247 host.deleteHost(); 248 host.startListening(); 249 250 // We want to bind a widget. 251 grantBindAppWidgetPermission(); 252 try { 253 // Allocate the first widget id to bind. 254 firstAppWidgetId = host.allocateAppWidgetId(); 255 256 // Grab a provider we defined to be bound. 257 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 258 259 // Bind the first widget. 260 getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId, 261 provider.getProfile(), provider.provider, null); 262 263 // Wait for onEnabled and onUpdate 264 waitForCallCount(invocationCounter, 2); 265 266 // Update the first widget options. 267 firstOptions = new Bundle(); 268 firstOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, 1); 269 firstOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, 2); 270 firstOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, 3); 271 firstOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, 4); 272 getAppWidgetManager().updateAppWidgetOptions(firstAppWidgetId, firstOptions); 273 274 // Wait for onAppWidgetOptionsChanged 275 waitForCallCount(invocationCounter, 3); 276 277 // Allocate the second app widget id to bind. 278 secondAppWidgetId = host.allocateAppWidgetId(); 279 280 // Bind the second widget. 281 getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId, 282 provider.getProfile(), provider.provider, null); 283 284 // Wait for onUpdate 285 waitForCallCount(invocationCounter, 4); 286 287 // Update the second widget options. 288 secondOptions = new Bundle(); 289 secondOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, 5); 290 secondOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, 6); 291 secondOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, 7); 292 secondOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, 8); 293 getAppWidgetManager().updateAppWidgetOptions(secondAppWidgetId, secondOptions); 294 295 // Wait for onAppWidgetOptionsChanged 296 waitForCallCount(invocationCounter, 5); 297 298 // Delete the first widget. 299 host.deleteAppWidgetId(firstAppWidgetId); 300 301 // Wait for onDeleted 302 waitForCallCount(invocationCounter, 6); 303 304 // Delete the second widget. 305 host.deleteAppWidgetId(secondAppWidgetId); 306 307 // Wait for onDeleted and onDisabled 308 waitForCallCount(invocationCounter, 8); 309 310 // Make sure the provider callbacks are correct. 311 InOrder inOrder = inOrder(callbacks); 312 313 inOrder.verify(callbacks).onEnabled(any(Context.class)); 314 inOrder.verify(callbacks).onUpdate(any(Context.class), 315 any(AppWidgetManager.class), eq(new int[] {firstAppWidgetId})); 316 inOrder.verify(callbacks).onAppWidgetOptionsChanged(any(Context.class), 317 any(AppWidgetManager.class), same(firstAppWidgetId), argThat( 318 new OptionsMatcher(firstOptions))); 319 inOrder.verify(callbacks).onUpdate(any(Context.class), 320 any(AppWidgetManager.class), eq(new int[] {secondAppWidgetId})); 321 inOrder.verify(callbacks).onAppWidgetOptionsChanged(any(Context.class), 322 any(AppWidgetManager.class), same(secondAppWidgetId), argThat( 323 new OptionsMatcher(secondOptions))); 324 inOrder.verify(callbacks).onDeleted(any(Context.class), 325 argThat(new WidgetIdsMatcher(new int[]{firstAppWidgetId}))); 326 inOrder.verify(callbacks).onDeleted(any(Context.class), 327 argThat(new WidgetIdsMatcher(new int[]{secondAppWidgetId}))); 328 inOrder.verify(callbacks).onDisabled(any(Context.class)); 329 } finally { 330 // Clean up. 331 host.deleteAppWidgetId(firstAppWidgetId); 332 host.deleteAppWidgetId(secondAppWidgetId); 333 host.deleteHost(); 334 FirstAppWidgetProvider.setCallbacks(null); 335 revokeBindAppWidgetPermission(); 336 } 337 } 338 339 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 340 @Test 341 public void testTwoAppWidgetProviderCallbacks() throws Exception { 342 AtomicInteger invocationCounter = new AtomicInteger(); 343 344 // Set a mock to intercept first provider callbacks. 345 AppWidgetProviderCallbacks firstCallbacks = createAppWidgetProviderCallbacks( 346 invocationCounter); 347 FirstAppWidgetProvider.setCallbacks(firstCallbacks); 348 349 // Set a mock to intercept second provider callbacks. 350 AppWidgetProviderCallbacks secondCallbacks = createAppWidgetProviderCallbacks( 351 invocationCounter); 352 SecondAppWidgetProvider.setCallbacks(secondCallbacks); 353 354 int firstAppWidgetId = 0; 355 int secondAppWidgetId = 0; 356 357 // Create a host and start listening. 358 AppWidgetHost host = new AppWidgetHost(getInstrumentation().getTargetContext(), 0); 359 host.deleteHost(); 360 host.startListening(); 361 362 // We want to bind widgets. 363 grantBindAppWidgetPermission(); 364 try { 365 // Allocate the first widget id to bind. 366 firstAppWidgetId = host.allocateAppWidgetId(); 367 368 // Allocate the second widget id to bind. 369 secondAppWidgetId = host.allocateAppWidgetId(); 370 371 // Grab the first provider we defined to be bound. 372 AppWidgetProviderInfo firstProvider = getFirstAppWidgetProviderInfo(); 373 374 // Bind the first widget. 375 getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId, 376 firstProvider.getProfile(), firstProvider.provider, null); 377 378 // Wait for onEnabled and onUpdate 379 waitForCallCount(invocationCounter, 2); 380 381 // Grab the second provider we defined to be bound. 382 AppWidgetProviderInfo secondProvider = getSecondAppWidgetProviderInfo(); 383 384 // Bind the second widget. 385 getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId, 386 secondProvider.getProfile(), secondProvider.provider, null); 387 388 // Wait for onEnabled and onUpdate 389 waitForCallCount(invocationCounter, 4); 390 391 // Delete the first widget. 392 host.deleteAppWidgetId(firstAppWidgetId); 393 394 // Wait for onDeleted and onDisabled 395 waitForCallCount(invocationCounter, 6); 396 397 // Delete the second widget. 398 host.deleteAppWidgetId(secondAppWidgetId); 399 400 // Wait for onDeleted and onDisabled 401 waitForCallCount(invocationCounter, 8); 402 403 // Make sure the first provider callbacks are correct. 404 InOrder firstInOrder = inOrder(firstCallbacks); 405 firstInOrder.verify(firstCallbacks).onEnabled(any(Context.class)); 406 firstInOrder.verify(firstCallbacks).onUpdate(any(Context.class), 407 any(AppWidgetManager.class), eq(new int[]{firstAppWidgetId})); 408 firstInOrder.verify(firstCallbacks).onDeleted(any(Context.class), 409 argThat(new WidgetIdsMatcher(new int[]{firstAppWidgetId}))); 410 firstInOrder.verify(firstCallbacks).onDisabled(any(Context.class)); 411 412 // Make sure the second provider callbacks are correct. 413 InOrder secondInOrder = inOrder(secondCallbacks); 414 secondInOrder.verify(secondCallbacks).onEnabled(any(Context.class)); 415 secondInOrder.verify(secondCallbacks).onUpdate(any(Context.class), 416 any(AppWidgetManager.class), eq(new int[]{secondAppWidgetId})); 417 secondInOrder.verify(secondCallbacks).onDeleted(any(Context.class), 418 argThat(new WidgetIdsMatcher(new int[] {secondAppWidgetId}))); 419 secondInOrder.verify(secondCallbacks).onDisabled(any(Context.class)); 420 } finally { 421 // Clean up. 422 host.deleteAppWidgetId(firstAppWidgetId); 423 host.deleteAppWidgetId(secondAppWidgetId); 424 host.deleteHost(); 425 FirstAppWidgetProvider.setCallbacks(null); 426 SecondAppWidgetProvider.setCallbacks(null); 427 revokeBindAppWidgetPermission(); 428 } 429 } 430 431 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 432 @Test 433 public void testGetAppWidgetIdsForProvider() throws Exception { 434 // We want to bind widgets. 435 grantBindAppWidgetPermission(); 436 437 // Create a host and start listening. 438 AppWidgetHost host = new AppWidgetHost( 439 getInstrumentation().getTargetContext(), 0); 440 host.deleteHost(); 441 host.startListening(); 442 443 int firstAppWidgetId = 0; 444 int secondAppWidgetId = 0; 445 446 try { 447 // Grab the provider we defined to be bound. 448 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 449 450 // Initially we have no widgets. 451 int[] widgetsIds = getAppWidgetManager().getAppWidgetIds(provider.provider); 452 assertTrue(widgetsIds.length == 0); 453 454 // Allocate the first widget id to bind. 455 firstAppWidgetId = host.allocateAppWidgetId(); 456 457 // Bind the first widget. 458 getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId, 459 provider.getProfile(), provider.provider, null); 460 461 // Allocate the second widget id to bind. 462 secondAppWidgetId = host.allocateAppWidgetId(); 463 464 // Bind the second widget. 465 getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId, 466 provider.getProfile(), provider.provider, null); 467 468 // Now we have two widgets, 469 widgetsIds = getAppWidgetManager().getAppWidgetIds(provider.provider); 470 assertTrue(Arrays.equals(widgetsIds, new int[]{firstAppWidgetId, secondAppWidgetId})); 471 } finally { 472 // Clean up. 473 host.deleteAppWidgetId(firstAppWidgetId); 474 host.deleteAppWidgetId(secondAppWidgetId); 475 host.deleteHost(); 476 revokeBindAppWidgetPermission(); 477 } 478 } 479 480 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 481 @Test 482 public void testGetAppWidgetInfo() throws Exception { 483 // We want to bind widgets. 484 grantBindAppWidgetPermission(); 485 486 // Create a host and start listening. 487 AppWidgetHost host = new AppWidgetHost( 488 getInstrumentation().getTargetContext(), 0); 489 host.deleteHost(); 490 host.startListening(); 491 492 int appWidgetId = 0; 493 494 try { 495 // Allocate an widget id to bind. 496 appWidgetId = host.allocateAppWidgetId(); 497 498 // The widget is not bound, so no info. 499 AppWidgetProviderInfo foundProvider = getAppWidgetManager() 500 .getAppWidgetInfo(appWidgetId); 501 assertNull(foundProvider); 502 503 // Grab the provider we defined to be bound. 504 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 505 506 // Bind the app widget. 507 getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId, 508 provider.getProfile(), provider.provider, null); 509 510 // The widget is bound, so the provider info should be there. 511 foundProvider = getAppWidgetManager().getAppWidgetInfo(appWidgetId); 512 assertEquals(provider.provider, foundProvider.provider); 513 assertEquals(provider.getProfile(), foundProvider.getProfile()); 514 515 Context context = getInstrumentation().getTargetContext(); 516 517 // Let us make sure the provider info is sane. 518 String label = foundProvider.loadLabel(context.getPackageManager()); 519 assertTrue(!TextUtils.isEmpty(label)); 520 521 Drawable icon = foundProvider.loadIcon(context, DisplayMetrics.DENSITY_DEFAULT); 522 assertNotNull(icon); 523 524 Drawable previewImage = foundProvider.loadPreviewImage(context, 0); 525 assertNotNull(previewImage); 526 } finally { 527 // Clean up. 528 host.deleteAppWidgetId(appWidgetId); 529 host.deleteHost(); 530 revokeBindAppWidgetPermission(); 531 } 532 } 533 534 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 535 @Test 536 public void testGetAppWidgetOptions() throws Exception { 537 // We want to bind widgets. 538 grantBindAppWidgetPermission(); 539 540 // Create a host and start listening. 541 AppWidgetHost host = new AppWidgetHost( 542 getInstrumentation().getTargetContext(), 0); 543 host.deleteHost(); 544 host.startListening(); 545 546 int appWidgetId = 0; 547 548 try { 549 // Grab the provider we defined to be bound. 550 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 551 552 // Allocate an widget id to bind. 553 appWidgetId = host.allocateAppWidgetId(); 554 555 // Initially we have no options. 556 Bundle foundOptions = getAppWidgetManager().getAppWidgetOptions(appWidgetId); 557 assertTrue(foundOptions.isEmpty()); 558 559 // We want to set the options when binding. 560 Bundle setOptions = new Bundle(); 561 setOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, 1); 562 setOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, 2); 563 setOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, 3); 564 setOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, 4); 565 566 // Bind the app widget. 567 getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId, 568 provider.getProfile(), provider.provider, setOptions); 569 570 // Make sure we get the options used when binding. 571 foundOptions = getAppWidgetManager().getAppWidgetOptions(appWidgetId); 572 assertTrue(equalOptions(setOptions, foundOptions)); 573 } finally { 574 // Clean up. 575 host.deleteAppWidgetId(appWidgetId); 576 host.deleteHost(); 577 revokeBindAppWidgetPermission(); 578 } 579 } 580 581 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 582 @Test 583 public void testDeleteHost() throws Exception { 584 // We want to bind widgets. 585 grantBindAppWidgetPermission(); 586 587 // Create a host and start listening. 588 AppWidgetHost host = new AppWidgetHost( 589 getInstrumentation().getTargetContext(), 0); 590 host.deleteHost(); 591 host.startListening(); 592 593 int appWidgetId = 0; 594 595 try { 596 // Allocate an widget id to bind. 597 appWidgetId = host.allocateAppWidgetId(); 598 599 // Grab the provider we defined to be bound. 600 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 601 602 // Bind the app widget. 603 getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId, 604 provider.getProfile(), provider.provider, null); 605 606 // The widget should be there. 607 int[] widgetIds = getAppWidgetManager().getAppWidgetIds(provider.provider); 608 assertTrue(Arrays.equals(widgetIds, new int[]{appWidgetId})); 609 610 // Delete the host. 611 host.deleteHost(); 612 613 // The host is gone and with it the widgets. 614 widgetIds = getAppWidgetManager().getAppWidgetIds(provider.provider); 615 assertTrue(widgetIds.length == 0); 616 } finally { 617 // Clean up. 618 host.deleteAppWidgetId(appWidgetId); 619 host.deleteHost(); 620 revokeBindAppWidgetPermission(); 621 } 622 } 623 624 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 625 @Test 626 public void testDeleteHosts() throws Exception { 627 // We want to bind widgets. 628 grantBindAppWidgetPermission(); 629 630 // Create the first host and start listening. 631 AppWidgetHost firstHost = new AppWidgetHost( 632 getInstrumentation().getTargetContext(), 0); 633 firstHost.deleteHost(); 634 firstHost.startListening(); 635 636 // Create the second host and start listening. 637 AppWidgetHost secondHost = new AppWidgetHost( 638 getInstrumentation().getTargetContext(), 1); 639 secondHost.deleteHost(); 640 secondHost.startListening(); 641 642 int firstAppWidgetId = 0; 643 int secondAppWidgetId = 0; 644 645 try { 646 // Grab the provider we defined to be bound. 647 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 648 649 // Allocate the first widget id to bind. 650 firstAppWidgetId = firstHost.allocateAppWidgetId(); 651 652 // Bind the first app widget. 653 getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId, 654 provider.getProfile(), provider.provider, null); 655 656 // Allocate the second widget id to bind. 657 secondAppWidgetId = secondHost.allocateAppWidgetId(); 658 659 // Bind the second app widget. 660 getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId, 661 provider.getProfile(), provider.provider, null); 662 663 // The widgets should be there. 664 int[] widgetIds = getAppWidgetManager().getAppWidgetIds(provider.provider); 665 assertTrue(Arrays.equals(widgetIds, new int[]{firstAppWidgetId, secondAppWidgetId})); 666 667 // Delete all hosts. 668 AppWidgetHost.deleteAllHosts(); 669 670 // The hosts are gone and with it the widgets. 671 widgetIds = getAppWidgetManager().getAppWidgetIds(provider.provider); 672 assertTrue(widgetIds.length == 0); 673 } finally { 674 // Clean up. 675 firstHost.deleteAppWidgetId(firstAppWidgetId); 676 secondHost.deleteAppWidgetId(secondAppWidgetId); 677 AppWidgetHost.deleteAllHosts(); 678 revokeBindAppWidgetPermission(); 679 } 680 } 681 682 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 683 @Test 684 public void testOnProvidersChanged() throws Exception { 685 // We want to bind widgets. 686 grantBindAppWidgetPermission(); 687 688 final AtomicInteger onProvidersChangedCallCounter = new AtomicInteger(); 689 690 // Create a host and start listening. 691 AppWidgetHost host = new AppWidgetHost( 692 getInstrumentation().getTargetContext(), 0) { 693 @Override 694 public void onProvidersChanged() { 695 synchronized (mLock) { 696 onProvidersChangedCallCounter.incrementAndGet(); 697 mLock.notifyAll(); 698 } 699 } 700 }; 701 host.deleteHost(); 702 host.startListening(); 703 704 int appWidgetId = 0; 705 706 try { 707 // Grab the provider we defined to be bound. 708 AppWidgetProviderInfo firstLookupProvider = getFirstAppWidgetProviderInfo(); 709 710 // Allocate a widget id to bind. 711 appWidgetId = host.allocateAppWidgetId(); 712 713 // Bind the first app widget. 714 getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId, 715 firstLookupProvider.getProfile(), firstLookupProvider.provider, null); 716 717 // Disable the provider we just bound to. 718 PackageManager packageManager = getInstrumentation().getTargetContext() 719 .getApplicationContext().getPackageManager(); 720 packageManager.setComponentEnabledSetting(firstLookupProvider.provider, 721 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 722 PackageManager.DONT_KILL_APP); 723 724 // Wait for the package change to propagate. 725 waitForCallCount(onProvidersChangedCallCounter, 1); 726 727 // The provider should not be present anymore. 728 AppWidgetProviderInfo secondLookupProvider = getFirstAppWidgetProviderInfo(); 729 assertNull(secondLookupProvider); 730 731 // Enable the provider we disabled. 732 packageManager.setComponentEnabledSetting(firstLookupProvider.provider, 733 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 734 PackageManager.DONT_KILL_APP); 735 736 // Wait for the package change to propagate. 737 waitForCallCount(onProvidersChangedCallCounter, 2); 738 } finally { 739 // Clean up. 740 host.deleteAppWidgetId(appWidgetId); 741 host.deleteHost(); 742 revokeBindAppWidgetPermission(); 743 } 744 } 745 746 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 747 @Test 748 public void testUpdateAppWidgetViaComponentName() throws Exception { 749 // We want to bind widgets. 750 grantBindAppWidgetPermission(); 751 752 final AtomicInteger updateAppWidgetCallCount = new AtomicInteger(); 753 754 // Create a host and start listening. 755 AppWidgetHost host = new AppWidgetHost( 756 getInstrumentation().getTargetContext(), 0) { 757 @Override 758 protected AppWidgetHostView onCreateView(Context context, int appWidgetId, 759 AppWidgetProviderInfo appWidget) { 760 return new MyAppWidgetHostView(context); 761 } 762 }; 763 host.deleteHost(); 764 host.startListening(); 765 766 int firstAppWidgetId = 0; 767 int secondAppWidgetId = 0; 768 769 try { 770 // Grab the provider to be bound. 771 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 772 773 // Allocate the first widget id to bind. 774 firstAppWidgetId = host.allocateAppWidgetId(); 775 776 // Bind the first app widget. 777 getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId, 778 provider.getProfile(), provider.provider, null); 779 780 // Create the first host view. 781 MyAppWidgetHostView firstHostView = (MyAppWidgetHostView) host.createView( 782 getInstrumentation().getContext(), firstAppWidgetId, provider); 783 MyAppWidgetHostView.OnUpdateAppWidgetListener firstAppHostViewListener = 784 mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class); 785 firstHostView.setOnUpdateAppWidgetListener(firstAppHostViewListener); 786 787 // Allocate the second widget id to bind. 788 secondAppWidgetId = host.allocateAppWidgetId(); 789 790 // Bind the second app widget. 791 getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId, 792 provider.getProfile(), provider.provider, null); 793 794 // Create the second host view. 795 MyAppWidgetHostView secondHostView = (MyAppWidgetHostView) host.createView( 796 getInstrumentation().getContext(), secondAppWidgetId, provider); 797 MyAppWidgetHostView.OnUpdateAppWidgetListener secondAppHostViewListener = 798 mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class); 799 doAnswer(new Answer<Void>() { 800 @Override 801 public Void answer(InvocationOnMock invocation) throws Throwable { 802 synchronized (mLock) { 803 updateAppWidgetCallCount.incrementAndGet(); 804 mLock.notifyAll(); 805 } 806 return null; 807 } 808 }).when(secondAppHostViewListener).onUpdateAppWidget(any(RemoteViews.class)); 809 secondHostView.setOnUpdateAppWidgetListener(secondAppHostViewListener); 810 811 // Update all app widgets. 812 final RemoteViews content = new RemoteViews( 813 getInstrumentation().getContext().getPackageName(), 814 R.layout.second_initial_layout); 815 getAppWidgetManager().updateAppWidget(provider.provider, content); 816 817 waitForCallCount(updateAppWidgetCallCount, 1); 818 819 // Verify the expected callbacks. 820 InOrder firstInOrder = inOrder(firstAppHostViewListener); 821 firstInOrder.verify(firstAppHostViewListener).onUpdateAppWidget(argThat( 822 new RemoteViewsMatcher(content.getLayoutId(), 823 provider.provider.getPackageName()))); 824 825 InOrder secondInOrder = inOrder(secondAppHostViewListener); 826 secondInOrder.verify(secondAppHostViewListener).onUpdateAppWidget(argThat( 827 new RemoteViewsMatcher(content.getLayoutId(), 828 provider.provider.getPackageName()))); 829 } finally { 830 // Clean up. 831 host.deleteAppWidgetId(firstAppWidgetId); 832 host.deleteAppWidgetId(secondAppWidgetId); 833 host.deleteHost(); 834 revokeBindAppWidgetPermission(); 835 } 836 } 837 838 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 839 @Test 840 public void testUpdateAppWidgetViaWidgetId() throws Exception { 841 // We want to bind widgets. 842 grantBindAppWidgetPermission(); 843 844 final AtomicInteger updateAppWidgetCallCount = new AtomicInteger(); 845 846 // Create a host and start listening. 847 AppWidgetHost host = new AppWidgetHost( 848 getInstrumentation().getTargetContext(), 0) { 849 @Override 850 protected AppWidgetHostView onCreateView(Context context, int appWidgetId, 851 AppWidgetProviderInfo appWidget) { 852 return new MyAppWidgetHostView(context); 853 } 854 }; 855 host.deleteHost(); 856 host.startListening(); 857 858 int firstAppWidgetId = 0; 859 860 try { 861 // Grab the provider to be bound. 862 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 863 864 // Allocate the first widget id to bind. 865 firstAppWidgetId = host.allocateAppWidgetId(); 866 867 // Bind the first app widget. 868 getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId, 869 provider.getProfile(), provider.provider, null); 870 871 // Create the first host view. 872 MyAppWidgetHostView hostView = (MyAppWidgetHostView) host.createView( 873 getInstrumentation().getContext(), firstAppWidgetId, provider); 874 MyAppWidgetHostView.OnUpdateAppWidgetListener appHostViewListener = 875 mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class); 876 doAnswer(new Answer<Void>() { 877 @Override 878 public Void answer(InvocationOnMock invocation) throws Throwable { 879 synchronized (mLock) { 880 updateAppWidgetCallCount.incrementAndGet(); 881 mLock.notifyAll(); 882 } 883 return null; 884 } 885 }).when(appHostViewListener).onUpdateAppWidget(any(RemoteViews.class)); 886 hostView.setOnUpdateAppWidgetListener(appHostViewListener); 887 888 // Update all app widgets. 889 RemoteViews content = new RemoteViews( 890 getInstrumentation().getContext().getPackageName(), 891 R.layout.second_initial_layout); 892 getAppWidgetManager().updateAppWidget(firstAppWidgetId, content); 893 894 waitForCallCount(updateAppWidgetCallCount, 1); 895 896 // Verify the expected callbacks. 897 InOrder inOrder = inOrder(appHostViewListener); 898 inOrder.verify(appHostViewListener).onUpdateAppWidget(argThat( 899 new RemoteViewsMatcher(content.getLayoutId(), 900 provider.provider.getPackageName()) 901 )); 902 } finally { 903 // Clean up. 904 host.deleteAppWidgetId(firstAppWidgetId); 905 host.deleteHost(); 906 revokeBindAppWidgetPermission(); 907 } 908 } 909 910 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 911 @Test 912 public void testUpdateAppWidgetViaWidgetIds() throws Exception { 913 // We want to bind widgets. 914 grantBindAppWidgetPermission(); 915 916 final AtomicInteger onUpdateAppWidgetCallCount = new AtomicInteger(); 917 918 // Create a host and start listening. 919 AppWidgetHost host = new AppWidgetHost( 920 getInstrumentation().getTargetContext(), 0) { 921 @Override 922 protected AppWidgetHostView onCreateView(Context context, int appWidgetId, 923 AppWidgetProviderInfo appWidget) { 924 return new MyAppWidgetHostView(context); 925 } 926 }; 927 host.deleteHost(); 928 host.startListening(); 929 930 int firstAppWidgetId = 0; 931 int secondAppWidgetId = 0; 932 933 try { 934 // Grab the provider to be bound. 935 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 936 937 // Allocate the first widget id to bind. 938 firstAppWidgetId = host.allocateAppWidgetId(); 939 940 // Bind the first app widget. 941 getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId, 942 provider.getProfile(), provider.provider, null); 943 944 // Create the first host view. 945 MyAppWidgetHostView firstHostView = (MyAppWidgetHostView) host.createView( 946 getInstrumentation().getContext(), firstAppWidgetId, provider); 947 MyAppWidgetHostView.OnUpdateAppWidgetListener firstAppHostViewListener = 948 mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class); 949 firstHostView.setOnUpdateAppWidgetListener(firstAppHostViewListener); 950 951 // Allocate the second widget id to bind. 952 secondAppWidgetId = host.allocateAppWidgetId(); 953 954 // Bind the second app widget. 955 getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId, 956 provider.getProfile(), provider.provider, null); 957 958 // Create the second host view. 959 MyAppWidgetHostView secondHostView = (MyAppWidgetHostView) host.createView( 960 getInstrumentation().getContext(), secondAppWidgetId, provider); 961 MyAppWidgetHostView.OnUpdateAppWidgetListener secondAppHostViewListener = 962 mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class); 963 doAnswer(new Answer<Void>() { 964 @Override 965 public Void answer(InvocationOnMock invocation) throws Throwable { 966 synchronized (mLock) { 967 onUpdateAppWidgetCallCount.incrementAndGet(); 968 mLock.notifyAll(); 969 } 970 return null; 971 } 972 }).when(secondAppHostViewListener).onUpdateAppWidget(any(RemoteViews.class)); 973 secondHostView.setOnUpdateAppWidgetListener(secondAppHostViewListener); 974 975 // Update all app widgets. 976 RemoteViews content = new RemoteViews( 977 getInstrumentation().getContext().getPackageName(), 978 R.layout.second_initial_layout); 979 getAppWidgetManager().updateAppWidget(new int[] {firstAppWidgetId, 980 secondAppWidgetId}, content); 981 982 waitForCallCount(onUpdateAppWidgetCallCount, 1); 983 984 // Verify the expected callbacks. 985 InOrder firstInOrder = inOrder(firstAppHostViewListener); 986 firstInOrder.verify(firstAppHostViewListener).onUpdateAppWidget( 987 argThat(new RemoteViewsMatcher(content.getLayoutId(), 988 provider.provider.getPackageName()))); 989 990 InOrder secondInOrder = inOrder(secondAppHostViewListener); 991 secondInOrder.verify(secondAppHostViewListener).onUpdateAppWidget( 992 argThat(new RemoteViewsMatcher(content.getLayoutId(), 993 provider.provider.getPackageName())) 994 ); 995 } finally { 996 // Clean up. 997 host.deleteAppWidgetId(firstAppWidgetId); 998 host.deleteAppWidgetId(secondAppWidgetId); 999 host.deleteHost(); 1000 revokeBindAppWidgetPermission(); 1001 } 1002 } 1003 1004 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 1005 @Test 1006 public void testPartiallyUpdateAppWidgetViaWidgetId() throws Exception { 1007 // We want to bind widgets. 1008 grantBindAppWidgetPermission(); 1009 1010 final AtomicInteger updateAppWidgetCallCount = new AtomicInteger(); 1011 1012 // Create a host and start listening. 1013 AppWidgetHost host = new AppWidgetHost( 1014 getInstrumentation().getTargetContext(), 0) { 1015 @Override 1016 protected AppWidgetHostView onCreateView(Context context, int appWidgetId, 1017 AppWidgetProviderInfo appWidget) { 1018 return new MyAppWidgetHostView(context); 1019 } 1020 }; 1021 host.deleteHost(); 1022 host.startListening(); 1023 1024 int firstAppWidgetId = 0; 1025 1026 try { 1027 // Grab the provider to be bound. 1028 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 1029 1030 // Allocate the first widget id to bind. 1031 firstAppWidgetId = host.allocateAppWidgetId(); 1032 1033 // Bind the first app widget. 1034 getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId, 1035 provider.getProfile(), provider.provider, null); 1036 1037 // Create the first host view. 1038 MyAppWidgetHostView hostView = (MyAppWidgetHostView) host.createView( 1039 getInstrumentation().getContext(), firstAppWidgetId, provider); 1040 MyAppWidgetHostView.OnUpdateAppWidgetListener appHostViewListener = 1041 mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class); 1042 doAnswer(new Answer<Void>() { 1043 @Override 1044 public Void answer(InvocationOnMock invocation) throws Throwable { 1045 synchronized (mLock) { 1046 updateAppWidgetCallCount.incrementAndGet(); 1047 mLock.notifyAll(); 1048 } 1049 return null; 1050 } 1051 }).when(appHostViewListener).onUpdateAppWidget(any(RemoteViews.class)); 1052 hostView.setOnUpdateAppWidgetListener(appHostViewListener); 1053 1054 // Set the content for all app widgets. 1055 RemoteViews content = new RemoteViews( 1056 getInstrumentation().getContext().getPackageName(), 1057 R.layout.first_initial_layout); 1058 getAppWidgetManager().updateAppWidget(firstAppWidgetId, content); 1059 1060 waitForCallCount(updateAppWidgetCallCount, 1); 1061 1062 // Partially update the content for all app widgets (pretend we changed something). 1063 getAppWidgetManager().partiallyUpdateAppWidget(firstAppWidgetId, content); 1064 1065 waitForCallCount(updateAppWidgetCallCount, 2); 1066 1067 // Verify the expected callbacks. 1068 InOrder inOrder = inOrder(appHostViewListener); 1069 inOrder.verify(appHostViewListener, times(2)).onUpdateAppWidget( 1070 argThat(new RemoteViewsMatcher(content.getLayoutId(), 1071 provider.provider.getPackageName()))); 1072 } finally { 1073 // Clean up. 1074 host.deleteAppWidgetId(firstAppWidgetId); 1075 host.deleteHost(); 1076 revokeBindAppWidgetPermission(); 1077 } 1078 } 1079 1080 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 1081 @Test 1082 public void testPartiallyUpdateAppWidgetViaWidgetIds() throws Exception { 1083 // We want to bind widgets. 1084 grantBindAppWidgetPermission(); 1085 1086 final AtomicInteger firstAppWidgetCallCounter = new AtomicInteger(); 1087 final AtomicInteger secondAppWidgetCallCounter = new AtomicInteger(); 1088 1089 // Create a host and start listening. 1090 AppWidgetHost host = new AppWidgetHost( 1091 getInstrumentation().getTargetContext(), 0) { 1092 @Override 1093 protected AppWidgetHostView onCreateView(Context context, int appWidgetId, 1094 AppWidgetProviderInfo appWidget) { 1095 return new MyAppWidgetHostView(context); 1096 } 1097 }; 1098 host.deleteHost(); 1099 host.startListening(); 1100 1101 int firstAppWidgetId = 0; 1102 int secondAppWidgetId = 0; 1103 1104 try { 1105 // Grab the provider to be bound. 1106 AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 1107 1108 // Allocate the first widget id to bind. 1109 firstAppWidgetId = host.allocateAppWidgetId(); 1110 1111 // Bind the first app widget. 1112 getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId, 1113 provider.getProfile(), provider.provider, null); 1114 1115 // Create the first host view. 1116 MyAppWidgetHostView firstHostView = (MyAppWidgetHostView) host.createView( 1117 getInstrumentation().getContext(), firstAppWidgetId, provider); 1118 MyAppWidgetHostView.OnUpdateAppWidgetListener firstAppHostViewListener = 1119 mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class); 1120 doAnswer(new Answer<Void>() { 1121 @Override 1122 public Void answer(InvocationOnMock invocation) throws Throwable { 1123 synchronized (mLock) { 1124 firstAppWidgetCallCounter.incrementAndGet(); 1125 mLock.notifyAll(); 1126 } 1127 return null; 1128 } 1129 }).when(firstAppHostViewListener).onUpdateAppWidget(any(RemoteViews.class)); 1130 firstHostView.setOnUpdateAppWidgetListener(firstAppHostViewListener); 1131 1132 // Allocate the second widget id to bind. 1133 secondAppWidgetId = host.allocateAppWidgetId(); 1134 1135 // Bind the second app widget. 1136 getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId, 1137 provider.getProfile(), provider.provider, null); 1138 1139 // Create the second host view. 1140 MyAppWidgetHostView secondHostView = (MyAppWidgetHostView) host.createView( 1141 getInstrumentation().getContext(), secondAppWidgetId, provider); 1142 MyAppWidgetHostView.OnUpdateAppWidgetListener secondAppHostViewListener = 1143 mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class); 1144 doAnswer(new Answer<Void>() { 1145 @Override 1146 public Void answer(InvocationOnMock invocation) throws Throwable { 1147 synchronized (mLock) { 1148 secondAppWidgetCallCounter.incrementAndGet(); 1149 mLock.notifyAll(); 1150 } 1151 return null; 1152 } 1153 }).when(secondAppHostViewListener).onUpdateAppWidget(any(RemoteViews.class)); 1154 secondHostView.setOnUpdateAppWidgetListener(secondAppHostViewListener); 1155 1156 // Set the content for all app widgets. 1157 RemoteViews content = new RemoteViews( 1158 getInstrumentation().getContext().getPackageName(), 1159 R.layout.first_initial_layout); 1160 getAppWidgetManager().updateAppWidget(new int[]{firstAppWidgetId, 1161 secondAppWidgetId}, content); 1162 1163 waitForCallCount(firstAppWidgetCallCounter, 1); 1164 waitForCallCount(secondAppWidgetCallCounter, 1); 1165 1166 // Partially update the content for all app widgets (pretend we changed something). 1167 getAppWidgetManager().partiallyUpdateAppWidget(new int[] {firstAppWidgetId, 1168 secondAppWidgetId}, content); 1169 1170 waitForCallCount(firstAppWidgetCallCounter, 2); 1171 waitForCallCount(secondAppWidgetCallCounter, 2); 1172 1173 // Verify the expected callbacks. 1174 InOrder firstInOrder = inOrder(firstAppHostViewListener); 1175 firstInOrder.verify(firstAppHostViewListener, times(2)).onUpdateAppWidget( 1176 argThat(new RemoteViewsMatcher(content.getLayoutId(), 1177 provider.provider.getPackageName()))); 1178 1179 InOrder secondInOrder = inOrder(secondAppHostViewListener); 1180 secondInOrder.verify(secondAppHostViewListener, times(2)).onUpdateAppWidget( 1181 argThat(new RemoteViewsMatcher(content.getLayoutId(), 1182 provider.provider.getPackageName()))); 1183 } finally { 1184 // Clean up. 1185 host.deleteAppWidgetId(firstAppWidgetId); 1186 host.deleteAppWidgetId(secondAppWidgetId); 1187 host.deleteHost(); 1188 revokeBindAppWidgetPermission(); 1189 } 1190 } 1191 1192 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 1193 @Test 1194 public void testCollectionWidgets() throws Exception { 1195 // We want to bind widgets. 1196 grantBindAppWidgetPermission(); 1197 1198 final AtomicInteger invocationCounter = new AtomicInteger(); 1199 final Context context = getInstrumentation().getTargetContext(); 1200 1201 // Create a host and start listening. 1202 final AppWidgetHost host = new AppWidgetHost(context, 0); 1203 host.deleteHost(); 1204 host.startListening(); 1205 1206 final int appWidgetId; 1207 1208 try { 1209 // Configure the provider behavior. 1210 AppWidgetProviderCallbacks callbacks = createAppWidgetProviderCallbacks( 1211 invocationCounter); 1212 doAnswer(new Answer<Void>() { 1213 @Override 1214 public Void answer(InvocationOnMock invocation) throws Throwable { 1215 final int appWidgetId = ((int[]) invocation.getArguments()[2])[0]; 1216 1217 Intent intent = new Intent(context, MyAppWidgetService.class); 1218 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); 1219 intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME))); 1220 1221 RemoteViews removeViews = new RemoteViews(context.getPackageName(), 1222 R.layout.collection_widget_layout); 1223 removeViews.setRemoteAdapter(R.id.stack_view, intent); 1224 1225 getAppWidgetManager().updateAppWidget(appWidgetId, removeViews); 1226 1227 synchronized (mLock) { 1228 invocationCounter.incrementAndGet(); 1229 mLock.notifyAll(); 1230 } 1231 1232 return null; 1233 } 1234 }).when(callbacks).onUpdate(any(Context.class), any(AppWidgetManager.class), 1235 any(int[].class)); 1236 FirstAppWidgetProvider.setCallbacks(callbacks); 1237 1238 // Grab the provider to be bound. 1239 final AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo(); 1240 1241 // Allocate a widget id to bind. 1242 appWidgetId = host.allocateAppWidgetId(); 1243 1244 // Bind the app widget. 1245 getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId, 1246 provider.getProfile(), provider.provider, null); 1247 1248 // Wait for onEnabled and onUpdate 1249 waitForCallCount(invocationCounter, 2); 1250 1251 // Configure the app widget service behavior. 1252 RemoteViewsFactory factory = mock(RemoteViewsFactory.class); 1253 doAnswer(new Answer<Integer>() { 1254 @Override 1255 public Integer answer(InvocationOnMock invocation) throws Throwable { 1256 return 1; 1257 } 1258 }).when(factory).getCount(); 1259 doAnswer(new Answer<RemoteViews>() { 1260 @Override 1261 public RemoteViews answer(InvocationOnMock invocation) throws Throwable { 1262 RemoteViews remoteViews = new RemoteViews(context.getPackageName(), 1263 R.layout.collection_widget_item_layout); 1264 remoteViews.setTextViewText(R.id.text_view, context.getText(R.string.foo)); 1265 synchronized (mLock) { 1266 invocationCounter.incrementAndGet(); 1267 } 1268 return remoteViews; 1269 } 1270 }).when(factory).getViewAt(any(int.class)); 1271 doAnswer(new Answer<Integer>() { 1272 @Override 1273 public Integer answer(InvocationOnMock invocation) throws Throwable { 1274 return 1; 1275 } 1276 }).when(factory).getViewTypeCount(); 1277 MyAppWidgetService.setFactory(factory); 1278 1279 getInstrumentation().runOnMainSync(new Runnable() { 1280 @Override 1281 public void run() { 1282 host.createView(context, appWidgetId, provider); 1283 } 1284 }); 1285 1286 // Wait for the interactions to occur. 1287 waitForCallCount(invocationCounter, 3); 1288 1289 // Verify the interactions. 1290 verify(factory, atLeastOnce()).hasStableIds(); 1291 verify(factory, atLeastOnce()).getViewTypeCount(); 1292 verify(factory, atLeastOnce()).getCount(); 1293 verify(factory, atLeastOnce()).getLoadingView(); 1294 verify(factory, atLeastOnce()).getViewAt(same(0)); 1295 } finally { 1296 // Clean up. 1297 FirstAppWidgetProvider.setCallbacks(null); 1298 host.deleteHost(); 1299 revokeBindAppWidgetPermission(); 1300 } 1301 } 1302 1303 @AppModeFull(reason = "Instant apps cannot provide or host app widgets") 1304 @Test 1305 public void testWidgetFeaturesParsed() throws Exception { 1306 assertEquals(0, getFirstAppWidgetProviderInfo().widgetFeatures); 1307 String packageName = getInstrumentation().getTargetContext().getPackageName(); 1308 1309 assertEquals(AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE, 1310 getProviderInfo(new ComponentName(packageName, 1311 AppWidgetProviderWithFeatures.Provider1.class.getName())).widgetFeatures); 1312 assertEquals(AppWidgetProviderInfo.WIDGET_FEATURE_HIDE_FROM_PICKER, 1313 getProviderInfo(new ComponentName(packageName, 1314 AppWidgetProviderWithFeatures.Provider2.class.getName())).widgetFeatures); 1315 assertEquals(AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE 1316 | AppWidgetProviderInfo.WIDGET_FEATURE_HIDE_FROM_PICKER, 1317 getProviderInfo(new ComponentName(packageName, 1318 AppWidgetProviderWithFeatures.Provider3.class.getName())).widgetFeatures); 1319 } 1320 1321 private void waitForCallCount(AtomicInteger counter, int expectedCount) { 1322 synchronized (mLock) { 1323 final long startTimeMillis = SystemClock.uptimeMillis(); 1324 while (counter.get() < expectedCount) { 1325 final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; 1326 final long remainingTimeMillis = OPERATION_TIMEOUT - elapsedTimeMillis; 1327 if (remainingTimeMillis <= 0) { 1328 fail("Did not get expected call"); 1329 } 1330 try { 1331 mLock.wait(remainingTimeMillis); 1332 } catch (InterruptedException ie) { 1333 /* ignore */ 1334 } 1335 } 1336 } 1337 } 1338 1339 @SuppressWarnings("deprecation") 1340 private void assertExpectedInstalledProviders(List<AppWidgetProviderInfo> providers) { 1341 boolean[] verifiedWidgets = verifyInstalledProviders(providers); 1342 assertTrue(verifiedWidgets[0]); 1343 assertTrue(verifiedWidgets[1]); 1344 } 1345 1346 private void grantBindAppWidgetPermission() throws Exception { 1347 runShellCommand(GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND); 1348 } 1349 1350 private void revokeBindAppWidgetPermission() throws Exception { 1351 runShellCommand(REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND); 1352 } 1353 1354 private AppWidgetProviderInfo getFirstAppWidgetProviderInfo() { 1355 return getProviderInfo(getFirstWidgetComponent()); 1356 } 1357 1358 private AppWidgetProviderInfo getSecondAppWidgetProviderInfo() { 1359 return getProviderInfo(getSecondWidgetComponent()); 1360 } 1361 1362 private AppWidgetProviderInfo getProviderInfo(ComponentName componentName) { 1363 List<AppWidgetProviderInfo> providers = getAppWidgetManager().getInstalledProviders(); 1364 1365 final int providerCount = providers.size(); 1366 for (int i = 0; i < providerCount; i++) { 1367 AppWidgetProviderInfo provider = providers.get(i); 1368 if (componentName.equals(provider.provider) 1369 && Process.myUserHandle().equals(provider.getProfile())) { 1370 return provider; 1371 1372 } 1373 } 1374 1375 return null; 1376 } 1377 1378 private AppWidgetManager getAppWidgetManager() { 1379 return (AppWidgetManager) getInstrumentation().getTargetContext() 1380 .getSystemService(Context.APPWIDGET_SERVICE); 1381 } 1382 1383 private AppWidgetProviderCallbacks createAppWidgetProviderCallbacks( 1384 final AtomicInteger callCounter) { 1385 // Set a mock to intercept provider callbacks. 1386 AppWidgetProviderCallbacks callbacks = mock(AppWidgetProviderCallbacks.class); 1387 1388 // onEnabled 1389 doAnswer(new Answer<Void>() { 1390 @Override 1391 public Void answer(InvocationOnMock invocation) throws Throwable { 1392 synchronized (mLock) { 1393 callCounter.incrementAndGet(); 1394 mLock.notifyAll(); 1395 } 1396 return null; 1397 } 1398 }).when(callbacks).onEnabled(any(Context.class)); 1399 1400 // onUpdate 1401 doAnswer(new Answer<Void>() { 1402 @Override 1403 public Void answer(InvocationOnMock invocation) throws Throwable { 1404 synchronized (mLock) { 1405 callCounter.incrementAndGet(); 1406 mLock.notifyAll(); 1407 } 1408 return null; 1409 } 1410 }).when(callbacks).onUpdate(any(Context.class), any(AppWidgetManager.class), 1411 any(int[].class)); 1412 1413 // onAppWidgetOptionsChanged 1414 doAnswer(new Answer<Void>() { 1415 @Override 1416 public Void answer(InvocationOnMock invocation) throws Throwable { 1417 synchronized (mLock) { 1418 callCounter.incrementAndGet(); 1419 mLock.notifyAll(); 1420 } 1421 return null; 1422 } 1423 }).when(callbacks).onAppWidgetOptionsChanged(any(Context.class), 1424 any(AppWidgetManager.class), any(int.class), any(Bundle.class)); 1425 1426 // onDeleted 1427 doAnswer(new Answer<Void>() { 1428 @Override 1429 public Void answer(InvocationOnMock invocation) throws Throwable { 1430 synchronized (mLock) { 1431 callCounter.incrementAndGet(); 1432 mLock.notifyAll(); 1433 } 1434 return null; 1435 } 1436 }).when(callbacks).onDeleted(any(Context.class), any(int[].class)); 1437 1438 // onDisabled 1439 doAnswer(new Answer<Void>() { 1440 @Override 1441 public Void answer(InvocationOnMock invocation) throws Throwable { 1442 synchronized (mLock) { 1443 callCounter.incrementAndGet(); 1444 mLock.notifyAll(); 1445 } 1446 return null; 1447 } 1448 }).when(callbacks).onDisabled(any(Context.class)); 1449 1450 // onRestored 1451 doAnswer(new Answer<Void>() { 1452 @Override 1453 public Void answer(InvocationOnMock invocation) throws Throwable { 1454 synchronized (mLock) { 1455 callCounter.incrementAndGet(); 1456 mLock.notifyAll(); 1457 } 1458 return null; 1459 } 1460 }).when(callbacks).onRestored(any(Context.class), any(int[].class), 1461 any(int[].class)); 1462 1463 return callbacks; 1464 } 1465 1466 private static boolean equalOptions(Bundle first, Bundle second) { 1467 return first.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH) 1468 == second.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH) 1469 && first.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT) 1470 == second.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT) 1471 && first.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH) 1472 == second.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH) 1473 && first.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT) 1474 == second.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT); 1475 } 1476 1477 private static final class OptionsMatcher extends BaseMatcher<Bundle> { 1478 private Bundle mOptions; 1479 1480 public OptionsMatcher(Bundle options) { 1481 mOptions = options; 1482 } 1483 1484 @Override 1485 public boolean matches(Object item) { 1486 Bundle options = (Bundle) item; 1487 return equalOptions(mOptions, options); 1488 } 1489 1490 @Override 1491 public void describeTo(Description description) { 1492 /* do nothing */ 1493 } 1494 } 1495 1496 private static final class WidgetIdsMatcher extends BaseMatcher<int[]> { 1497 private final int[] mWidgetIds; 1498 1499 public WidgetIdsMatcher(int[] widgetIds) { 1500 mWidgetIds = widgetIds; 1501 } 1502 1503 @Override 1504 public boolean matches(Object item) { 1505 final int[] widgetIds = (int[]) item; 1506 return Arrays.equals(widgetIds, mWidgetIds); 1507 } 1508 1509 @Override 1510 public void describeTo(Description description) { 1511 /* do nothing */ 1512 } 1513 } 1514 1515 private static final class RemoteViewsMatcher extends BaseMatcher<RemoteViews> { 1516 private final int mLayoutId; 1517 private final String mPackageName; 1518 1519 public RemoteViewsMatcher(int layoutId, String packageName) { 1520 mLayoutId = layoutId; 1521 mPackageName = packageName; 1522 } 1523 1524 @Override 1525 public boolean matches(Object item) { 1526 final RemoteViews remoteViews = (RemoteViews) item; 1527 return remoteViews != null && remoteViews.getLayoutId() == mLayoutId 1528 && remoteViews.getPackage().equals(mPackageName); 1529 } 1530 1531 @Override 1532 public void describeTo(Description description) { 1533 /* do nothing */ 1534 } 1535 } 1536 1537 private static class MyAppWidgetHostView extends AppWidgetHostView { 1538 private OnUpdateAppWidgetListener mOnUpdateAppWidgetListener; 1539 1540 1541 public interface OnUpdateAppWidgetListener { 1542 public void onUpdateAppWidget(RemoteViews remoteViews); 1543 } 1544 1545 private MyAppWidgetHostView(Context context) { 1546 super(context); 1547 } 1548 1549 public void setOnUpdateAppWidgetListener(OnUpdateAppWidgetListener listener) { 1550 mOnUpdateAppWidgetListener = listener; 1551 } 1552 1553 @Override 1554 public void updateAppWidget(RemoteViews remoteViews) { 1555 super.updateAppWidget(remoteViews); 1556 if (mOnUpdateAppWidgetListener != null) { 1557 mOnUpdateAppWidgetListener.onUpdateAppWidget(remoteViews); 1558 } 1559 } 1560 } 1561 } 1562