1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations 14 * under the License. 15 */ 16 17 package com.android.settings.applications.appinfo; 18 19 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 20 21 import android.app.Activity; 22 import android.app.admin.DevicePolicyManager; 23 import android.app.settings.SettingsEnums; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.content.pm.ApplicationInfo; 29 import android.content.pm.PackageInfo; 30 import android.content.pm.PackageManager; 31 import android.content.pm.PackageManager.NameNotFoundException; 32 import android.content.pm.UserInfo; 33 import android.net.Uri; 34 import android.os.Bundle; 35 import android.os.UserHandle; 36 import android.os.UserManager; 37 import android.text.TextUtils; 38 import android.util.Log; 39 import android.view.Menu; 40 import android.view.MenuInflater; 41 import android.view.MenuItem; 42 43 import androidx.annotation.VisibleForTesting; 44 45 import com.android.settings.R; 46 import com.android.settings.SettingsActivity; 47 import com.android.settings.SettingsPreferenceFragment; 48 import com.android.settings.applications.manageapplications.ManageApplications; 49 import com.android.settings.applications.specialaccess.interactacrossprofiles.InteractAcrossProfilesDetailsPreferenceController; 50 import com.android.settings.applications.specialaccess.pictureinpicture.PictureInPictureDetailPreferenceController; 51 import com.android.settings.core.SubSettingLauncher; 52 import com.android.settings.dashboard.DashboardFragment; 53 import com.android.settingslib.RestrictedLockUtilsInternal; 54 import com.android.settingslib.applications.AppUtils; 55 import com.android.settingslib.applications.ApplicationsState; 56 import com.android.settingslib.applications.ApplicationsState.AppEntry; 57 import com.android.settingslib.core.AbstractPreferenceController; 58 import com.android.settingslib.core.lifecycle.Lifecycle; 59 60 import java.util.ArrayList; 61 import java.util.Arrays; 62 import java.util.List; 63 64 /** 65 * Dashboard fragment to display application information from Settings. This activity presents 66 * extended information associated with a package like code, data, total size, permissions 67 * used by the application and also the set of default launchable activities. 68 * For system applications, an option to clear user data is displayed only if data size is > 0. 69 * System applications that do not want clear user data do not have this option. 70 * For non-system applications, there is no option to clear data. Instead there is an option to 71 * uninstall the application. 72 */ 73 public class AppInfoDashboardFragment extends DashboardFragment 74 implements ApplicationsState.Callbacks, 75 ButtonActionDialogFragment.AppButtonsDialogListener { 76 77 private static final String TAG = "AppInfoDashboard"; 78 79 // Menu identifiers 80 @VisibleForTesting 81 static final int UNINSTALL_ALL_USERS_MENU = 1; 82 @VisibleForTesting 83 static final int UNINSTALL_UPDATES = 2; 84 static final int INSTALL_INSTANT_APP_MENU = 3; 85 86 // Result code identifiers 87 @VisibleForTesting 88 static final int REQUEST_UNINSTALL = 0; 89 private static final int REQUEST_REMOVE_DEVICE_ADMIN = 5; 90 91 static final int SUB_INFO_FRAGMENT = 1; 92 93 static final int LOADER_CHART_DATA = 2; 94 static final int LOADER_STORAGE = 3; 95 static final int LOADER_BATTERY = 4; 96 97 public static final String ARG_PACKAGE_NAME = "package"; 98 public static final String ARG_PACKAGE_UID = "uid"; 99 100 private static final boolean localLOGV = false; 101 102 private EnforcedAdmin mAppsControlDisallowedAdmin; 103 private boolean mAppsControlDisallowedBySystem; 104 105 private ApplicationsState mState; 106 private ApplicationsState.Session mSession; 107 private ApplicationsState.AppEntry mAppEntry; 108 private PackageInfo mPackageInfo; 109 private int mUserId; 110 private String mPackageName; 111 112 private DevicePolicyManager mDpm; 113 private UserManager mUserManager; 114 private PackageManager mPm; 115 116 @VisibleForTesting 117 boolean mFinishing; 118 private boolean mListeningToPackageRemove; 119 120 121 private boolean mInitialized; 122 private boolean mShowUninstalled; 123 private boolean mUpdatedSysApp = false; 124 125 private List<Callback> mCallbacks = new ArrayList<>(); 126 127 private InstantAppButtonsPreferenceController mInstantAppButtonPreferenceController; 128 private AppButtonsPreferenceController mAppButtonsPreferenceController; 129 130 /** 131 * Callback to invoke when app info has been changed. 132 */ 133 public interface Callback { refreshUi()134 void refreshUi(); 135 } 136 137 @Override onAttach(Context context)138 public void onAttach(Context context) { 139 super.onAttach(context); 140 final String packageName = getPackageName(); 141 final TimeSpentInAppPreferenceController timeSpentInAppPreferenceController = use( 142 TimeSpentInAppPreferenceController.class); 143 timeSpentInAppPreferenceController.setPackageName(packageName); 144 timeSpentInAppPreferenceController.initLifeCycleOwner(this); 145 146 use(AppDataUsagePreferenceController.class).setParentFragment(this); 147 final AppInstallerInfoPreferenceController installer = 148 use(AppInstallerInfoPreferenceController.class); 149 installer.setPackageName(packageName); 150 installer.setParentFragment(this); 151 use(AppInstallerPreferenceCategoryController.class).setChildren(Arrays.asList(installer)); 152 use(AppNotificationPreferenceController.class).setParentFragment(this); 153 154 use(AppOpenByDefaultPreferenceController.class) 155 .setPackageName(packageName) 156 .setParentFragment(this); 157 158 use(AppPermissionPreferenceController.class).setParentFragment(this); 159 use(AppPermissionPreferenceController.class).setPackageName(packageName); 160 use(AppSettingPreferenceController.class) 161 .setPackageName(packageName) 162 .setParentFragment(this); 163 use(AppStoragePreferenceController.class).setParentFragment(this); 164 use(AppVersionPreferenceController.class).setParentFragment(this); 165 use(InstantAppDomainsPreferenceController.class).setParentFragment(this); 166 167 final WriteSystemSettingsPreferenceController writeSystemSettings = 168 use(WriteSystemSettingsPreferenceController.class); 169 writeSystemSettings.setParentFragment(this); 170 171 final DrawOverlayDetailPreferenceController drawOverlay = 172 use(DrawOverlayDetailPreferenceController.class); 173 drawOverlay.setParentFragment(this); 174 175 final PictureInPictureDetailPreferenceController pip = 176 use(PictureInPictureDetailPreferenceController.class); 177 pip.setPackageName(packageName); 178 pip.setParentFragment(this); 179 180 final ExternalSourceDetailPreferenceController externalSource = 181 use(ExternalSourceDetailPreferenceController.class); 182 externalSource.setPackageName(packageName); 183 externalSource.setParentFragment(this); 184 185 final InteractAcrossProfilesDetailsPreferenceController acrossProfiles = 186 use(InteractAcrossProfilesDetailsPreferenceController.class); 187 acrossProfiles.setPackageName(packageName); 188 acrossProfiles.setParentFragment(this); 189 190 use(AdvancedAppInfoPreferenceCategoryController.class).setChildren(Arrays.asList( 191 writeSystemSettings, drawOverlay, pip, externalSource, acrossProfiles)); 192 } 193 194 @Override onCreate(Bundle icicle)195 public void onCreate(Bundle icicle) { 196 super.onCreate(icicle); 197 mFinishing = false; 198 final Activity activity = getActivity(); 199 mDpm = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE); 200 mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE); 201 mPm = activity.getPackageManager(); 202 if (!ensurePackageInfoAvailable(activity)) { 203 return; 204 } 205 if (!ensureDisplayableModule(activity)) { 206 return; 207 } 208 startListeningToPackageRemove(); 209 210 setHasOptionsMenu(true); 211 } 212 213 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)214 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 215 if (!ensurePackageInfoAvailable(getActivity())) { 216 return; 217 } 218 super.onCreatePreferences(savedInstanceState, rootKey); 219 } 220 221 @Override onDestroy()222 public void onDestroy() { 223 stopListeningToPackageRemove(); 224 super.onDestroy(); 225 } 226 227 @Override getMetricsCategory()228 public int getMetricsCategory() { 229 return SettingsEnums.APPLICATIONS_INSTALLED_APP_DETAILS; 230 } 231 232 @Override onResume()233 public void onResume() { 234 super.onResume(); 235 final Activity activity = getActivity(); 236 mAppsControlDisallowedAdmin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced( 237 activity, UserManager.DISALLOW_APPS_CONTROL, mUserId); 238 mAppsControlDisallowedBySystem = RestrictedLockUtilsInternal.hasBaseUserRestriction( 239 activity, UserManager.DISALLOW_APPS_CONTROL, mUserId); 240 241 if (!refreshUi()) { 242 setIntentAndFinish(true, true); 243 } 244 } 245 246 @Override getPreferenceScreenResId()247 protected int getPreferenceScreenResId() { 248 return R.xml.app_info_settings; 249 } 250 251 @Override getLogTag()252 protected String getLogTag() { 253 return TAG; 254 } 255 256 @Override createPreferenceControllers(Context context)257 protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { 258 retrieveAppEntry(); 259 if (mPackageInfo == null) { 260 return null; 261 } 262 final String packageName = getPackageName(); 263 final List<AbstractPreferenceController> controllers = new ArrayList<>(); 264 final Lifecycle lifecycle = getSettingsLifecycle(); 265 266 // The following are controllers for preferences that needs to refresh the preference state 267 // when app state changes. 268 controllers.add( 269 new AppHeaderViewPreferenceController(context, this, packageName, lifecycle)); 270 271 for (AbstractPreferenceController controller : controllers) { 272 mCallbacks.add((Callback) controller); 273 } 274 275 // The following are controllers for preferences that don't need to refresh the preference 276 // state when app state changes. 277 mInstantAppButtonPreferenceController = 278 new InstantAppButtonsPreferenceController(context, this, packageName, lifecycle); 279 controllers.add(mInstantAppButtonPreferenceController); 280 mAppButtonsPreferenceController = new AppButtonsPreferenceController( 281 (SettingsActivity) getActivity(), this, lifecycle, packageName, mState, 282 REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN); 283 controllers.add(mAppButtonsPreferenceController); 284 controllers.add(new AppBatteryPreferenceController(context, this, packageName, lifecycle)); 285 controllers.add(new AppMemoryPreferenceController(context, this, lifecycle)); 286 controllers.add(new DefaultHomeShortcutPreferenceController(context, packageName)); 287 controllers.add(new DefaultBrowserShortcutPreferenceController(context, packageName)); 288 controllers.add(new DefaultPhoneShortcutPreferenceController(context, packageName)); 289 controllers.add(new DefaultEmergencyShortcutPreferenceController(context, packageName)); 290 controllers.add(new DefaultSmsShortcutPreferenceController(context, packageName)); 291 292 return controllers; 293 } 294 295 @Override isParalleledControllers()296 protected boolean isParalleledControllers() { 297 return true; 298 } 299 addToCallbackList(Callback callback)300 void addToCallbackList(Callback callback) { 301 if (callback != null) { 302 mCallbacks.add(callback); 303 } 304 } 305 getAppEntry()306 ApplicationsState.AppEntry getAppEntry() { 307 return mAppEntry; 308 } 309 setAppEntry(ApplicationsState.AppEntry appEntry)310 void setAppEntry(ApplicationsState.AppEntry appEntry) { 311 mAppEntry = appEntry; 312 } 313 getPackageInfo()314 public PackageInfo getPackageInfo() { 315 return mPackageInfo; 316 } 317 318 @Override onPackageSizeChanged(String packageName)319 public void onPackageSizeChanged(String packageName) { 320 if (!TextUtils.equals(packageName, mPackageName)) { 321 Log.d(TAG, "Package change irrelevant, skipping"); 322 return; 323 } 324 refreshUi(); 325 } 326 327 /** 328 * Ensures the {@link PackageInfo} is available to proceed. If it's not available, the fragment 329 * will finish. 330 * 331 * @return true if packageInfo is available. 332 */ 333 @VisibleForTesting ensurePackageInfoAvailable(Activity activity)334 boolean ensurePackageInfoAvailable(Activity activity) { 335 if (mPackageInfo == null) { 336 mFinishing = true; 337 Log.w(TAG, "Package info not available. Is this package already uninstalled?"); 338 activity.finishAndRemoveTask(); 339 return false; 340 } 341 return true; 342 } 343 344 /** 345 * Ensures the package is displayable as directed by {@link AppUtils#isHiddenSystemModule}. 346 * If it's not, the fragment will finish. 347 * 348 * @return true if package is displayable. 349 */ 350 @VisibleForTesting ensureDisplayableModule(Activity activity)351 boolean ensureDisplayableModule(Activity activity) { 352 if (AppUtils.isHiddenSystemModule(activity.getApplicationContext(), mPackageName)) { 353 mFinishing = true; 354 Log.w(TAG, "Package is hidden module, exiting: " + mPackageName); 355 activity.finishAndRemoveTask(); 356 return false; 357 } 358 return true; 359 } 360 361 @Override onCreateOptionsMenu(Menu menu, MenuInflater inflater)362 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 363 super.onCreateOptionsMenu(menu, inflater); 364 menu.add(0, UNINSTALL_UPDATES, 0, R.string.app_factory_reset) 365 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 366 menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text) 367 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 368 } 369 370 @Override onPrepareOptionsMenu(Menu menu)371 public void onPrepareOptionsMenu(Menu menu) { 372 if (mFinishing) { 373 return; 374 } 375 super.onPrepareOptionsMenu(menu); 376 menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(shouldShowUninstallForAll(mAppEntry)); 377 mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; 378 final MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES); 379 final boolean uninstallUpdateDisabled = getContext().getResources().getBoolean( 380 R.bool.config_disable_uninstall_update); 381 uninstallUpdatesItem.setVisible(mUserManager.isAdminUser() 382 && mUpdatedSysApp 383 && !mAppsControlDisallowedBySystem 384 && !uninstallUpdateDisabled); 385 if (uninstallUpdatesItem.isVisible()) { 386 RestrictedLockUtilsInternal.setMenuItemAsDisabledByAdmin(getActivity(), 387 uninstallUpdatesItem, mAppsControlDisallowedAdmin); 388 } 389 } 390 391 @Override onOptionsItemSelected(MenuItem item)392 public boolean onOptionsItemSelected(MenuItem item) { 393 switch (item.getItemId()) { 394 case UNINSTALL_ALL_USERS_MENU: 395 uninstallPkg(mAppEntry.info.packageName, true, false); 396 return true; 397 case UNINSTALL_UPDATES: 398 uninstallPkg(mAppEntry.info.packageName, false, false); 399 return true; 400 } 401 return super.onOptionsItemSelected(item); 402 } 403 404 @Override onActivityResult(int requestCode, int resultCode, Intent data)405 public void onActivityResult(int requestCode, int resultCode, Intent data) { 406 super.onActivityResult(requestCode, resultCode, data); 407 if (requestCode == REQUEST_UNINSTALL) { 408 // Refresh option menu 409 getActivity().invalidateOptionsMenu(); 410 } 411 if (mAppButtonsPreferenceController != null) { 412 mAppButtonsPreferenceController.handleActivityResult(requestCode, resultCode, data); 413 } 414 } 415 416 @Override handleDialogClick(int id)417 public void handleDialogClick(int id) { 418 if (mAppButtonsPreferenceController != null) { 419 mAppButtonsPreferenceController.handleDialogClick(id); 420 } 421 } 422 423 @VisibleForTesting shouldShowUninstallForAll(AppEntry appEntry)424 boolean shouldShowUninstallForAll(AppEntry appEntry) { 425 boolean showIt = true; 426 if (mUpdatedSysApp) { 427 showIt = false; 428 } else if (appEntry == null) { 429 showIt = false; 430 } else if ((appEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 431 showIt = false; 432 } else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 433 showIt = false; 434 } else if (UserHandle.myUserId() != 0) { 435 showIt = false; 436 } else if (mUserManager.getUsers().size() < 2) { 437 showIt = false; 438 } else if (getNumberOfUserWithPackageInstalled(mPackageName) < 2 439 && (appEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) { 440 showIt = false; 441 } else if (AppUtils.isInstant(appEntry.info)) { 442 showIt = false; 443 } 444 return showIt; 445 } 446 447 @VisibleForTesting refreshUi()448 boolean refreshUi() { 449 retrieveAppEntry(); 450 if (mAppEntry == null) { 451 return false; // onCreate must have failed, make sure to exit 452 } 453 454 if (mPackageInfo == null) { 455 return false; // onCreate must have failed, make sure to exit 456 } 457 458 mState.ensureIcon(mAppEntry); 459 460 // Update the preference summaries. 461 for (Callback callback : mCallbacks) { 462 callback.refreshUi(); 463 } 464 if (mAppButtonsPreferenceController.isAvailable()) { 465 mAppButtonsPreferenceController.refreshUi(); 466 } 467 468 if (!mInitialized) { 469 // First time init: are we displaying an uninstalled app? 470 mInitialized = true; 471 mShowUninstalled = (mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0; 472 } else { 473 // All other times: if the app no longer exists then we want 474 // to go away. 475 try { 476 final ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo( 477 mAppEntry.info.packageName, 478 PackageManager.MATCH_DISABLED_COMPONENTS 479 | PackageManager.MATCH_ANY_USER); 480 if (!mShowUninstalled) { 481 // If we did not start out with the app uninstalled, then 482 // it transitioning to the uninstalled state for the current 483 // user means we should go away as well. 484 return (ainfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0; 485 } 486 } catch (NameNotFoundException e) { 487 return false; 488 } 489 } 490 491 return true; 492 } 493 uninstallPkg(String packageName, boolean allUsers, boolean andDisable)494 private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) { 495 stopListeningToPackageRemove(); 496 // Create new intent to launch Uninstaller activity 497 final Uri packageURI = Uri.parse("package:" + packageName); 498 final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI); 499 uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers); 500 mMetricsFeatureProvider.action( 501 getContext(), SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP); 502 startActivityForResult(uninstallIntent, REQUEST_UNINSTALL); 503 } 504 startAppInfoFragment(Class<?> fragment, int title, Bundle args, SettingsPreferenceFragment caller, AppEntry appEntry)505 public static void startAppInfoFragment(Class<?> fragment, int title, Bundle args, 506 SettingsPreferenceFragment caller, AppEntry appEntry) { 507 // start new fragment to display extended information 508 if (args == null) { 509 args = new Bundle(); 510 } 511 args.putString(ARG_PACKAGE_NAME, appEntry.info.packageName); 512 args.putInt(ARG_PACKAGE_UID, appEntry.info.uid); 513 new SubSettingLauncher(caller.getContext()) 514 .setDestination(fragment.getName()) 515 .setArguments(args) 516 .setTitleRes(title) 517 .setResultListener(caller, SUB_INFO_FRAGMENT) 518 .setSourceMetricsCategory(caller.getMetricsCategory()) 519 .launch(); 520 } 521 onPackageRemoved()522 private void onPackageRemoved() { 523 getActivity().finishActivity(SUB_INFO_FRAGMENT); 524 getActivity().finishAndRemoveTask(); 525 } 526 527 @VisibleForTesting getNumberOfUserWithPackageInstalled(String packageName)528 int getNumberOfUserWithPackageInstalled(String packageName) { 529 final List<UserInfo> userInfos = mUserManager.getUsers(true); 530 int count = 0; 531 532 for (final UserInfo userInfo : userInfos) { 533 try { 534 // Use this API to check whether user has this package 535 final ApplicationInfo info = mPm.getApplicationInfoAsUser( 536 packageName, PackageManager.GET_META_DATA, userInfo.id); 537 if ((info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) { 538 count++; 539 } 540 } catch (NameNotFoundException e) { 541 Log.e(TAG, "Package: " + packageName + " not found for user: " + userInfo.id); 542 } 543 } 544 545 return count; 546 } 547 getPackageName()548 private String getPackageName() { 549 if (mPackageName != null) { 550 return mPackageName; 551 } 552 final Bundle args = getArguments(); 553 mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null; 554 if (mPackageName == null) { 555 final Intent intent = (args == null) ? 556 getActivity().getIntent() : (Intent) args.getParcelable("intent"); 557 if (intent != null) { 558 mPackageName = intent.getData().getSchemeSpecificPart(); 559 } 560 } 561 return mPackageName; 562 } 563 564 @VisibleForTesting retrieveAppEntry()565 void retrieveAppEntry() { 566 final Activity activity = getActivity(); 567 if (activity == null || mFinishing) { 568 return; 569 } 570 if (mState == null) { 571 mState = ApplicationsState.getInstance(activity.getApplication()); 572 mSession = mState.newSession(this, getSettingsLifecycle()); 573 } 574 mUserId = UserHandle.myUserId(); 575 mAppEntry = mState.getEntry(getPackageName(), UserHandle.myUserId()); 576 if (mAppEntry != null) { 577 // Get application info again to refresh changed properties of application 578 try { 579 mPackageInfo = activity.getPackageManager().getPackageInfo( 580 mAppEntry.info.packageName, 581 PackageManager.MATCH_DISABLED_COMPONENTS | 582 PackageManager.MATCH_ANY_USER | 583 PackageManager.GET_SIGNATURES | 584 PackageManager.GET_PERMISSIONS); 585 } catch (NameNotFoundException e) { 586 Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e); 587 } 588 } else { 589 Log.w(TAG, "Missing AppEntry; maybe reinstalling?"); 590 mPackageInfo = null; 591 } 592 } 593 setIntentAndFinish(boolean finish, boolean appChanged)594 private void setIntentAndFinish(boolean finish, boolean appChanged) { 595 if (localLOGV) Log.i(TAG, "appChanged=" + appChanged); 596 final Intent intent = new Intent(); 597 intent.putExtra(ManageApplications.APP_CHG, appChanged); 598 final SettingsActivity sa = (SettingsActivity) getActivity(); 599 sa.finishPreferencePanel(Activity.RESULT_OK, intent); 600 mFinishing = true; 601 } 602 603 @Override onRunningStateChanged(boolean running)604 public void onRunningStateChanged(boolean running) { 605 // No op. 606 } 607 608 @Override onRebuildComplete(ArrayList<AppEntry> apps)609 public void onRebuildComplete(ArrayList<AppEntry> apps) { 610 // No op. 611 } 612 613 @Override onPackageIconChanged()614 public void onPackageIconChanged() { 615 // No op. 616 } 617 618 @Override onAllSizesComputed()619 public void onAllSizesComputed() { 620 // No op. 621 } 622 623 @Override onLauncherInfoChanged()624 public void onLauncherInfoChanged() { 625 // No op. 626 } 627 628 @Override onLoadEntriesCompleted()629 public void onLoadEntriesCompleted() { 630 // No op. 631 } 632 633 @Override onPackageListChanged()634 public void onPackageListChanged() { 635 if (!refreshUi()) { 636 setIntentAndFinish(true, true); 637 } 638 } 639 640 @VisibleForTesting startListeningToPackageRemove()641 void startListeningToPackageRemove() { 642 if (mListeningToPackageRemove) { 643 return; 644 } 645 mListeningToPackageRemove = true; 646 final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); 647 filter.addDataScheme("package"); 648 getContext().registerReceiver(mPackageRemovedReceiver, filter); 649 } 650 stopListeningToPackageRemove()651 private void stopListeningToPackageRemove() { 652 if (!mListeningToPackageRemove) { 653 return; 654 } 655 mListeningToPackageRemove = false; 656 getContext().unregisterReceiver(mPackageRemovedReceiver); 657 } 658 659 @VisibleForTesting 660 final BroadcastReceiver mPackageRemovedReceiver = new BroadcastReceiver() { 661 @Override 662 public void onReceive(Context context, Intent intent) { 663 if (mFinishing) { 664 return; 665 } 666 667 final String packageName = intent.getData().getSchemeSpecificPart(); 668 if (mAppEntry == null 669 || mAppEntry.info == null 670 || TextUtils.equals(mAppEntry.info.packageName, packageName)) { 671 onPackageRemoved(); 672 } else if (mAppEntry.info.isResourceOverlay() 673 && TextUtils.equals(mPackageInfo.overlayTarget, packageName)) { 674 refreshUi(); 675 } 676 } 677 }; 678 679 } 680