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