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