InstalledAppDetails.java revision 6ba9e15c4af5085a8923ff2c3045061e667ef135
1/** 2 * Copyright (C) 2007 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 17package com.android.settings.applications; 18 19import android.Manifest.permission; 20import android.app.Activity; 21import android.app.ActivityManager; 22import android.app.AlertDialog; 23import android.app.LoaderManager.LoaderCallbacks; 24import android.app.Notification; 25import android.app.admin.DevicePolicyManager; 26import android.content.ActivityNotFoundException; 27import android.content.BroadcastReceiver; 28import android.content.ComponentName; 29import android.content.Context; 30import android.content.DialogInterface; 31import android.content.Intent; 32import android.content.Loader; 33import android.content.pm.ApplicationInfo; 34import android.content.pm.PackageInfo; 35import android.content.pm.PackageManager; 36import android.content.pm.PackageManager.NameNotFoundException; 37import android.content.pm.ResolveInfo; 38import android.content.pm.UserInfo; 39import android.content.res.Resources; 40import android.graphics.drawable.Drawable; 41import android.icu.text.ListFormatter; 42import android.net.INetworkStatsService; 43import android.net.INetworkStatsSession; 44import android.net.NetworkTemplate; 45import android.net.TrafficStats; 46import android.net.Uri; 47import android.os.AsyncTask; 48import android.os.BatteryStats; 49import android.os.Bundle; 50import android.os.RemoteException; 51import android.os.ServiceManager; 52import android.os.UserHandle; 53import android.os.UserManager; 54import android.provider.Settings; 55import android.service.notification.NotificationListenerService.Ranking; 56import android.support.v7.preference.Preference; 57import android.support.v7.preference.Preference.OnPreferenceClickListener; 58import android.support.v7.preference.PreferenceCategory; 59import android.support.v7.preference.PreferenceScreen; 60import android.text.TextUtils; 61import android.text.format.DateUtils; 62import android.text.format.Formatter; 63import android.util.Log; 64import android.view.LayoutInflater; 65import android.view.Menu; 66import android.view.MenuInflater; 67import android.view.MenuItem; 68import android.view.View; 69import android.view.View.OnClickListener; 70import android.view.ViewGroup; 71import android.webkit.IWebViewUpdateService; 72import android.widget.Button; 73import android.widget.ImageView; 74import android.widget.TextView; 75 76import com.android.internal.logging.MetricsProto.MetricsEvent; 77import com.android.internal.os.BatterySipper; 78import com.android.internal.os.BatteryStatsHelper; 79import com.android.internal.widget.LockPatternUtils; 80import com.android.settings.AppHeader; 81import com.android.settings.DeviceAdminAdd; 82import com.android.settings.R; 83import com.android.settings.SettingsActivity; 84import com.android.settings.SettingsPreferenceFragment; 85import com.android.settings.Utils; 86import com.android.settings.applications.PermissionsSummaryHelper.PermissionsResultCallback; 87import com.android.settings.datausage.AppDataUsage; 88import com.android.settings.datausage.DataUsageList; 89import com.android.settings.datausage.DataUsageSummary; 90import com.android.settings.fuelgauge.BatteryEntry; 91import com.android.settings.fuelgauge.PowerUsageDetail; 92import com.android.settings.notification.AppNotificationSettings; 93import com.android.settings.notification.NotificationBackend; 94import com.android.settings.notification.NotificationBackend.AppRow; 95import com.android.settingslib.AppItem; 96import com.android.settingslib.RestrictedLockUtils; 97import com.android.settingslib.applications.AppUtils; 98import com.android.settingslib.applications.ApplicationsState; 99import com.android.settingslib.applications.ApplicationsState.AppEntry; 100import com.android.settingslib.net.ChartData; 101import com.android.settingslib.net.ChartDataLoader; 102 103import java.lang.ref.WeakReference; 104import java.util.ArrayList; 105import java.util.HashSet; 106import java.util.List; 107 108import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 109 110/** 111 * Activity to display application information from Settings. This activity presents 112 * extended information associated with a package like code, data, total size, permissions 113 * used by the application and also the set of default launchable activities. 114 * For system applications, an option to clear user data is displayed only if data size is > 0. 115 * System applications that do not want clear user data do not have this option. 116 * For non-system applications, there is no option to clear data. Instead there is an option to 117 * uninstall the application. 118 */ 119public class InstalledAppDetails extends AppInfoBase 120 implements View.OnClickListener, OnPreferenceClickListener { 121 122 private static final String LOG_TAG = "InstalledAppDetails"; 123 124 // Menu identifiers 125 public static final int UNINSTALL_ALL_USERS_MENU = 1; 126 public static final int UNINSTALL_UPDATES = 2; 127 128 // Result code identifiers 129 public static final int REQUEST_UNINSTALL = 0; 130 private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1; 131 132 private static final int SUB_INFO_FRAGMENT = 1; 133 134 private static final int LOADER_CHART_DATA = 2; 135 136 private static final int DLG_FORCE_STOP = DLG_BASE + 1; 137 private static final int DLG_DISABLE = DLG_BASE + 2; 138 private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 3; 139 140 private static final String KEY_HEADER = "header_view"; 141 private static final String KEY_NOTIFICATION = "notification_settings"; 142 private static final String KEY_STORAGE = "storage_settings"; 143 private static final String KEY_PERMISSION = "permission_settings"; 144 private static final String KEY_DATA = "data_settings"; 145 private static final String KEY_LAUNCH = "preferred_settings"; 146 private static final String KEY_BATTERY = "battery"; 147 private static final String KEY_MEMORY = "memory"; 148 149 private static final String NOTIFICATION_TUNER_SETTING = "show_importance_slider"; 150 151 private final HashSet<String> mHomePackages = new HashSet<String>(); 152 153 private boolean mInitialized; 154 private boolean mShowUninstalled; 155 private LayoutPreference mHeader; 156 private Button mUninstallButton; 157 private boolean mUpdatedSysApp = false; 158 private Button mForceStopButton; 159 private Preference mNotificationPreference; 160 private Preference mStoragePreference; 161 private Preference mPermissionsPreference; 162 private Preference mLaunchPreference; 163 private Preference mDataPreference; 164 private Preference mMemoryPreference; 165 166 private boolean mDisableAfterUninstall; 167 // Used for updating notification preference. 168 private final NotificationBackend mBackend = new NotificationBackend(); 169 170 private ChartData mChartData; 171 private INetworkStatsSession mStatsSession; 172 173 private Preference mBatteryPreference; 174 175 private BatteryStatsHelper mBatteryHelper; 176 private BatterySipper mSipper; 177 178 protected ProcStatsData mStatsManager; 179 protected ProcStatsPackageEntry mStats; 180 181 private boolean handleDisableable(Button button) { 182 boolean disableable = false; 183 // Try to prevent the user from bricking their phone 184 // by not allowing disabling of apps signed with the 185 // system cert and any launcher app in the system. 186 if (mHomePackages.contains(mAppEntry.info.packageName) 187 || Utils.isSystemPackage(getContext().getResources(), mPm, mPackageInfo)) { 188 // Disable button for core system applications. 189 button.setText(R.string.disable_text); 190 } else if (mAppEntry.info.enabled && !isDisabledUntilUsed()) { 191 button.setText(R.string.disable_text); 192 disableable = true; 193 } else { 194 button.setText(R.string.enable_text); 195 disableable = true; 196 } 197 198 return disableable; 199 } 200 201 private boolean isDisabledUntilUsed() { 202 return mAppEntry.info.enabledSetting 203 == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; 204 } 205 206 private void initUninstallButtons() { 207 final boolean isBundled = (mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; 208 boolean enabled = true; 209 if (isBundled) { 210 enabled = handleDisableable(mUninstallButton); 211 } else { 212 if ((mPackageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0 213 && mUserManager.getUsers().size() >= 2) { 214 // When we have multiple users, there is a separate menu 215 // to uninstall for all users. 216 enabled = false; 217 } 218 mUninstallButton.setText(R.string.uninstall_text); 219 } 220 // If this is a device admin, it can't be uninstalled or disabled. 221 // We do this here so the text of the button is still set correctly. 222 if (isBundled && mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 223 enabled = false; 224 } 225 226 // We don't allow uninstalling DO/PO on *any* users, because if it's a system app, 227 // "uninstall" is actually "downgrade to the system version + disable", and "downgrade" 228 // will clear data on all users. 229 if (isProfileOrDeviceOwner(mPackageInfo.packageName)) { 230 enabled = false; 231 } 232 233 // Don't allow uninstalling the device provisioning package. 234 if (Utils.isDeviceProvisioningPackage(getResources(), mAppEntry.info.packageName)) { 235 enabled = false; 236 } 237 238 // If the uninstall intent is already queued, disable the uninstall button 239 if (mDpm.isUninstallInQueue(mPackageName)) { 240 enabled = false; 241 } 242 243 // Home apps need special handling. Bundled ones we don't risk downgrading 244 // because that can interfere with home-key resolution. Furthermore, we 245 // can't allow uninstallation of the only home app, and we don't want to 246 // allow uninstallation of an explicitly preferred one -- the user can go 247 // to Home settings and pick a different one, after which we'll permit 248 // uninstallation of the now-not-default one. 249 if (enabled && mHomePackages.contains(mPackageInfo.packageName)) { 250 if (isBundled) { 251 enabled = false; 252 } else { 253 ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>(); 254 ComponentName currentDefaultHome = mPm.getHomeActivities(homeActivities); 255 if (currentDefaultHome == null) { 256 // No preferred default, so permit uninstall only when 257 // there is more than one candidate 258 enabled = (mHomePackages.size() > 1); 259 } else { 260 // There is an explicit default home app -- forbid uninstall of 261 // that one, but permit it for installed-but-inactive ones. 262 enabled = !mPackageInfo.packageName.equals(currentDefaultHome.getPackageName()); 263 } 264 } 265 } 266 267 if (mAppsControlDisallowedBySystem) { 268 enabled = false; 269 } 270 271 try { 272 IWebViewUpdateService webviewUpdateService = 273 IWebViewUpdateService.Stub.asInterface(ServiceManager.getService("webviewupdate")); 274 if (webviewUpdateService.isFallbackPackage(mAppEntry.info.packageName)) { 275 enabled = false; 276 } 277 } catch (RemoteException e) { 278 throw new RuntimeException(e); 279 } 280 281 mUninstallButton.setEnabled(enabled); 282 if (enabled) { 283 // Register listener 284 mUninstallButton.setOnClickListener(this); 285 } 286 } 287 288 /** Returns if the supplied package is device owner or profile owner of at least one user */ 289 private boolean isProfileOrDeviceOwner(String packageName) { 290 List<UserInfo> userInfos = mUserManager.getUsers(); 291 DevicePolicyManager dpm = (DevicePolicyManager) 292 getContext().getSystemService(Context.DEVICE_POLICY_SERVICE); 293 if (dpm.isDeviceOwnerAppOnAnyUser(packageName)) { 294 return true; 295 } 296 for (UserInfo userInfo : userInfos) { 297 ComponentName cn = dpm.getProfileOwnerAsUser(userInfo.id); 298 if (cn != null && cn.getPackageName().equals(packageName)) { 299 return true; 300 } 301 } 302 return false; 303 } 304 305 /** Called when the activity is first created. */ 306 @Override 307 public void onCreate(Bundle icicle) { 308 super.onCreate(icicle); 309 310 setHasOptionsMenu(true); 311 addPreferencesFromResource(R.xml.installed_app_details); 312 addDynamicPrefs(); 313 314 if (Utils.isBandwidthControlEnabled()) { 315 INetworkStatsService statsService = INetworkStatsService.Stub.asInterface( 316 ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); 317 try { 318 mStatsSession = statsService.openSession(); 319 } catch (RemoteException e) { 320 throw new RuntimeException(e); 321 } 322 } else { 323 removePreference(KEY_DATA); 324 } 325 mBatteryHelper = new BatteryStatsHelper(getActivity(), true); 326 } 327 328 @Override 329 protected int getMetricsCategory() { 330 return MetricsEvent.APPLICATIONS_INSTALLED_APP_DETAILS; 331 } 332 333 @Override 334 public void onResume() { 335 super.onResume(); 336 if (mFinishing) { 337 return; 338 } 339 mState.requestSize(mPackageName, mUserId); 340 AppItem app = new AppItem(mAppEntry.info.uid); 341 app.addUid(mAppEntry.info.uid); 342 if (mStatsSession != null) { 343 getLoaderManager().restartLoader(LOADER_CHART_DATA, 344 ChartDataLoader.buildArgs(getTemplate(getContext()), app), 345 mDataCallbacks); 346 } 347 new BatteryUpdater().execute(); 348 new MemoryUpdater().execute(); 349 updateDynamicPrefs(); 350 } 351 352 @Override 353 public void onPause() { 354 getLoaderManager().destroyLoader(LOADER_CHART_DATA); 355 super.onPause(); 356 } 357 358 @Override 359 public void onDestroy() { 360 TrafficStats.closeQuietly(mStatsSession); 361 super.onDestroy(); 362 } 363 364 public void onActivityCreated(Bundle savedInstanceState) { 365 super.onActivityCreated(savedInstanceState); 366 if (mFinishing) { 367 return; 368 } 369 handleHeader(); 370 371 mNotificationPreference = findPreference(KEY_NOTIFICATION); 372 mNotificationPreference.setOnPreferenceClickListener(this); 373 mStoragePreference = findPreference(KEY_STORAGE); 374 mStoragePreference.setOnPreferenceClickListener(this); 375 mPermissionsPreference = findPreference(KEY_PERMISSION); 376 mPermissionsPreference.setOnPreferenceClickListener(this); 377 mDataPreference = findPreference(KEY_DATA); 378 if (mDataPreference != null) { 379 mDataPreference.setOnPreferenceClickListener(this); 380 } 381 mBatteryPreference = findPreference(KEY_BATTERY); 382 mBatteryPreference.setEnabled(false); 383 mBatteryPreference.setOnPreferenceClickListener(this); 384 mMemoryPreference = findPreference(KEY_MEMORY); 385 mMemoryPreference.setOnPreferenceClickListener(this); 386 387 mLaunchPreference = findPreference(KEY_LAUNCH); 388 if (mAppEntry != null && mAppEntry.info != null) { 389 if ((mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0 || 390 !mAppEntry.info.enabled) { 391 mLaunchPreference.setEnabled(false); 392 } else { 393 mLaunchPreference.setOnPreferenceClickListener(this); 394 } 395 } else { 396 mLaunchPreference.setEnabled(false); 397 } 398 } 399 400 private void handleHeader() { 401 mHeader = (LayoutPreference) findPreference(KEY_HEADER); 402 403 // Get Control button panel 404 View btnPanel = mHeader.findViewById(R.id.control_buttons_panel); 405 mForceStopButton = (Button) btnPanel.findViewById(R.id.right_button); 406 mForceStopButton.setText(R.string.force_stop); 407 mUninstallButton = (Button) btnPanel.findViewById(R.id.left_button); 408 mForceStopButton.setEnabled(false); 409 410 View gear = mHeader.findViewById(R.id.gear); 411 Intent i = new Intent(Intent.ACTION_APPLICATION_PREFERENCES); 412 i.setPackage(mPackageName); 413 final Intent intent = resolveIntent(i); 414 if (intent != null) { 415 gear.setVisibility(View.VISIBLE); 416 gear.setOnClickListener(new OnClickListener() { 417 @Override 418 public void onClick(View v) { 419 startActivity(intent); 420 } 421 }); 422 } else { 423 gear.setVisibility(View.GONE); 424 } 425 } 426 427 private Intent resolveIntent(Intent i) { 428 ResolveInfo result = getContext().getPackageManager().resolveActivity(i, 0); 429 return result != null ? new Intent(i.getAction()) 430 .setClassName(result.activityInfo.packageName, result.activityInfo.name) : null; 431 } 432 433 @Override 434 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 435 menu.add(0, UNINSTALL_UPDATES, 0, R.string.app_factory_reset) 436 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 437 menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text) 438 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 439 } 440 441 @Override 442 public void onPrepareOptionsMenu(Menu menu) { 443 if (mFinishing) { 444 return; 445 } 446 boolean showIt = true; 447 if (mUpdatedSysApp) { 448 showIt = false; 449 } else if (mAppEntry == null) { 450 showIt = false; 451 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 452 showIt = false; 453 } else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 454 showIt = false; 455 } else if (UserHandle.myUserId() != 0) { 456 showIt = false; 457 } else if (mUserManager.getUsers().size() < 2) { 458 showIt = false; 459 } 460 menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(showIt); 461 mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; 462 MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES); 463 uninstallUpdatesItem.setVisible(mUpdatedSysApp && !mAppsControlDisallowedBySystem); 464 if (uninstallUpdatesItem.isVisible()) { 465 RestrictedLockUtils.setMenuItemAsDisabledByAdmin(getActivity(), 466 uninstallUpdatesItem, mAppsControlDisallowedAdmin); 467 } 468 } 469 470 @Override 471 public boolean onOptionsItemSelected(MenuItem item) { 472 switch (item.getItemId()) { 473 case UNINSTALL_ALL_USERS_MENU: 474 uninstallPkg(mAppEntry.info.packageName, true, false); 475 return true; 476 case UNINSTALL_UPDATES: 477 uninstallPkg(mAppEntry.info.packageName, false, false); 478 return true; 479 } 480 return false; 481 } 482 483 @Override 484 public void onActivityResult(int requestCode, int resultCode, Intent data) { 485 super.onActivityResult(requestCode, resultCode, data); 486 if (requestCode == REQUEST_UNINSTALL) { 487 if (mDisableAfterUninstall) { 488 mDisableAfterUninstall = false; 489 new DisableChanger(this, mAppEntry.info, 490 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) 491 .execute((Object)null); 492 } 493 if (!refreshUi()) { 494 setIntentAndFinish(true, true); 495 } 496 } 497 if (requestCode == REQUEST_REMOVE_DEVICE_ADMIN) { 498 if (!refreshUi()) { 499 setIntentAndFinish(true, true); 500 } 501 } 502 } 503 504 // Utility method to set application label and icon. 505 private void setAppLabelAndIcon(PackageInfo pkgInfo) { 506 final View appSnippet = mHeader.findViewById(R.id.app_snippet); 507 mState.ensureIcon(mAppEntry); 508 setupAppSnippet(appSnippet, mAppEntry.label, mAppEntry.icon, 509 pkgInfo != null ? pkgInfo.versionName : null); 510 } 511 512 private boolean signaturesMatch(String pkg1, String pkg2) { 513 if (pkg1 != null && pkg2 != null) { 514 try { 515 final int match = mPm.checkSignatures(pkg1, pkg2); 516 if (match >= PackageManager.SIGNATURE_MATCH) { 517 return true; 518 } 519 } catch (Exception e) { 520 // e.g. named alternate package not found during lookup; 521 // this is an expected case sometimes 522 } 523 } 524 return false; 525 } 526 527 @Override 528 protected boolean refreshUi() { 529 retrieveAppEntry(); 530 if (mAppEntry == null) { 531 return false; // onCreate must have failed, make sure to exit 532 } 533 534 if (mPackageInfo == null) { 535 return false; // onCreate must have failed, make sure to exit 536 } 537 538 // Get list of "home" apps and trace through any meta-data references 539 List<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>(); 540 mPm.getHomeActivities(homeActivities); 541 mHomePackages.clear(); 542 for (int i = 0; i< homeActivities.size(); i++) { 543 ResolveInfo ri = homeActivities.get(i); 544 final String activityPkg = ri.activityInfo.packageName; 545 mHomePackages.add(activityPkg); 546 547 // Also make sure to include anything proxying for the home app 548 final Bundle metadata = ri.activityInfo.metaData; 549 if (metadata != null) { 550 final String metaPkg = metadata.getString(ActivityManager.META_HOME_ALTERNATE); 551 if (signaturesMatch(metaPkg, activityPkg)) { 552 mHomePackages.add(metaPkg); 553 } 554 } 555 } 556 557 checkForceStop(); 558 setAppLabelAndIcon(mPackageInfo); 559 initUninstallButtons(); 560 561 // Update the preference summaries. 562 Activity context = getActivity(); 563 mStoragePreference.setSummary(AppStorageSettings.getSummary(mAppEntry, context)); 564 565 PermissionsSummaryHelper.getPermissionSummary(getContext(), 566 mPackageName, mPermissionCallback); 567 mLaunchPreference.setSummary(AppUtils.getLaunchByDefaultSummary(mAppEntry, mUsbManager, 568 mPm, context)); 569 mNotificationPreference.setSummary(getNotificationSummary(mAppEntry, context, 570 mBackend)); 571 if (mDataPreference != null) { 572 mDataPreference.setSummary(getDataSummary()); 573 } 574 575 updateBattery(); 576 577 if (!mInitialized) { 578 // First time init: are we displaying an uninstalled app? 579 mInitialized = true; 580 mShowUninstalled = (mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0; 581 } else { 582 // All other times: if the app no longer exists then we want 583 // to go away. 584 try { 585 ApplicationInfo ainfo = context.getPackageManager().getApplicationInfo( 586 mAppEntry.info.packageName, PackageManager.GET_UNINSTALLED_PACKAGES 587 | PackageManager.GET_DISABLED_COMPONENTS); 588 if (!mShowUninstalled) { 589 // If we did not start out with the app uninstalled, then 590 // it transitioning to the uninstalled state for the current 591 // user means we should go away as well. 592 return (ainfo.flags&ApplicationInfo.FLAG_INSTALLED) != 0; 593 } 594 } catch (NameNotFoundException e) { 595 return false; 596 } 597 } 598 599 return true; 600 } 601 602 private void updateBattery() { 603 if (mSipper != null) { 604 mBatteryPreference.setEnabled(true); 605 int dischargeAmount = mBatteryHelper.getStats().getDischargeAmount( 606 BatteryStats.STATS_SINCE_CHARGED); 607 final int percentOfMax = (int) ((mSipper.totalPowerMah) 608 / mBatteryHelper.getTotalPower() * dischargeAmount + .5f); 609 mBatteryPreference.setSummary(getString(R.string.battery_summary, percentOfMax)); 610 } else { 611 mBatteryPreference.setEnabled(false); 612 mBatteryPreference.setSummary(getString(R.string.no_battery_summary)); 613 } 614 } 615 616 private CharSequence getDataSummary() { 617 if (mChartData != null) { 618 long totalBytes = mChartData.detail.getTotalBytes(); 619 if (totalBytes == 0) { 620 return getString(R.string.no_data_usage); 621 } 622 Context context = getActivity(); 623 return getString(R.string.data_summary_format, 624 Formatter.formatFileSize(context, totalBytes), 625 DateUtils.formatDateTime(context, mChartData.detail.getStart(), 626 DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH)); 627 } 628 return getString(R.string.computing_size); 629 } 630 631 @Override 632 protected AlertDialog createDialog(int id, int errorCode) { 633 switch (id) { 634 case DLG_DISABLE: 635 return new AlertDialog.Builder(getActivity()) 636 .setMessage(getActivity().getText(R.string.app_disable_dlg_text)) 637 .setPositiveButton(R.string.app_disable_dlg_positive, 638 new DialogInterface.OnClickListener() { 639 public void onClick(DialogInterface dialog, int which) { 640 // Disable the app 641 new DisableChanger(InstalledAppDetails.this, mAppEntry.info, 642 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) 643 .execute((Object)null); 644 } 645 }) 646 .setNegativeButton(R.string.dlg_cancel, null) 647 .create(); 648 case DLG_SPECIAL_DISABLE: 649 return new AlertDialog.Builder(getActivity()) 650 .setMessage(getActivity().getText(R.string.app_disable_dlg_text)) 651 .setPositiveButton(R.string.app_disable_dlg_positive, 652 new DialogInterface.OnClickListener() { 653 public void onClick(DialogInterface dialog, int which) { 654 // Disable the app and ask for uninstall 655 uninstallPkg(mAppEntry.info.packageName, 656 false, true); 657 } 658 }) 659 .setNegativeButton(R.string.dlg_cancel, null) 660 .create(); 661 case DLG_FORCE_STOP: 662 return new AlertDialog.Builder(getActivity()) 663 .setTitle(getActivity().getText(R.string.force_stop_dlg_title)) 664 .setMessage(getActivity().getText(R.string.force_stop_dlg_text)) 665 .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { 666 public void onClick(DialogInterface dialog, int which) { 667 // Force stop 668 forceStopPackage(mAppEntry.info.packageName); 669 } 670 }) 671 .setNegativeButton(R.string.dlg_cancel, null) 672 .create(); 673 } 674 return null; 675 } 676 677 private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) { 678 // Create new intent to launch Uninstaller activity 679 Uri packageURI = Uri.parse("package:"+packageName); 680 Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI); 681 uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers); 682 startActivityForResult(uninstallIntent, REQUEST_UNINSTALL); 683 mDisableAfterUninstall = andDisable; 684 } 685 686 private void forceStopPackage(String pkgName) { 687 ActivityManager am = (ActivityManager) getActivity().getSystemService( 688 Context.ACTIVITY_SERVICE); 689 am.forceStopPackage(pkgName); 690 int userId = UserHandle.getUserId(mAppEntry.info.uid); 691 mState.invalidatePackage(pkgName, userId); 692 ApplicationsState.AppEntry newEnt = mState.getEntry(pkgName, userId); 693 if (newEnt != null) { 694 mAppEntry = newEnt; 695 } 696 checkForceStop(); 697 } 698 699 private void updateForceStopButton(boolean enabled) { 700 if (mAppsControlDisallowedBySystem) { 701 mForceStopButton.setEnabled(false); 702 } else { 703 mForceStopButton.setEnabled(enabled); 704 mForceStopButton.setOnClickListener(InstalledAppDetails.this); 705 } 706 } 707 708 private void checkForceStop() { 709 if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 710 // User can't force stop device admin. 711 updateForceStopButton(false); 712 } else if ((mAppEntry.info.flags&ApplicationInfo.FLAG_STOPPED) == 0) { 713 // If the app isn't explicitly stopped, then always show the 714 // force stop button. 715 updateForceStopButton(true); 716 } else { 717 Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART, 718 Uri.fromParts("package", mAppEntry.info.packageName, null)); 719 intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mAppEntry.info.packageName }); 720 intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid); 721 intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(mAppEntry.info.uid)); 722 getActivity().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, 723 mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null); 724 } 725 } 726 727 private void startManagePermissionsActivity() { 728 // start new activity to manage app permissions 729 Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS); 730 intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mAppEntry.info.packageName); 731 intent.putExtra(AppHeader.EXTRA_HIDE_INFO_BUTTON, true); 732 try { 733 startActivity(intent); 734 } catch (ActivityNotFoundException e) { 735 Log.w(LOG_TAG, "No app can handle android.intent.action.MANAGE_APP_PERMISSIONS"); 736 } 737 } 738 739 private void startAppInfoFragment(Class<?> fragment, CharSequence title) { 740 startAppInfoFragment(fragment, title, this, mAppEntry); 741 } 742 743 public static void startAppInfoFragment(Class<?> fragment, CharSequence title, 744 SettingsPreferenceFragment caller, AppEntry appEntry) { 745 // start new fragment to display extended information 746 Bundle args = new Bundle(); 747 args.putString(ARG_PACKAGE_NAME, appEntry.info.packageName); 748 args.putInt(ARG_PACKAGE_UID, appEntry.info.uid); 749 args.putBoolean(AppHeader.EXTRA_HIDE_INFO_BUTTON, true); 750 751 SettingsActivity sa = (SettingsActivity) caller.getActivity(); 752 sa.startPreferencePanel(fragment.getName(), args, -1, title, caller, SUB_INFO_FRAGMENT); 753 } 754 755 /* 756 * Method implementing functionality of buttons clicked 757 * @see android.view.View.OnClickListener#onClick(android.view.View) 758 */ 759 public void onClick(View v) { 760 if (mAppEntry == null) { 761 setIntentAndFinish(true, true); 762 return; 763 } 764 String packageName = mAppEntry.info.packageName; 765 if (v == mUninstallButton) { 766 if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 767 Activity activity = getActivity(); 768 Intent uninstallDAIntent = new Intent(activity, DeviceAdminAdd.class); 769 uninstallDAIntent.putExtra(DeviceAdminAdd.EXTRA_DEVICE_ADMIN_PACKAGE_NAME, 770 mPackageName); 771 activity.startActivityForResult(uninstallDAIntent, REQUEST_REMOVE_DEVICE_ADMIN); 772 return; 773 } 774 EnforcedAdmin admin = RestrictedLockUtils.checkIfUninstallBlocked(getActivity(), 775 packageName, mUserId); 776 boolean uninstallBlockedBySystem = mAppsControlDisallowedBySystem || 777 RestrictedLockUtils.hasBaseUserRestriction(getActivity(), packageName, mUserId); 778 if (admin != null && !uninstallBlockedBySystem) { 779 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(), admin); 780 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 781 if (mAppEntry.info.enabled && !isDisabledUntilUsed()) { 782 // If the system app has an update and this is the only user on the device, 783 // then offer to downgrade the app, otherwise only offer to disable the 784 // app for this user. 785 if (mUpdatedSysApp && isSingleUser()) { 786 showDialogInner(DLG_SPECIAL_DISABLE, 0); 787 } else { 788 showDialogInner(DLG_DISABLE, 0); 789 } 790 } else { 791 new DisableChanger(this, mAppEntry.info, 792 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) 793 .execute((Object) null); 794 } 795 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { 796 uninstallPkg(packageName, true, false); 797 } else { 798 uninstallPkg(packageName, false, false); 799 } 800 } else if (v == mForceStopButton) { 801 if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) { 802 RestrictedLockUtils.sendShowAdminSupportDetailsIntent( 803 getActivity(), mAppsControlDisallowedAdmin); 804 } else { 805 showDialogInner(DLG_FORCE_STOP, 0); 806 //forceStopPackage(mAppInfo.packageName); 807 } 808 } 809 } 810 811 /** Returns whether there is only one user on this device, not including the system-only user */ 812 private boolean isSingleUser() { 813 final int userCount = mUserManager.getUserCount(); 814 return userCount == 1 815 || (mUserManager.isSplitSystemUser() && userCount == 2); 816 } 817 818 @Override 819 public boolean onPreferenceClick(Preference preference) { 820 if (preference == mStoragePreference) { 821 startAppInfoFragment(AppStorageSettings.class, mStoragePreference.getTitle()); 822 } else if (preference == mNotificationPreference) { 823 startAppInfoFragment(AppNotificationSettings.class, 824 getString(R.string.app_notifications_title)); 825 } else if (preference == mPermissionsPreference) { 826 startManagePermissionsActivity(); 827 } else if (preference == mLaunchPreference) { 828 startAppInfoFragment(AppLaunchSettings.class, mLaunchPreference.getTitle()); 829 } else if (preference == mMemoryPreference) { 830 ProcessStatsBase.launchMemoryDetail((SettingsActivity) getActivity(), 831 mStatsManager.getMemInfo(), mStats, false); 832 } else if (preference == mDataPreference) { 833 startAppInfoFragment(AppDataUsage.class, getString(R.string.app_data_usage)); 834 } else if (preference == mBatteryPreference) { 835 BatteryEntry entry = new BatteryEntry(getActivity(), null, mUserManager, mSipper); 836 PowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(), 837 mBatteryHelper, BatteryStats.STATS_SINCE_CHARGED, entry, true, false); 838 } else { 839 return false; 840 } 841 return true; 842 } 843 844 private void addDynamicPrefs() { 845 if (Utils.isManagedProfile(UserManager.get(getContext()))) { 846 return; 847 } 848 final PreferenceScreen screen = getPreferenceScreen(); 849 if (DefaultHomePreference.hasHomePreference(mPackageName, getContext())) { 850 screen.addPreference(new ShortcutPreference(getPrefContext(), 851 AdvancedAppSettings.class, "default_home", R.string.home_app, 852 R.string.configure_apps)); 853 } 854 if (DefaultBrowserPreference.hasBrowserPreference(mPackageName, getContext())) { 855 screen.addPreference(new ShortcutPreference(getPrefContext(), 856 AdvancedAppSettings.class, "default_browser", R.string.default_browser_title, 857 R.string.configure_apps)); 858 } 859 if (DefaultPhonePreference.hasPhonePreference(mPackageName, getContext())) { 860 screen.addPreference(new ShortcutPreference(getPrefContext(), 861 AdvancedAppSettings.class, "default_phone_app", R.string.default_phone_title, 862 R.string.configure_apps)); 863 } 864 if (DefaultEmergencyPreference.hasEmergencyPreference(mPackageName, getContext())) { 865 screen.addPreference(new ShortcutPreference(getPrefContext(), 866 AdvancedAppSettings.class, "default_emergency_app", 867 R.string.default_emergency_app, R.string.configure_apps)); 868 } 869 if (DefaultSmsPreference.hasSmsPreference(mPackageName, getContext())) { 870 screen.addPreference(new ShortcutPreference(getPrefContext(), 871 AdvancedAppSettings.class, "default_sms_app", R.string.sms_application_title, 872 R.string.configure_apps)); 873 } 874 boolean hasDrawOverOtherApps = hasPermission(permission.SYSTEM_ALERT_WINDOW); 875 boolean hasWriteSettings = hasPermission(permission.WRITE_SETTINGS); 876 if (hasDrawOverOtherApps || hasWriteSettings) { 877 PreferenceCategory category = new PreferenceCategory(getPrefContext()); 878 category.setTitle(R.string.advanced_apps); 879 screen.addPreference(category); 880 881 if (hasDrawOverOtherApps) { 882 Preference pref = new Preference(getPrefContext()); 883 pref.setTitle(R.string.draw_overlay); 884 pref.setKey("system_alert_window"); 885 pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { 886 @Override 887 public boolean onPreferenceClick(Preference preference) { 888 startAppInfoFragment(DrawOverlayDetails.class, 889 getString(R.string.draw_overlay)); 890 return true; 891 } 892 }); 893 category.addPreference(pref); 894 } 895 if (hasWriteSettings) { 896 Preference pref = new Preference(getPrefContext()); 897 pref.setTitle(R.string.write_settings); 898 pref.setKey("write_settings_apps"); 899 pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { 900 @Override 901 public boolean onPreferenceClick(Preference preference) { 902 startAppInfoFragment(WriteSettingsDetails.class, 903 getString(R.string.write_settings)); 904 return true; 905 } 906 }); 907 category.addPreference(pref); 908 } 909 } 910 911 addAppInstallerInfoPref(screen); 912 } 913 914 private void addAppInstallerInfoPref(PreferenceScreen screen) { 915 String installerPackageName = null; 916 try { 917 installerPackageName = 918 getContext().getPackageManager().getInstallerPackageName(mPackageName); 919 } catch (IllegalArgumentException e) { 920 Log.e(TAG, "Exception while retrieving the package installer of " + mPackageName, e); 921 } 922 if (installerPackageName == null) { 923 return; 924 } 925 final CharSequence installerLabel = Utils.getApplicationLabel(getContext(), 926 installerPackageName); 927 if (installerLabel == null) { 928 return; 929 } 930 PreferenceCategory category = new PreferenceCategory(getPrefContext()); 931 category.setTitle(R.string.app_install_details_group_title); 932 screen.addPreference(category); 933 Preference pref = new Preference(getPrefContext()); 934 pref.setTitle(R.string.app_install_details_title); 935 pref.setKey("app_info_store"); 936 pref.setSummary(getString(R.string.app_install_details_summary, installerLabel)); 937 final Intent intent = new Intent(Intent.ACTION_SHOW_APP_INFO) 938 .setPackage(installerPackageName); 939 final Intent result = resolveIntent(intent); 940 if (result != null) { 941 result.putExtra(Intent.EXTRA_PACKAGE_NAME, mPackageName); 942 pref.setIntent(result); 943 } else { 944 pref.setEnabled(false); 945 } 946 category.addPreference(pref); 947 } 948 949 private boolean hasPermission(String permission) { 950 if (mPackageInfo == null || mPackageInfo.requestedPermissions == null) { 951 return false; 952 } 953 for (int i = 0; i < mPackageInfo.requestedPermissions.length; i++) { 954 if (mPackageInfo.requestedPermissions[i].equals(permission)) { 955 return true; 956 } 957 } 958 return false; 959 } 960 961 private void updateDynamicPrefs() { 962 Preference pref = findPreference("default_home"); 963 if (pref != null) { 964 pref.setSummary(DefaultHomePreference.isHomeDefault(mPackageName, getContext()) 965 ? R.string.yes : R.string.no); 966 } 967 pref = findPreference("default_browser"); 968 if (pref != null) { 969 pref.setSummary(DefaultBrowserPreference.isBrowserDefault(mPackageName, getContext()) 970 ? R.string.yes : R.string.no); 971 } 972 pref = findPreference("default_phone_app"); 973 if (pref != null) { 974 pref.setSummary(DefaultPhonePreference.isPhoneDefault(mPackageName, getContext()) 975 ? R.string.yes : R.string.no); 976 } 977 pref = findPreference("default_emergency_app"); 978 if (pref != null) { 979 pref.setSummary(DefaultEmergencyPreference.isEmergencyDefault(mPackageName, 980 getContext()) ? R.string.yes : R.string.no); 981 } 982 pref = findPreference("default_sms_app"); 983 if (pref != null) { 984 pref.setSummary(DefaultSmsPreference.isSmsDefault(mPackageName, getContext()) 985 ? R.string.yes : R.string.no); 986 } 987 pref = findPreference("system_alert_window"); 988 if (pref != null) { 989 pref.setSummary(DrawOverlayDetails.getSummary(getContext(), mAppEntry)); 990 } 991 pref = findPreference("write_settings_apps"); 992 if (pref != null) { 993 pref.setSummary(WriteSettingsDetails.getSummary(getContext(), mAppEntry)); 994 } 995 } 996 997 public static void setupAppSnippet(View appSnippet, CharSequence label, Drawable icon, 998 CharSequence versionName) { 999 LayoutInflater.from(appSnippet.getContext()).inflate(R.layout.widget_text_views, 1000 (ViewGroup) appSnippet.findViewById(android.R.id.widget_frame)); 1001 1002 ImageView iconView = (ImageView) appSnippet.findViewById(android.R.id.icon); 1003 iconView.setImageDrawable(icon); 1004 // Set application name. 1005 TextView labelView = (TextView) appSnippet.findViewById(android.R.id.title); 1006 labelView.setText(label); 1007 // Version number of application 1008 TextView appVersion = (TextView) appSnippet.findViewById(R.id.widget_text1); 1009 1010 if (!TextUtils.isEmpty(versionName)) { 1011 appVersion.setSelected(true); 1012 appVersion.setVisibility(View.VISIBLE); 1013 appVersion.setText(appSnippet.getContext().getString(R.string.version_text, 1014 String.valueOf(versionName))); 1015 } else { 1016 appVersion.setVisibility(View.INVISIBLE); 1017 } 1018 } 1019 1020 public static NetworkTemplate getTemplate(Context context) { 1021 if (DataUsageList.hasReadyMobileRadio(context)) { 1022 return NetworkTemplate.buildTemplateMobileWildcard(); 1023 } 1024 if (DataUsageSummary.hasWifiRadio(context)) { 1025 return NetworkTemplate.buildTemplateWifiWildcard(); 1026 } 1027 return NetworkTemplate.buildTemplateEthernet(); 1028 } 1029 1030 public static CharSequence getNotificationSummary(AppEntry appEntry, Context context) { 1031 return getNotificationSummary(appEntry, context, new NotificationBackend()); 1032 } 1033 1034 public static CharSequence getNotificationSummary(AppEntry appEntry, Context context, 1035 NotificationBackend backend) { 1036 AppRow appRow = backend.loadAppRow(context, context.getPackageManager(), appEntry.info); 1037 return getNotificationSummary(appRow, context); 1038 } 1039 1040 public static CharSequence getNotificationSummary(AppRow appRow, Context context) { 1041 boolean showSlider = Settings.Secure.getInt( 1042 context.getContentResolver(), NOTIFICATION_TUNER_SETTING, 0) == 1; 1043 List<String> summaryAttributes = new ArrayList<>(); 1044 StringBuffer summary = new StringBuffer(); 1045 if (showSlider) { 1046 if (appRow.appImportance != Ranking.IMPORTANCE_UNSPECIFIED) { 1047 summaryAttributes.add(context.getString( 1048 R.string.notification_summary_level, appRow.appImportance)); 1049 } 1050 } else { 1051 if (appRow.banned) { 1052 summaryAttributes.add(context.getString(R.string.notifications_disabled)); 1053 } else if (appRow.appImportance > Ranking.IMPORTANCE_NONE 1054 && appRow.appImportance < Ranking.IMPORTANCE_DEFAULT) { 1055 summaryAttributes.add(context.getString(R.string.notifications_silenced)); 1056 } 1057 } 1058 final boolean lockscreenSecure = new LockPatternUtils(context).isSecure( 1059 UserHandle.myUserId()); 1060 if (lockscreenSecure) { 1061 if (appRow.appVisOverride == Notification.VISIBILITY_PRIVATE) { 1062 summaryAttributes.add(context.getString(R.string.notifications_redacted)); 1063 } else if (appRow.appVisOverride == Notification.VISIBILITY_SECRET) { 1064 summaryAttributes.add(context.getString(R.string.notifications_hidden)); 1065 } 1066 } 1067 if (appRow.appBypassDnd) { 1068 summaryAttributes.add(context.getString(R.string.notifications_priority)); 1069 } 1070 final int N = summaryAttributes.size(); 1071 for (int i = 0; i < N; i++) { 1072 if (i > 0) { 1073 summary.append(context.getString(R.string.notifications_summary_divider)); 1074 } 1075 summary.append(summaryAttributes.get(i)); 1076 } 1077 return summary.toString(); 1078 } 1079 1080 private class MemoryUpdater extends AsyncTask<Void, Void, ProcStatsPackageEntry> { 1081 1082 @Override 1083 protected ProcStatsPackageEntry doInBackground(Void... params) { 1084 if (getActivity() == null) { 1085 return null; 1086 } 1087 if (mPackageInfo == null) { 1088 return null; 1089 } 1090 if (mStatsManager == null) { 1091 mStatsManager = new ProcStatsData(getActivity(), false); 1092 mStatsManager.setDuration(ProcessStatsBase.sDurations[0]); 1093 } 1094 mStatsManager.refreshStats(true); 1095 for (ProcStatsPackageEntry pkgEntry : mStatsManager.getEntries()) { 1096 for (ProcStatsEntry entry : pkgEntry.mEntries) { 1097 if (entry.mUid == mPackageInfo.applicationInfo.uid) { 1098 pkgEntry.updateMetrics(); 1099 return pkgEntry; 1100 } 1101 } 1102 } 1103 return null; 1104 } 1105 1106 @Override 1107 protected void onPostExecute(ProcStatsPackageEntry entry) { 1108 if (getActivity() == null) { 1109 return; 1110 } 1111 if (entry != null) { 1112 mStats = entry; 1113 mMemoryPreference.setEnabled(true); 1114 double amount = Math.max(entry.mRunWeight, entry.mBgWeight) 1115 * mStatsManager.getMemInfo().weightToRam; 1116 mMemoryPreference.setSummary(getString(R.string.memory_use_summary, 1117 Formatter.formatShortFileSize(getContext(), (long) amount))); 1118 } else { 1119 mMemoryPreference.setEnabled(false); 1120 mMemoryPreference.setSummary(getString(R.string.no_memory_use_summary)); 1121 } 1122 } 1123 1124 } 1125 1126 private class BatteryUpdater extends AsyncTask<Void, Void, Void> { 1127 @Override 1128 protected Void doInBackground(Void... params) { 1129 mBatteryHelper.create((Bundle) null); 1130 mBatteryHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, 1131 mUserManager.getUserProfiles()); 1132 List<BatterySipper> usageList = mBatteryHelper.getUsageList(); 1133 final int N = usageList.size(); 1134 for (int i = 0; i < N; i++) { 1135 BatterySipper sipper = usageList.get(i); 1136 if (sipper.getUid() == mPackageInfo.applicationInfo.uid) { 1137 mSipper = sipper; 1138 break; 1139 } 1140 } 1141 return null; 1142 } 1143 1144 @Override 1145 protected void onPostExecute(Void result) { 1146 if (getActivity() == null) { 1147 return; 1148 } 1149 refreshUi(); 1150 } 1151 } 1152 1153 private static class DisableChanger extends AsyncTask<Object, Object, Object> { 1154 final PackageManager mPm; 1155 final WeakReference<InstalledAppDetails> mActivity; 1156 final ApplicationInfo mInfo; 1157 final int mState; 1158 1159 DisableChanger(InstalledAppDetails activity, ApplicationInfo info, int state) { 1160 mPm = activity.mPm; 1161 mActivity = new WeakReference<InstalledAppDetails>(activity); 1162 mInfo = info; 1163 mState = state; 1164 } 1165 1166 @Override 1167 protected Object doInBackground(Object... params) { 1168 mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0); 1169 return null; 1170 } 1171 } 1172 1173 private final LoaderCallbacks<ChartData> mDataCallbacks = new LoaderCallbacks<ChartData>() { 1174 1175 @Override 1176 public Loader<ChartData> onCreateLoader(int id, Bundle args) { 1177 return new ChartDataLoader(getActivity(), mStatsSession, args); 1178 } 1179 1180 @Override 1181 public void onLoadFinished(Loader<ChartData> loader, ChartData data) { 1182 mChartData = data; 1183 mDataPreference.setSummary(getDataSummary()); 1184 } 1185 1186 @Override 1187 public void onLoaderReset(Loader<ChartData> loader) { 1188 // Leave last result. 1189 } 1190 }; 1191 1192 private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() { 1193 @Override 1194 public void onReceive(Context context, Intent intent) { 1195 updateForceStopButton(getResultCode() != Activity.RESULT_CANCELED); 1196 } 1197 }; 1198 1199 private final PermissionsResultCallback mPermissionCallback 1200 = new PermissionsResultCallback() { 1201 @Override 1202 public void onPermissionSummaryResult(int standardGrantedPermissionCount, 1203 int requestedPermissionCount, int additionalGrantedPermissionCount, 1204 List<CharSequence> grantedGroupLabels) { 1205 if (getActivity() == null) { 1206 return; 1207 } 1208 final Resources res = getResources(); 1209 CharSequence summary = null; 1210 1211 if (requestedPermissionCount == 0) { 1212 summary = res.getString( 1213 R.string.runtime_permissions_summary_no_permissions_requested); 1214 mPermissionsPreference.setOnPreferenceClickListener(null); 1215 mPermissionsPreference.setEnabled(false); 1216 } else { 1217 final ArrayList<CharSequence> list = new ArrayList<>(grantedGroupLabels); 1218 if (additionalGrantedPermissionCount > 0) { 1219 // N additional permissions. 1220 list.add(res.getQuantityString( 1221 R.plurals.runtime_permissions_additional_count, 1222 additionalGrantedPermissionCount, additionalGrantedPermissionCount)); 1223 } 1224 if (list.size() == 0) { 1225 summary = res.getString( 1226 R.string.runtime_permissions_summary_no_permissions_granted); 1227 } else { 1228 summary = ListFormatter.getInstance().format(list); 1229 } 1230 mPermissionsPreference.setOnPreferenceClickListener(InstalledAppDetails.this); 1231 mPermissionsPreference.setEnabled(true); 1232 } 1233 mPermissionsPreference.setSummary(summary); 1234 } 1235 }; 1236} 1237