1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.applications; 18 19 20 import android.app.AlertDialog; 21 import android.app.AppOpsManager; 22 import android.app.Fragment; 23 import android.app.LoaderManager; 24 import android.app.admin.DevicePolicyManager; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.pm.ApplicationInfo; 28 import android.content.pm.PackageInfo; 29 import android.content.pm.PackageManager; 30 import android.content.res.Resources; 31 import android.os.BatteryStats; 32 import android.os.Bundle; 33 import android.os.UserManager; 34 import android.support.v7.preference.Preference; 35 import android.support.v7.preference.PreferenceManager; 36 import android.support.v7.preference.PreferenceScreen; 37 import android.view.View; 38 import android.widget.Button; 39 40 import com.android.internal.os.BatterySipper; 41 import com.android.internal.os.BatteryStatsHelper; 42 import com.android.settings.R; 43 import com.android.settings.SettingsActivity; 44 import com.android.settings.testutils.SettingsRobolectricTestRunner; 45 import com.android.settings.TestConfig; 46 import com.android.settings.applications.instantapps.InstantAppButtonsController; 47 import com.android.settings.applications.instantapps.InstantAppButtonsController.ShowDialogDelegate; 48 import com.android.settings.enterprise.DevicePolicyManagerWrapper; 49 import com.android.settings.fuelgauge.BatteryUtils; 50 import com.android.settings.testutils.FakeFeatureFactory; 51 import com.android.settingslib.Utils; 52 import com.android.settingslib.applications.AppUtils; 53 import com.android.settingslib.applications.ApplicationsState.AppEntry; 54 import com.android.settingslib.applications.StorageStatsSource.AppStorageStats; 55 import com.android.settingslib.applications.instantapps.InstantAppDataProvider; 56 57 import org.junit.Before; 58 import org.junit.Test; 59 import org.junit.runner.RunWith; 60 import org.mockito.Answers; 61 import org.mockito.Mock; 62 import org.mockito.MockitoAnnotations; 63 import org.robolectric.RuntimeEnvironment; 64 import org.robolectric.annotation.Config; 65 import org.robolectric.annotation.Implementation; 66 import org.robolectric.annotation.Implements; 67 import org.robolectric.util.ReflectionHelpers; 68 69 import java.util.ArrayList; 70 import java.util.HashSet; 71 import java.util.List; 72 73 import static com.google.common.truth.Truth.assertThat; 74 import static org.mockito.ArgumentMatchers.nullable; 75 import static org.mockito.Matchers.anyDouble; 76 import static org.mockito.Matchers.anyInt; 77 import static org.mockito.Mockito.doReturn; 78 import static org.mockito.Mockito.mock; 79 import static org.mockito.Mockito.never; 80 import static org.mockito.Mockito.spy; 81 import static org.mockito.Mockito.times; 82 import static org.mockito.Mockito.verify; 83 import static org.mockito.Mockito.when; 84 85 86 @RunWith(SettingsRobolectricTestRunner.class) 87 @Config( 88 manifest = TestConfig.MANIFEST_PATH, 89 sdk = TestConfig.SDK_VERSION, 90 shadows = InstalledAppDetailsTest.ShadowUtils.class 91 ) 92 public final class InstalledAppDetailsTest { 93 94 private static final String PACKAGE_NAME = "test_package_name"; 95 private static final int TARGET_UID = 111; 96 private static final int OTHER_UID = 222; 97 private static final double BATTERY_LEVEL = 60; 98 private static final String BATTERY_LEVEL_STRING = "60%"; 99 100 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 101 private Context mContext; 102 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 103 private UserManager mUserManager; 104 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 105 private SettingsActivity mActivity; 106 @Mock 107 private DevicePolicyManagerWrapper mDevicePolicyManager; 108 @Mock 109 private BatterySipper mBatterySipper; 110 @Mock 111 private BatterySipper mOtherBatterySipper; 112 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 113 private BatteryStatsHelper mBatteryStatsHelper; 114 @Mock 115 private BatteryStats.Uid mUid; 116 @Mock 117 private PackageManager mPackageManager; 118 @Mock 119 private BatteryUtils mBatteryUtils; 120 @Mock 121 private LoaderManager mLoaderManager; 122 @Mock 123 private AppOpsManager mAppOpsManager; 124 125 private FakeFeatureFactory mFeatureFactory; 126 private InstalledAppDetails mAppDetail; 127 private Context mShadowContext; 128 private Preference mBatteryPreference; 129 130 131 @Before 132 public void setUp() { 133 MockitoAnnotations.initMocks(this); 134 mFeatureFactory = FakeFeatureFactory.setupForTest(mContext); 135 mShadowContext = RuntimeEnvironment.application; 136 mAppDetail = spy(new InstalledAppDetails()); 137 mAppDetail.mBatteryUtils = mBatteryUtils; 138 139 mBatteryPreference = new Preference(mShadowContext); 140 mAppDetail.mBatteryPreference = mBatteryPreference; 141 142 mBatterySipper.drainType = BatterySipper.DrainType.IDLE; 143 mBatterySipper.uidObj = mUid; 144 doReturn(TARGET_UID).when(mBatterySipper).getUid(); 145 doReturn(OTHER_UID).when(mOtherBatterySipper).getUid(); 146 doReturn(mActivity).when(mAppDetail).getActivity(); 147 doReturn(mShadowContext).when(mAppDetail).getContext(); 148 doReturn(mPackageManager).when(mActivity).getPackageManager(); 149 doReturn(mAppOpsManager).when(mActivity).getSystemService(Context.APP_OPS_SERVICE); 150 151 // Default to not considering any apps to be instant (individual tests can override this). 152 ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", 153 (InstantAppDataProvider) (i -> false)); 154 } 155 156 @Test 157 public void shouldShowUninstallForAll_installForOneOtherUserOnly_shouldReturnTrue() { 158 when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false); 159 when(mUserManager.getUsers().size()).thenReturn(2); 160 ReflectionHelpers.setField(mAppDetail, "mDpm", mDevicePolicyManager); 161 ReflectionHelpers.setField(mAppDetail, "mUserManager", mUserManager); 162 final ApplicationInfo info = new ApplicationInfo(); 163 info.enabled = true; 164 final AppEntry appEntry = mock(AppEntry.class); 165 appEntry.info = info; 166 final PackageInfo packageInfo = mock(PackageInfo.class); 167 ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo); 168 169 assertThat(mAppDetail.shouldShowUninstallForAll(appEntry)).isTrue(); 170 } 171 172 @Test 173 public void shouldShowUninstallForAll_installForSelfOnly_shouldReturnFalse() { 174 when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false); 175 when(mUserManager.getUsers().size()).thenReturn(2); 176 ReflectionHelpers.setField(mAppDetail, "mDpm", mDevicePolicyManager); 177 ReflectionHelpers.setField(mAppDetail, "mUserManager", mUserManager); 178 final ApplicationInfo info = new ApplicationInfo(); 179 info.flags = ApplicationInfo.FLAG_INSTALLED; 180 info.enabled = true; 181 final AppEntry appEntry = mock(AppEntry.class); 182 appEntry.info = info; 183 final PackageInfo packageInfo = mock(PackageInfo.class); 184 ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo); 185 186 assertThat(mAppDetail.shouldShowUninstallForAll(appEntry)).isFalse(); 187 } 188 189 @Test 190 public void getStorageSummary_shouldWorkForExternal() { 191 Context context = RuntimeEnvironment.application.getApplicationContext(); 192 AppStorageStats stats = mock(AppStorageStats.class); 193 when(stats.getTotalBytes()).thenReturn(1L); 194 195 assertThat(InstalledAppDetails.getStorageSummary(context, stats, true)) 196 .isEqualTo("1.00B used in external storage"); 197 } 198 199 @Test 200 public void getStorageSummary_shouldWorkForInternal() { 201 Context context = RuntimeEnvironment.application.getApplicationContext(); 202 AppStorageStats stats = mock(AppStorageStats.class); 203 when(stats.getTotalBytes()).thenReturn(1L); 204 205 assertThat(InstalledAppDetails.getStorageSummary(context, stats, false)) 206 .isEqualTo("1.00B used in internal storage"); 207 } 208 209 @Test 210 public void launchFragment_hasNoPackageInfo_shouldFinish() { 211 ReflectionHelpers.setField(mAppDetail, "mPackageInfo", null); 212 213 assertThat(mAppDetail.ensurePackageInfoAvailable(mActivity)).isFalse(); 214 verify(mActivity).finishAndRemoveTask(); 215 } 216 217 @Test 218 public void launchFragment_hasPackageInfo_shouldReturnTrue() { 219 final PackageInfo packageInfo = mock(PackageInfo.class); 220 ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo); 221 222 assertThat(mAppDetail.ensurePackageInfoAvailable(mActivity)).isTrue(); 223 verify(mActivity, never()).finishAndRemoveTask(); 224 } 225 226 @Test 227 public void packageSizeChange_isOtherPackage_shouldNotRefreshUi() { 228 ReflectionHelpers.setField(mAppDetail, "mPackageName", PACKAGE_NAME); 229 mAppDetail.onPackageSizeChanged("Not_" + PACKAGE_NAME); 230 231 verify(mAppDetail, never()).refreshUi(); 232 } 233 234 @Test 235 public void packageSizeChange_isOwnPackage_shouldRefreshUi() { 236 doReturn(Boolean.TRUE).when(mAppDetail).refreshUi(); 237 ReflectionHelpers.setField(mAppDetail, "mPackageName", PACKAGE_NAME); 238 239 mAppDetail.onPackageSizeChanged(PACKAGE_NAME); 240 241 verify(mAppDetail).refreshUi(); 242 } 243 244 @Test 245 public void launchPowerUsageDetailFragment_shouldNotCrash() { 246 mAppDetail.mBatteryPreference = mBatteryPreference; 247 mAppDetail.mSipper = mBatterySipper; 248 mAppDetail.mBatteryHelper = mBatteryStatsHelper; 249 250 // Should not crash 251 mAppDetail.onPreferenceClick(mBatteryPreference); 252 } 253 254 // Tests that we don't show the "uninstall for all users" button for instant apps. 255 @Test 256 public void instantApps_noUninstallForAllButton() { 257 // Make this app appear to be instant. 258 ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", 259 (InstantAppDataProvider) (i -> true)); 260 when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false); 261 when(mUserManager.getUsers().size()).thenReturn(2); 262 263 final ApplicationInfo info = new ApplicationInfo(); 264 info.enabled = true; 265 final AppEntry appEntry = mock(AppEntry.class); 266 appEntry.info = info; 267 final PackageInfo packageInfo = mock(PackageInfo.class); 268 269 ReflectionHelpers.setField(mAppDetail, "mDpm", mDevicePolicyManager); 270 ReflectionHelpers.setField(mAppDetail, "mUserManager", mUserManager); 271 ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo); 272 273 assertThat(mAppDetail.shouldShowUninstallForAll(appEntry)).isFalse(); 274 } 275 276 // Tests that we don't show the uninstall button for instant apps" 277 @Test 278 public void instantApps_noUninstallButton() { 279 // Make this app appear to be instant. 280 ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", 281 (InstantAppDataProvider) (i -> true)); 282 final ApplicationInfo info = new ApplicationInfo(); 283 info.flags = ApplicationInfo.FLAG_INSTALLED; 284 info.enabled = true; 285 final AppEntry appEntry = mock(AppEntry.class); 286 appEntry.info = info; 287 final PackageInfo packageInfo = mock(PackageInfo.class); 288 packageInfo.applicationInfo = info; 289 final Button uninstallButton = mock(Button.class); 290 291 ReflectionHelpers.setField(mAppDetail, "mUserManager", mUserManager); 292 ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry); 293 ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo); 294 ReflectionHelpers.setField(mAppDetail, "mUninstallButton", uninstallButton); 295 296 mAppDetail.initUnintsallButtonForUserApp(); 297 verify(uninstallButton).setVisibility(View.GONE); 298 } 299 300 // Tests that we don't show the force stop button for instant apps (they aren't allowed to run 301 // when they aren't in the foreground). 302 @Test 303 public void instantApps_noForceStop() { 304 // Make this app appear to be instant. 305 ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", 306 (InstantAppDataProvider) (i -> true)); 307 final PackageInfo packageInfo = mock(PackageInfo.class); 308 final AppEntry appEntry = mock(AppEntry.class); 309 final ApplicationInfo info = new ApplicationInfo(); 310 appEntry.info = info; 311 final Button forceStopButton = mock(Button.class); 312 313 ReflectionHelpers.setField(mAppDetail, "mDpm", mDevicePolicyManager); 314 ReflectionHelpers.setField(mAppDetail, "mPackageInfo", packageInfo); 315 ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry); 316 ReflectionHelpers.setField(mAppDetail, "mForceStopButton", forceStopButton); 317 318 mAppDetail.checkForceStop(); 319 verify(forceStopButton).setVisibility(View.GONE); 320 } 321 322 @Test 323 public void instantApps_buttonControllerHandlesDialog() { 324 InstantAppButtonsController mockController = mock(InstantAppButtonsController.class); 325 ReflectionHelpers.setField( 326 mAppDetail, "mInstantAppButtonsController", mockController); 327 // Make sure first that button controller is not called for supported dialog id 328 AlertDialog mockDialog = mock(AlertDialog.class); 329 when(mockController.createDialog(InstantAppButtonsController.DLG_CLEAR_APP)) 330 .thenReturn(mockDialog); 331 assertThat(mAppDetail.createDialog(InstantAppButtonsController.DLG_CLEAR_APP, 0)) 332 .isEqualTo(mockDialog); 333 verify(mockController).createDialog(InstantAppButtonsController.DLG_CLEAR_APP); 334 } 335 336 // A helper class for testing the InstantAppButtonsController - it lets us look up the 337 // preference associated with a key for instant app buttons and get back a mock 338 // LayoutPreference (to avoid a null pointer exception). 339 public static class InstalledAppDetailsWithMockInstantButtons extends InstalledAppDetails { 340 @Mock 341 private LayoutPreference mInstantButtons; 342 343 public InstalledAppDetailsWithMockInstantButtons() { 344 super(); 345 MockitoAnnotations.initMocks(this); 346 } 347 348 @Override 349 public Preference findPreference(CharSequence key) { 350 if (key == "instant_app_buttons") { 351 return mInstantButtons; 352 } 353 return super.findPreference(key); 354 } 355 } 356 357 @Test 358 public void instantApps_instantSpecificButtons() { 359 // Make this app appear to be instant. 360 ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", 361 (InstantAppDataProvider) (i -> true)); 362 final PackageInfo packageInfo = mock(PackageInfo.class); 363 364 final InstalledAppDetailsWithMockInstantButtons 365 fragment = new InstalledAppDetailsWithMockInstantButtons(); 366 ReflectionHelpers.setField(fragment, "mPackageInfo", packageInfo); 367 ReflectionHelpers.setField(fragment, "mApplicationFeatureProvider", 368 mFeatureFactory.applicationFeatureProvider); 369 370 final InstantAppButtonsController buttonsController = 371 mock(InstantAppButtonsController.class); 372 when(buttonsController.setPackageName(nullable(String.class))) 373 .thenReturn(buttonsController); 374 when(mFeatureFactory.applicationFeatureProvider.newInstantAppButtonsController( 375 nullable(Fragment.class), nullable(View.class), nullable(ShowDialogDelegate.class))) 376 .thenReturn(buttonsController); 377 378 fragment.maybeAddInstantAppButtons(); 379 verify(buttonsController).setPackageName(nullable(String.class)); 380 verify(buttonsController).show(); 381 } 382 383 @Test 384 public void instantApps_removeCorrectPref() { 385 PreferenceScreen mockPreferenceScreen = mock(PreferenceScreen.class); 386 PreferenceManager mockPreferenceManager = mock(PreferenceManager.class); 387 AppDomainsPreference mockAppDomainsPref = mock(AppDomainsPreference.class); 388 Preference mockLaunchPreference = mock(Preference.class); 389 PackageInfo mockPackageInfo = mock(PackageInfo.class); 390 PackageManager mockPackageManager = mock(PackageManager.class); 391 ReflectionHelpers.setField( 392 mAppDetail, "mLaunchPreference", mockLaunchPreference); 393 ReflectionHelpers.setField( 394 mAppDetail, "mInstantAppDomainsPreference", mockAppDomainsPref); 395 ReflectionHelpers.setField( 396 mAppDetail, "mPreferenceManager", mockPreferenceManager); 397 ReflectionHelpers.setField( 398 mAppDetail, "mPackageInfo", mockPackageInfo); 399 ReflectionHelpers.setField( 400 mAppDetail, "mPm", mockPackageManager); 401 when(mockPreferenceManager.getPreferenceScreen()).thenReturn(mockPreferenceScreen); 402 403 ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", 404 (InstantAppDataProvider) (i -> false)); 405 mAppDetail.prepareInstantAppPrefs(); 406 407 // For the non instant case we remove the app domain pref, and leave the launch pref 408 verify(mockPreferenceScreen).removePreference(mockAppDomainsPref); 409 verify(mockPreferenceScreen, never()).removePreference(mockLaunchPreference); 410 411 // For the instant app case we remove the launch preff, and leave the app domain pref 412 ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", 413 (InstantAppDataProvider) (i -> true)); 414 415 mAppDetail.prepareInstantAppPrefs(); 416 verify(mockPreferenceScreen).removePreference(mockLaunchPreference); 417 // Will be 1 still due to above call 418 verify(mockPreferenceScreen, times(1)) 419 .removePreference(mockAppDomainsPref); 420 } 421 422 @Test 423 public void onActivityResult_uninstalledUpdates_shouldInvalidateOptionsMenu() { 424 doReturn(true).when(mAppDetail).refreshUi(); 425 426 mAppDetail.onActivityResult(InstalledAppDetails.REQUEST_UNINSTALL, 0, mock(Intent.class)); 427 428 verify(mActivity).invalidateOptionsMenu(); 429 } 430 431 @Test 432 public void findTargetSipper_findCorrectSipper() { 433 List<BatterySipper> usageList = new ArrayList<>(); 434 usageList.add(mBatterySipper); 435 usageList.add(mOtherBatterySipper); 436 doReturn(usageList).when(mBatteryStatsHelper).getUsageList(); 437 438 assertThat(mAppDetail.findTargetSipper(mBatteryStatsHelper, TARGET_UID)).isEqualTo( 439 mBatterySipper); 440 } 441 442 @Test 443 public void updateBattery_noBatteryStats_summaryNo() { 444 doReturn(mShadowContext.getString(R.string.no_battery_summary)).when(mAppDetail).getString( 445 R.string.no_battery_summary); 446 mAppDetail.updateBattery(); 447 448 assertThat(mBatteryPreference.getSummary()).isEqualTo( 449 "No battery use since last full charge"); 450 } 451 452 @Test 453 public void updateBattery_hasBatteryStats_summaryPercent() { 454 mAppDetail.mBatteryHelper = mBatteryStatsHelper; 455 mAppDetail.mSipper = mBatterySipper; 456 doReturn(BATTERY_LEVEL).when(mBatteryUtils).calculateBatteryPercent(anyDouble(), 457 anyDouble(), anyDouble(), anyInt()); 458 doReturn(mShadowContext.getString(R.string.battery_summary, BATTERY_LEVEL_STRING)).when( 459 mAppDetail).getString(R.string.battery_summary, BATTERY_LEVEL_STRING); 460 doReturn(new ArrayList<>()).when(mBatteryStatsHelper).getUsageList(); 461 462 mAppDetail.updateBattery(); 463 464 assertThat(mBatteryPreference.getSummary()).isEqualTo("60% use since last full charge"); 465 } 466 467 @Test 468 public void isBatteryStatsAvailable_hasBatteryStatsHelperAndSipper_returnTrue() { 469 mAppDetail.mBatteryHelper = mBatteryStatsHelper; 470 mAppDetail.mSipper = mBatterySipper; 471 472 assertThat(mAppDetail.isBatteryStatsAvailable()).isTrue(); 473 } 474 475 @Test 476 public void isBatteryStatsAvailable_parametersNull_returnFalse() { 477 assertThat(mAppDetail.isBatteryStatsAvailable()).isFalse(); 478 } 479 480 @Test 481 public void handleDisableable_appIsHomeApp_buttonShouldNotWork() { 482 final ApplicationInfo info = new ApplicationInfo(); 483 info.packageName = "pkg"; 484 info.enabled = true; 485 final AppEntry appEntry = mock(AppEntry.class); 486 appEntry.info = info; 487 final HashSet<String> homePackages = new HashSet<>(); 488 homePackages.add(info.packageName); 489 490 ReflectionHelpers.setField(mAppDetail, "mHomePackages", homePackages); 491 ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry); 492 final Button button = mock(Button.class); 493 494 assertThat(mAppDetail.handleDisableable(button)).isFalse(); 495 verify(button).setText(R.string.disable_text); 496 } 497 498 @Test 499 public void handleDisableable_appIsEnabled_buttonShouldWork() { 500 final ApplicationInfo info = new ApplicationInfo(); 501 info.packageName = "pkg"; 502 info.enabled = true; 503 info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED; 504 505 final AppEntry appEntry = mock(AppEntry.class); 506 appEntry.info = info; 507 when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages()).thenReturn( 508 new HashSet<>()); 509 510 ReflectionHelpers.setField(mAppDetail, "mApplicationFeatureProvider", 511 mFeatureFactory.applicationFeatureProvider); 512 ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry); 513 final Button button = mock(Button.class); 514 515 assertThat(mAppDetail.handleDisableable(button)).isTrue(); 516 verify(button).setText(R.string.disable_text); 517 } 518 519 @Test 520 public void handleDisableable_appIsEnabledAndInKeepEnabledWhitelist_buttonShouldNotWork() { 521 final ApplicationInfo info = new ApplicationInfo(); 522 info.packageName = "pkg"; 523 info.enabled = true; 524 info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED; 525 526 final AppEntry appEntry = mock(AppEntry.class); 527 appEntry.info = info; 528 529 final HashSet<String> packages = new HashSet<>(); 530 packages.add(info.packageName); 531 when(mFeatureFactory.applicationFeatureProvider.getKeepEnabledPackages()).thenReturn( 532 packages); 533 534 ReflectionHelpers.setField(mAppDetail, "mApplicationFeatureProvider", 535 mFeatureFactory.applicationFeatureProvider); 536 ReflectionHelpers.setField(mAppDetail, "mAppEntry", appEntry); 537 538 final Button button = mock(Button.class); 539 540 assertThat(mAppDetail.handleDisableable(button)).isFalse(); 541 verify(button).setText(R.string.disable_text); 542 } 543 544 @Test 545 public void testRestartBatteryStatsLoader() { 546 doReturn(mLoaderManager).when(mAppDetail).getLoaderManager(); 547 548 mAppDetail.restartBatteryStatsLoader(); 549 550 verify(mLoaderManager).restartLoader(InstalledAppDetails.LOADER_BATTERY, Bundle.EMPTY, 551 mAppDetail.mBatteryCallbacks); 552 } 553 554 @Implements(Utils.class) 555 public static class ShadowUtils { 556 @Implementation 557 public static boolean isSystemPackage(Resources resources, PackageManager pm, 558 PackageInfo pkg) { 559 return false; 560 } 561 } 562 } 563