1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.applications; 18 19 import android.app.Activity; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.pm.ApplicationInfo; 23 import android.content.pm.PackageItemInfo; 24 import android.content.pm.PackageManager; 25 import android.content.pm.ResolveInfo; 26 import android.icu.text.AlphabeticIndex; 27 import android.os.Bundle; 28 import android.os.Environment; 29 import android.os.Handler; 30 import android.os.LocaleList; 31 import android.os.UserHandle; 32 import android.os.UserManager; 33 import android.preference.PreferenceFrameLayout; 34 import android.text.TextUtils; 35 import android.util.ArraySet; 36 import android.util.Log; 37 import android.view.LayoutInflater; 38 import android.view.Menu; 39 import android.view.MenuInflater; 40 import android.view.MenuItem; 41 import android.view.View; 42 import android.view.ViewGroup; 43 import android.widget.AbsListView; 44 import android.widget.AdapterView; 45 import android.widget.AdapterView.OnItemClickListener; 46 import android.widget.AdapterView.OnItemSelectedListener; 47 import android.widget.ArrayAdapter; 48 import android.widget.BaseAdapter; 49 import android.widget.Filter; 50 import android.widget.Filterable; 51 import android.widget.FrameLayout; 52 import android.widget.ListView; 53 import android.widget.SectionIndexer; 54 import android.widget.Spinner; 55 import com.android.internal.logging.MetricsProto.MetricsEvent; 56 import com.android.settings.AppHeader; 57 import com.android.settings.InstrumentedFragment; 58 import com.android.settings.R; 59 import com.android.settings.Settings.AllApplicationsActivity; 60 import com.android.settings.Settings.DomainsURLsAppListActivity; 61 import com.android.settings.Settings.HighPowerApplicationsActivity; 62 import com.android.settings.Settings.NotificationAppListActivity; 63 import com.android.settings.Settings.OverlaySettingsActivity; 64 import com.android.settings.Settings.StorageUseActivity; 65 import com.android.settings.Settings.UsageAccessSettingsActivity; 66 import com.android.settings.Settings.WriteSettingsActivity; 67 import com.android.settings.SettingsActivity; 68 import com.android.settings.Utils; 69 import com.android.settings.applications.AppStateAppOpsBridge.PermissionState; 70 import com.android.settings.applications.AppStateUsageBridge.UsageState; 71 import com.android.settings.dashboard.SummaryLoader; 72 import com.android.settings.fuelgauge.HighPowerDetail; 73 import com.android.settings.fuelgauge.PowerWhitelistBackend; 74 import com.android.settings.notification.AppNotificationSettings; 75 import com.android.settings.notification.ConfigureNotificationSettings; 76 import com.android.settings.notification.NotificationBackend; 77 import com.android.settings.notification.NotificationBackend.AppRow; 78 import com.android.settingslib.HelpUtils; 79 import com.android.settingslib.applications.ApplicationsState; 80 import com.android.settingslib.applications.ApplicationsState.AppEntry; 81 import com.android.settingslib.applications.ApplicationsState.AppFilter; 82 import com.android.settingslib.applications.ApplicationsState.CompoundFilter; 83 import com.android.settingslib.applications.ApplicationsState.VolumeFilter; 84 85 import java.util.ArrayList; 86 import java.util.Collections; 87 import java.util.Comparator; 88 import java.util.List; 89 import java.util.Locale; 90 91 /** 92 * Activity to pick an application that will be used to display installation information and 93 * options to uninstall/delete user data for system applications. This activity 94 * can be launched through Settings or via the ACTION_MANAGE_PACKAGE_STORAGE 95 * intent. 96 */ 97 public class ManageApplications extends InstrumentedFragment 98 implements OnItemClickListener, OnItemSelectedListener { 99 100 static final String TAG = "ManageApplications"; 101 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 102 103 // Intent extras. 104 public static final String EXTRA_CLASSNAME = "classname"; 105 // Used for storage only. 106 public static final String EXTRA_VOLUME_UUID = "volumeUuid"; 107 public static final String EXTRA_VOLUME_NAME = "volumeName"; 108 109 private static final String EXTRA_SORT_ORDER = "sortOrder"; 110 private static final String EXTRA_SHOW_SYSTEM = "showSystem"; 111 private static final String EXTRA_HAS_ENTRIES = "hasEntries"; 112 private static final String EXTRA_HAS_BRIDGE = "hasBridge"; 113 114 // attributes used as keys when passing values to InstalledAppDetails activity 115 public static final String APP_CHG = "chg"; 116 117 // constant value that can be used to check return code from sub activity. 118 private static final int INSTALLED_APP_DETAILS = 1; 119 private static final int ADVANCED_SETTINGS = 2; 120 121 public static final int SIZE_TOTAL = 0; 122 public static final int SIZE_INTERNAL = 1; 123 public static final int SIZE_EXTERNAL = 2; 124 125 // Filter options used for displayed list of applications 126 // The order which they appear is the order they will show when spinner is present. 127 public static final int FILTER_APPS_POWER_WHITELIST = 0; 128 public static final int FILTER_APPS_POWER_WHITELIST_ALL = 1; 129 public static final int FILTER_APPS_ALL = 2; 130 public static final int FILTER_APPS_ENABLED = 3; 131 public static final int FILTER_APPS_DISABLED = 4; 132 public static final int FILTER_APPS_BLOCKED = 5; 133 public static final int FILTER_APPS_SILENT = 6; 134 public static final int FILTER_APPS_SENSITIVE = 7; 135 public static final int FILTER_APPS_HIDE_NOTIFICATIONS = 8; 136 public static final int FILTER_APPS_PRIORITY = 9; 137 public static final int FILTER_APPS_PERSONAL = 10; 138 public static final int FILTER_APPS_WORK = 11; 139 public static final int FILTER_APPS_WITH_DOMAIN_URLS = 12; 140 public static final int FILTER_APPS_USAGE_ACCESS = 13; 141 public static final int FILTER_APPS_WITH_OVERLAY = 14; 142 public static final int FILTER_APPS_WRITE_SETTINGS = 15; 143 144 // This is the string labels for the filter modes above, the order must be kept in sync. 145 public static final int[] FILTER_LABELS = new int[]{ 146 R.string.high_power_filter_on, // High power whitelist, on 147 R.string.filter_all_apps, // Without disabled until used 148 R.string.filter_all_apps, // All apps 149 R.string.filter_enabled_apps, // Enabled 150 R.string.filter_apps_disabled, // Disabled 151 R.string.filter_notif_blocked_apps, // Blocked Notifications 152 R.string.filter_notif_silent, // Silenced Notifications 153 R.string.filter_notif_sensitive_apps, // Sensitive Notifications 154 R.string.filter_notif_hide_notifications_apps, // Sensitive Notifications 155 R.string.filter_notif_priority_apps, // Priority Notifications 156 R.string.filter_personal_apps, // Personal 157 R.string.filter_work_apps, // Work 158 R.string.filter_with_domain_urls_apps, // Domain URLs 159 R.string.filter_all_apps, // Usage access screen, never displayed 160 R.string.filter_overlay_apps, // Apps with overlay permission 161 R.string.filter_write_settings_apps, // Apps that can write system settings 162 }; 163 // This is the actual mapping to filters from FILTER_ constants above, the order must 164 // be kept in sync. 165 public static final AppFilter[] FILTERS = new AppFilter[]{ 166 new CompoundFilter(AppStatePowerBridge.FILTER_POWER_WHITELISTED, 167 ApplicationsState.FILTER_ALL_ENABLED), // High power whitelist, on 168 new CompoundFilter(ApplicationsState.FILTER_WITHOUT_DISABLED_UNTIL_USED, 169 ApplicationsState.FILTER_ALL_ENABLED), // Without disabled until used 170 ApplicationsState.FILTER_EVERYTHING, // All apps 171 ApplicationsState.FILTER_ALL_ENABLED, // Enabled 172 ApplicationsState.FILTER_DISABLED, // Disabled 173 AppStateNotificationBridge.FILTER_APP_NOTIFICATION_BLOCKED, // Blocked Notifications 174 AppStateNotificationBridge.FILTER_APP_NOTIFICATION_SILENCED, // Silenced Notifications 175 AppStateNotificationBridge.FILTER_APP_NOTIFICATION_HIDE_SENSITIVE, // Sensitive Notifications 176 AppStateNotificationBridge.FILTER_APP_NOTIFICATION_HIDE_ALL, // Hide all Notifications 177 AppStateNotificationBridge.FILTER_APP_NOTIFICATION_PRIORITY, // Priority Notifications 178 ApplicationsState.FILTER_PERSONAL, // Personal 179 ApplicationsState.FILTER_WORK, // Work 180 ApplicationsState.FILTER_WITH_DOMAIN_URLS, // Apps with Domain URLs 181 AppStateUsageBridge.FILTER_APP_USAGE, // Apps with Domain URLs 182 AppStateOverlayBridge.FILTER_SYSTEM_ALERT_WINDOW, // Apps that can draw overlays 183 AppStateWriteSettingsBridge.FILTER_WRITE_SETTINGS, // Apps that can write system settings 184 }; 185 186 // sort order 187 private int mSortOrder = R.id.sort_order_alpha; 188 189 // whether showing system apps. 190 private boolean mShowSystem; 191 192 private ApplicationsState mApplicationsState; 193 194 public int mListType; 195 public int mFilter; 196 197 public ApplicationsAdapter mApplications; 198 199 private View mLoadingContainer; 200 201 private View mListContainer; 202 203 // ListView used to display list 204 private ListView mListView; 205 206 // Size resource used for packages whose size computation failed for some reason 207 CharSequence mInvalidSizeStr; 208 209 // layout inflater object used to inflate views 210 private LayoutInflater mInflater; 211 212 private String mCurrentPkgName; 213 private int mCurrentUid; 214 private boolean mFinishAfterDialog; 215 216 private Menu mOptionsMenu; 217 218 public static final int LIST_TYPE_MAIN = 0; 219 public static final int LIST_TYPE_NOTIFICATION = 1; 220 public static final int LIST_TYPE_DOMAINS_URLS = 2; 221 public static final int LIST_TYPE_STORAGE = 3; 222 public static final int LIST_TYPE_USAGE_ACCESS = 4; 223 public static final int LIST_TYPE_HIGH_POWER = 5; 224 public static final int LIST_TYPE_OVERLAY = 6; 225 public static final int LIST_TYPE_WRITE_SETTINGS = 7; 226 227 private View mRootView; 228 229 private View mSpinnerHeader; 230 private Spinner mFilterSpinner; 231 private FilterSpinnerAdapter mFilterAdapter; 232 private NotificationBackend mNotifBackend; 233 private ResetAppsHelper mResetAppsHelper; 234 private String mVolumeUuid; 235 private String mVolumeName; 236 237 @Override 238 public void onCreate(Bundle savedInstanceState) { 239 super.onCreate(savedInstanceState); 240 setHasOptionsMenu(true); 241 mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication()); 242 243 Intent intent = getActivity().getIntent(); 244 Bundle args = getArguments(); 245 String className = args != null ? args.getString(EXTRA_CLASSNAME) : null; 246 if (className == null) { 247 className = intent.getComponent().getClassName(); 248 } 249 if (className.equals(AllApplicationsActivity.class.getName())) { 250 mShowSystem = true; 251 } else if (className.equals(NotificationAppListActivity.class.getName())) { 252 mListType = LIST_TYPE_NOTIFICATION; 253 mNotifBackend = new NotificationBackend(); 254 } else if (className.equals(DomainsURLsAppListActivity.class.getName())) { 255 mListType = LIST_TYPE_DOMAINS_URLS; 256 } else if (className.equals(StorageUseActivity.class.getName())) { 257 if (args != null && args.containsKey(EXTRA_VOLUME_UUID)) { 258 mVolumeUuid = args.getString(EXTRA_VOLUME_UUID); 259 mVolumeName = args.getString(EXTRA_VOLUME_NAME); 260 mListType = LIST_TYPE_STORAGE; 261 } else { 262 // No volume selected, display a normal list, sorted by size. 263 mListType = LIST_TYPE_MAIN; 264 } 265 mSortOrder = R.id.sort_order_size; 266 } else if (className.equals(UsageAccessSettingsActivity.class.getName())) { 267 mListType = LIST_TYPE_USAGE_ACCESS; 268 } else if (className.equals(HighPowerApplicationsActivity.class.getName())) { 269 mListType = LIST_TYPE_HIGH_POWER; 270 // Default to showing system. 271 mShowSystem = true; 272 } else if (className.equals(OverlaySettingsActivity.class.getName())) { 273 mListType = LIST_TYPE_OVERLAY; 274 } else if (className.equals(WriteSettingsActivity.class.getName())) { 275 mListType = LIST_TYPE_WRITE_SETTINGS; 276 } else { 277 mListType = LIST_TYPE_MAIN; 278 } 279 mFilter = getDefaultFilter(); 280 281 if (savedInstanceState != null) { 282 mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder); 283 mShowSystem = savedInstanceState.getBoolean(EXTRA_SHOW_SYSTEM, mShowSystem); 284 } 285 286 mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value); 287 288 mResetAppsHelper = new ResetAppsHelper(getActivity()); 289 } 290 291 292 @Override 293 public View onCreateView(LayoutInflater inflater, ViewGroup container, 294 Bundle savedInstanceState) { 295 // initialize the inflater 296 mInflater = inflater; 297 298 mRootView = inflater.inflate(R.layout.manage_applications_apps, null); 299 mLoadingContainer = mRootView.findViewById(R.id.loading_container); 300 mLoadingContainer.setVisibility(View.VISIBLE); 301 mListContainer = mRootView.findViewById(R.id.list_container); 302 if (mListContainer != null) { 303 // Create adapter and list view here 304 View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty); 305 ListView lv = (ListView) mListContainer.findViewById(android.R.id.list); 306 if (emptyView != null) { 307 lv.setEmptyView(emptyView); 308 } 309 lv.setOnItemClickListener(this); 310 lv.setSaveEnabled(true); 311 lv.setItemsCanFocus(true); 312 lv.setTextFilterEnabled(true); 313 mListView = lv; 314 mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter); 315 if (savedInstanceState != null) { 316 mApplications.mHasReceivedLoadEntries = 317 savedInstanceState.getBoolean(EXTRA_HAS_ENTRIES, false); 318 mApplications.mHasReceivedBridgeCallback = 319 savedInstanceState.getBoolean(EXTRA_HAS_BRIDGE, false); 320 } 321 mListView.setAdapter(mApplications); 322 mListView.setRecyclerListener(mApplications); 323 mListView.setFastScrollEnabled(isFastScrollEnabled()); 324 325 Utils.prepareCustomPreferencesList(container, mRootView, mListView, false); 326 } 327 328 // We have to do this now because PreferenceFrameLayout looks at it 329 // only when the view is added. 330 if (container instanceof PreferenceFrameLayout) { 331 ((PreferenceFrameLayout.LayoutParams) mRootView.getLayoutParams()).removeBorders = true; 332 } 333 334 createHeader(); 335 336 mResetAppsHelper.onRestoreInstanceState(savedInstanceState); 337 338 return mRootView; 339 } 340 341 private void createHeader() { 342 Activity activity = getActivity(); 343 FrameLayout pinnedHeader = (FrameLayout) mRootView.findViewById(R.id.pinned_header); 344 mSpinnerHeader = (ViewGroup) activity.getLayoutInflater() 345 .inflate(R.layout.apps_filter_spinner, pinnedHeader, false); 346 mFilterSpinner = (Spinner) mSpinnerHeader.findViewById(R.id.filter_spinner); 347 mFilterAdapter = new FilterSpinnerAdapter(this); 348 mFilterSpinner.setAdapter(mFilterAdapter); 349 mFilterSpinner.setOnItemSelectedListener(this); 350 pinnedHeader.addView(mSpinnerHeader, 0); 351 352 mFilterAdapter.enableFilter(getDefaultFilter()); 353 if (mListType == LIST_TYPE_MAIN) { 354 if (UserManager.get(getActivity()).getUserProfiles().size() > 1) { 355 mFilterAdapter.enableFilter(FILTER_APPS_PERSONAL); 356 mFilterAdapter.enableFilter(FILTER_APPS_WORK); 357 } 358 } 359 if (mListType == LIST_TYPE_NOTIFICATION) { 360 mFilterAdapter.enableFilter(FILTER_APPS_BLOCKED); 361 mFilterAdapter.enableFilter(FILTER_APPS_SILENT); 362 mFilterAdapter.enableFilter(FILTER_APPS_SENSITIVE); 363 mFilterAdapter.enableFilter(FILTER_APPS_HIDE_NOTIFICATIONS); 364 mFilterAdapter.enableFilter(FILTER_APPS_PRIORITY); 365 } 366 if (mListType == LIST_TYPE_HIGH_POWER) { 367 mFilterAdapter.enableFilter(FILTER_APPS_POWER_WHITELIST_ALL); 368 } 369 if (mListType == LIST_TYPE_STORAGE) { 370 mApplications.setOverrideFilter(new VolumeFilter(mVolumeUuid)); 371 } 372 } 373 374 @Override 375 public void onViewCreated(View view, Bundle savedInstanceState) { 376 super.onViewCreated(view, savedInstanceState); 377 if (mListType == LIST_TYPE_STORAGE) { 378 FrameLayout pinnedHeader = (FrameLayout) mRootView.findViewById(R.id.pinned_header); 379 AppHeader.createAppHeader(getActivity(), null, mVolumeName, null, -1, pinnedHeader); 380 } 381 } 382 383 private int getDefaultFilter() { 384 switch (mListType) { 385 case LIST_TYPE_DOMAINS_URLS: 386 return FILTER_APPS_WITH_DOMAIN_URLS; 387 case LIST_TYPE_USAGE_ACCESS: 388 return FILTER_APPS_USAGE_ACCESS; 389 case LIST_TYPE_HIGH_POWER: 390 return FILTER_APPS_POWER_WHITELIST; 391 case LIST_TYPE_OVERLAY: 392 return FILTER_APPS_WITH_OVERLAY; 393 case LIST_TYPE_WRITE_SETTINGS: 394 return FILTER_APPS_WRITE_SETTINGS; 395 default: 396 return FILTER_APPS_ALL; 397 } 398 } 399 400 private boolean isFastScrollEnabled() { 401 switch (mListType) { 402 case LIST_TYPE_MAIN: 403 case LIST_TYPE_NOTIFICATION: 404 case LIST_TYPE_STORAGE: 405 return mSortOrder == R.id.sort_order_alpha; 406 default: 407 return false; 408 } 409 } 410 411 @Override 412 protected int getMetricsCategory() { 413 switch (mListType) { 414 case LIST_TYPE_MAIN: 415 return MetricsEvent.MANAGE_APPLICATIONS; 416 case LIST_TYPE_NOTIFICATION: 417 return MetricsEvent.MANAGE_APPLICATIONS_NOTIFICATIONS; 418 case LIST_TYPE_DOMAINS_URLS: 419 return MetricsEvent.MANAGE_DOMAIN_URLS; 420 case LIST_TYPE_STORAGE: 421 return MetricsEvent.APPLICATIONS_STORAGE_APPS; 422 case LIST_TYPE_USAGE_ACCESS: 423 return MetricsEvent.USAGE_ACCESS; 424 case LIST_TYPE_HIGH_POWER: 425 return MetricsEvent.APPLICATIONS_HIGH_POWER_APPS; 426 case LIST_TYPE_OVERLAY: 427 return MetricsEvent.SYSTEM_ALERT_WINDOW_APPS; 428 case LIST_TYPE_WRITE_SETTINGS: 429 return MetricsEvent.SYSTEM_ALERT_WINDOW_APPS; 430 default: 431 return MetricsEvent.VIEW_UNKNOWN; 432 } 433 } 434 435 @Override 436 public void onResume() { 437 super.onResume(); 438 updateView(); 439 updateOptionsMenu(); 440 if (mApplications != null) { 441 mApplications.resume(mSortOrder); 442 mApplications.updateLoading(); 443 } 444 } 445 446 @Override 447 public void onSaveInstanceState(Bundle outState) { 448 super.onSaveInstanceState(outState); 449 mResetAppsHelper.onSaveInstanceState(outState); 450 outState.putInt(EXTRA_SORT_ORDER, mSortOrder); 451 outState.putBoolean(EXTRA_SHOW_SYSTEM, mShowSystem); 452 outState.putBoolean(EXTRA_HAS_ENTRIES, mApplications.mHasReceivedLoadEntries); 453 outState.putBoolean(EXTRA_HAS_BRIDGE, mApplications.mHasReceivedBridgeCallback); 454 } 455 456 @Override 457 public void onPause() { 458 super.onPause(); 459 if (mApplications != null) { 460 mApplications.pause(); 461 } 462 } 463 464 @Override 465 public void onStop() { 466 super.onStop(); 467 mResetAppsHelper.stop(); 468 } 469 470 @Override 471 public void onDestroyView() { 472 super.onDestroyView(); 473 474 if (mApplications != null) { 475 mApplications.release(); 476 } 477 mRootView = null; 478 } 479 480 @Override 481 public void onActivityResult(int requestCode, int resultCode, Intent data) { 482 if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) { 483 if (mListType == LIST_TYPE_NOTIFICATION) { 484 mApplications.mExtraInfoBridge.forceUpdate(mCurrentPkgName, mCurrentUid); 485 } else if (mListType == LIST_TYPE_HIGH_POWER || mListType == LIST_TYPE_OVERLAY 486 || mListType == LIST_TYPE_WRITE_SETTINGS) { 487 if (mFinishAfterDialog) { 488 getActivity().onBackPressed(); 489 } else { 490 mApplications.mExtraInfoBridge.forceUpdate(mCurrentPkgName, mCurrentUid); 491 } 492 } else { 493 mApplicationsState.requestSize(mCurrentPkgName, UserHandle.getUserId(mCurrentUid)); 494 } 495 } 496 } 497 498 // utility method used to start sub activity 499 private void startApplicationDetailsActivity() { 500 switch (mListType) { 501 case LIST_TYPE_NOTIFICATION: 502 startAppInfoFragment(AppNotificationSettings.class, 503 R.string.app_notifications_title); 504 break; 505 case LIST_TYPE_DOMAINS_URLS: 506 startAppInfoFragment(AppLaunchSettings.class, R.string.auto_launch_label); 507 break; 508 case LIST_TYPE_USAGE_ACCESS: 509 startAppInfoFragment(UsageAccessDetails.class, R.string.usage_access); 510 break; 511 case LIST_TYPE_STORAGE: 512 startAppInfoFragment(AppStorageSettings.class, R.string.storage_settings); 513 break; 514 case LIST_TYPE_HIGH_POWER: 515 HighPowerDetail.show(this, mCurrentPkgName, INSTALLED_APP_DETAILS, 516 mFinishAfterDialog); 517 break; 518 case LIST_TYPE_OVERLAY: 519 startAppInfoFragment(DrawOverlayDetails.class, R.string.overlay_settings); 520 break; 521 case LIST_TYPE_WRITE_SETTINGS: 522 startAppInfoFragment(WriteSettingsDetails.class, R.string.write_system_settings); 523 break; 524 // TODO: Figure out if there is a way where we can spin up the profile's settings 525 // process ahead of time, to avoid a long load of data when user clicks on a managed app. 526 // Maybe when they load the list of apps that contains managed profile apps. 527 default: 528 startAppInfoFragment(InstalledAppDetails.class, R.string.application_info_label); 529 break; 530 } 531 } 532 533 private void startAppInfoFragment(Class<?> fragment, int titleRes) { 534 AppInfoBase.startAppInfoFragment(fragment, titleRes, mCurrentPkgName, mCurrentUid, this, 535 INSTALLED_APP_DETAILS); 536 } 537 538 @Override 539 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 540 if (mListType == LIST_TYPE_DOMAINS_URLS) { 541 return; 542 } 543 HelpUtils.prepareHelpMenuItem(getActivity(), menu, mListType == LIST_TYPE_MAIN 544 ? R.string.help_uri_apps : R.string.help_uri_notifications, getClass().getName()); 545 mOptionsMenu = menu; 546 inflater.inflate(R.menu.manage_apps, menu); 547 updateOptionsMenu(); 548 } 549 550 @Override 551 public void onPrepareOptionsMenu(Menu menu) { 552 updateOptionsMenu(); 553 } 554 555 @Override 556 public void onDestroyOptionsMenu() { 557 mOptionsMenu = null; 558 } 559 560 void updateOptionsMenu() { 561 if (mOptionsMenu == null) { 562 return; 563 } 564 mOptionsMenu.findItem(R.id.advanced).setVisible( 565 mListType == LIST_TYPE_MAIN || mListType == LIST_TYPE_NOTIFICATION); 566 567 mOptionsMenu.findItem(R.id.sort_order_alpha).setVisible(mListType == LIST_TYPE_STORAGE 568 && mSortOrder != R.id.sort_order_alpha); 569 mOptionsMenu.findItem(R.id.sort_order_size).setVisible(mListType == LIST_TYPE_STORAGE 570 && mSortOrder != R.id.sort_order_size); 571 572 mOptionsMenu.findItem(R.id.show_system).setVisible(!mShowSystem 573 && mListType != LIST_TYPE_HIGH_POWER); 574 mOptionsMenu.findItem(R.id.hide_system).setVisible(mShowSystem 575 && mListType != LIST_TYPE_HIGH_POWER); 576 } 577 578 @Override 579 public boolean onOptionsItemSelected(MenuItem item) { 580 int menuId = item.getItemId(); 581 switch (item.getItemId()) { 582 case R.id.sort_order_alpha: 583 case R.id.sort_order_size: 584 mSortOrder = menuId; 585 mListView.setFastScrollEnabled(isFastScrollEnabled()); 586 if (mApplications != null) { 587 mApplications.rebuild(mSortOrder); 588 } 589 break; 590 case R.id.show_system: 591 case R.id.hide_system: 592 mShowSystem = !mShowSystem; 593 mApplications.rebuild(false); 594 break; 595 case R.id.reset_app_preferences: 596 mResetAppsHelper.buildResetDialog(); 597 return true; 598 case R.id.advanced: 599 if (mListType == LIST_TYPE_NOTIFICATION) { 600 ((SettingsActivity) getActivity()).startPreferencePanel( 601 ConfigureNotificationSettings.class.getName(), null, 602 R.string.configure_notification_settings, null, this, ADVANCED_SETTINGS); 603 } else { 604 ((SettingsActivity) getActivity()).startPreferencePanel( 605 AdvancedAppSettings.class.getName(), null, R.string.configure_apps, 606 null, this, ADVANCED_SETTINGS); 607 } 608 return true; 609 default: 610 // Handle the home button 611 return false; 612 } 613 updateOptionsMenu(); 614 return true; 615 } 616 617 @Override 618 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 619 if (mApplications != null && mApplications.getCount() > position) { 620 ApplicationsState.AppEntry entry = mApplications.getAppEntry(position); 621 mCurrentPkgName = entry.info.packageName; 622 mCurrentUid = entry.info.uid; 623 startApplicationDetailsActivity(); 624 } 625 } 626 627 @Override 628 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 629 mFilter = mFilterAdapter.getFilter(position); 630 mApplications.setFilter(mFilter); 631 if (DEBUG) Log.d(TAG, "Selecting filter " + mFilter); 632 } 633 634 @Override 635 public void onNothingSelected(AdapterView<?> parent) { 636 } 637 638 public void updateView() { 639 updateOptionsMenu(); 640 final Activity host = getActivity(); 641 if (host != null) { 642 host.invalidateOptionsMenu(); 643 } 644 } 645 646 public void setHasDisabled(boolean hasDisabledApps) { 647 if (mListType != LIST_TYPE_MAIN) { 648 return; 649 } 650 mFilterAdapter.setFilterEnabled(FILTER_APPS_ENABLED, hasDisabledApps); 651 mFilterAdapter.setFilterEnabled(FILTER_APPS_DISABLED, hasDisabledApps); 652 } 653 654 static class FilterSpinnerAdapter extends ArrayAdapter<CharSequence> { 655 656 private final ManageApplications mManageApplications; 657 658 // Use ArrayAdapter for view logic, but have our own list for managing 659 // the options available. 660 private final ArrayList<Integer> mFilterOptions = new ArrayList<>(); 661 662 public FilterSpinnerAdapter(ManageApplications manageApplications) { 663 super(manageApplications.getActivity(), R.layout.filter_spinner_item); 664 setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 665 mManageApplications = manageApplications; 666 } 667 668 public int getFilter(int position) { 669 return mFilterOptions.get(position); 670 } 671 672 public void setFilterEnabled(int filter, boolean enabled) { 673 if (enabled) { 674 enableFilter(filter); 675 } else { 676 disableFilter(filter); 677 } 678 } 679 680 public void enableFilter(int filter) { 681 if (mFilterOptions.contains(filter)) return; 682 if (DEBUG) Log.d(TAG, "Enabling filter " + filter); 683 mFilterOptions.add(filter); 684 Collections.sort(mFilterOptions); 685 mManageApplications.mSpinnerHeader.setVisibility( 686 mFilterOptions.size() > 1 ? View.VISIBLE : View.GONE); 687 notifyDataSetChanged(); 688 if (mFilterOptions.size() == 1) { 689 if (DEBUG) Log.d(TAG, "Auto selecting filter " + filter); 690 mManageApplications.mFilterSpinner.setSelection(0); 691 mManageApplications.onItemSelected(null, null, 0, 0); 692 } 693 } 694 695 public void disableFilter(int filter) { 696 if (!mFilterOptions.remove((Integer) filter)) { 697 return; 698 } 699 if (DEBUG) Log.d(TAG, "Disabling filter " + filter); 700 Collections.sort(mFilterOptions); 701 mManageApplications.mSpinnerHeader.setVisibility( 702 mFilterOptions.size() > 1 ? View.VISIBLE : View.GONE); 703 notifyDataSetChanged(); 704 if (mManageApplications.mFilter == filter) { 705 if (mFilterOptions.size() > 0) { 706 if (DEBUG) Log.d(TAG, "Auto selecting filter " + mFilterOptions.get(0)); 707 mManageApplications.mFilterSpinner.setSelection(0); 708 mManageApplications.onItemSelected(null, null, 0, 0); 709 } 710 } 711 } 712 713 @Override 714 public int getCount() { 715 return mFilterOptions.size(); 716 } 717 718 @Override 719 public CharSequence getItem(int position) { 720 return getFilterString(mFilterOptions.get(position)); 721 } 722 723 private CharSequence getFilterString(int filter) { 724 return mManageApplications.getString(FILTER_LABELS[filter]); 725 } 726 727 } 728 729 /* 730 * Custom adapter implementation for the ListView 731 * This adapter maintains a map for each displayed application and its properties 732 * An index value on each AppInfo object indicates the correct position or index 733 * in the list. If the list gets updated dynamically when the user is viewing the list of 734 * applications, we need to return the correct index of position. This is done by mapping 735 * the getId methods via the package name into the internal maps and indices. 736 * The order of applications in the list is mirrored in mAppLocalList 737 */ 738 static class ApplicationsAdapter extends BaseAdapter implements Filterable, 739 ApplicationsState.Callbacks, AppStateBaseBridge.Callback, 740 AbsListView.RecyclerListener, SectionIndexer { 741 private static final SectionInfo[] EMPTY_SECTIONS = new SectionInfo[0]; 742 743 private final ApplicationsState mState; 744 private final ApplicationsState.Session mSession; 745 private final ManageApplications mManageApplications; 746 private final Context mContext; 747 private final ArrayList<View> mActive = new ArrayList<View>(); 748 private final AppStateBaseBridge mExtraInfoBridge; 749 private final Handler mBgHandler; 750 private final Handler mFgHandler; 751 private int mFilterMode; 752 private ArrayList<ApplicationsState.AppEntry> mBaseEntries; 753 private ArrayList<ApplicationsState.AppEntry> mEntries; 754 private boolean mResumed; 755 private int mLastSortMode = -1; 756 private int mWhichSize = SIZE_TOTAL; 757 CharSequence mCurFilterPrefix; 758 private PackageManager mPm; 759 private AppFilter mOverrideFilter; 760 private boolean mHasReceivedLoadEntries; 761 private boolean mHasReceivedBridgeCallback; 762 763 private AlphabeticIndex.ImmutableIndex mIndex; 764 private SectionInfo[] mSections = EMPTY_SECTIONS; 765 private int[] mPositionToSectionIndex; 766 767 private Filter mFilter = new Filter() { 768 @Override 769 protected FilterResults performFiltering(CharSequence constraint) { 770 ArrayList<ApplicationsState.AppEntry> entries 771 = applyPrefixFilter(constraint, mBaseEntries); 772 FilterResults fr = new FilterResults(); 773 fr.values = entries; 774 fr.count = entries.size(); 775 return fr; 776 } 777 778 @Override 779 @SuppressWarnings("unchecked") 780 protected void publishResults(CharSequence constraint, FilterResults results) { 781 mCurFilterPrefix = constraint; 782 mEntries = (ArrayList<ApplicationsState.AppEntry>) results.values; 783 rebuildSections(); 784 notifyDataSetChanged(); 785 } 786 }; 787 788 public ApplicationsAdapter(ApplicationsState state, ManageApplications manageApplications, 789 int filterMode) { 790 mState = state; 791 mFgHandler = new Handler(); 792 mBgHandler = new Handler(mState.getBackgroundLooper()); 793 mSession = state.newSession(this); 794 mManageApplications = manageApplications; 795 mContext = manageApplications.getActivity(); 796 mPm = mContext.getPackageManager(); 797 mFilterMode = filterMode; 798 if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) { 799 mExtraInfoBridge = new AppStateNotificationBridge(mContext, mState, this, 800 manageApplications.mNotifBackend); 801 } else if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) { 802 mExtraInfoBridge = new AppStateUsageBridge(mContext, mState, this); 803 } else if (mManageApplications.mListType == LIST_TYPE_HIGH_POWER) { 804 mExtraInfoBridge = new AppStatePowerBridge(mState, this); 805 } else if (mManageApplications.mListType == LIST_TYPE_OVERLAY) { 806 mExtraInfoBridge = new AppStateOverlayBridge(mContext, mState, this); 807 } else if (mManageApplications.mListType == LIST_TYPE_WRITE_SETTINGS) { 808 mExtraInfoBridge = new AppStateWriteSettingsBridge(mContext, mState, this); 809 } else { 810 mExtraInfoBridge = null; 811 } 812 } 813 814 public void setOverrideFilter(AppFilter overrideFilter) { 815 mOverrideFilter = overrideFilter; 816 rebuild(true); 817 } 818 819 public void setFilter(int filter) { 820 mFilterMode = filter; 821 rebuild(true); 822 } 823 824 public void resume(int sort) { 825 if (DEBUG) Log.i(TAG, "Resume! mResumed=" + mResumed); 826 if (!mResumed) { 827 mResumed = true; 828 mSession.resume(); 829 mLastSortMode = sort; 830 if (mExtraInfoBridge != null) { 831 mExtraInfoBridge.resume(); 832 } 833 rebuild(false); 834 } else { 835 rebuild(sort); 836 } 837 } 838 839 public void pause() { 840 if (mResumed) { 841 mResumed = false; 842 mSession.pause(); 843 if (mExtraInfoBridge != null) { 844 mExtraInfoBridge.pause(); 845 } 846 } 847 } 848 849 public void release() { 850 mSession.release(); 851 if (mExtraInfoBridge != null) { 852 mExtraInfoBridge.release(); 853 } 854 } 855 856 public void rebuild(int sort) { 857 if (sort == mLastSortMode) { 858 return; 859 } 860 mLastSortMode = sort; 861 rebuild(true); 862 } 863 864 public void rebuild(boolean eraseold) { 865 if (!mHasReceivedLoadEntries 866 || (mExtraInfoBridge != null && !mHasReceivedBridgeCallback)) { 867 // Don't rebuild the list until all the app entries are loaded. 868 return; 869 } 870 if (DEBUG) Log.i(TAG, "Rebuilding app list..."); 871 ApplicationsState.AppFilter filterObj; 872 Comparator<AppEntry> comparatorObj; 873 boolean emulated = Environment.isExternalStorageEmulated(); 874 if (emulated) { 875 mWhichSize = SIZE_TOTAL; 876 } else { 877 mWhichSize = SIZE_INTERNAL; 878 } 879 filterObj = FILTERS[mFilterMode]; 880 if (mOverrideFilter != null) { 881 filterObj = mOverrideFilter; 882 } 883 if (!mManageApplications.mShowSystem) { 884 filterObj = new CompoundFilter(filterObj, 885 ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER); 886 } 887 switch (mLastSortMode) { 888 case R.id.sort_order_size: 889 switch (mWhichSize) { 890 case SIZE_INTERNAL: 891 comparatorObj = ApplicationsState.INTERNAL_SIZE_COMPARATOR; 892 break; 893 case SIZE_EXTERNAL: 894 comparatorObj = ApplicationsState.EXTERNAL_SIZE_COMPARATOR; 895 break; 896 default: 897 comparatorObj = ApplicationsState.SIZE_COMPARATOR; 898 break; 899 } 900 break; 901 default: 902 comparatorObj = ApplicationsState.ALPHA_COMPARATOR; 903 break; 904 } 905 AppFilter finalFilterObj = filterObj; 906 mBgHandler.post(() -> { 907 final ArrayList<AppEntry> entries = mSession.rebuild(finalFilterObj, 908 comparatorObj, false); 909 if (entries != null) { 910 mFgHandler.post(() -> onRebuildComplete(entries)); 911 } 912 }); 913 } 914 915 916 static private boolean packageNameEquals(PackageItemInfo info1, PackageItemInfo info2) { 917 if (info1 == null || info2 == null) { 918 return false; 919 } 920 if (info1.packageName == null || info2.packageName == null) { 921 return false; 922 } 923 return info1.packageName.equals(info2.packageName); 924 } 925 926 private ArrayList<ApplicationsState.AppEntry> removeDuplicateIgnoringUser( 927 ArrayList<ApplicationsState.AppEntry> entries) 928 { 929 int size = entries.size(); 930 // returnList will not have more entries than entries 931 ArrayList<ApplicationsState.AppEntry> returnEntries = new 932 ArrayList<ApplicationsState.AppEntry>(size); 933 934 // assume appinfo of same package but different users are grouped together 935 PackageItemInfo lastInfo = null; 936 for (int i = 0; i < size; i++) { 937 AppEntry appEntry = entries.get(i); 938 PackageItemInfo info = appEntry.info; 939 if (!packageNameEquals(lastInfo, appEntry.info)) { 940 returnEntries.add(appEntry); 941 } 942 lastInfo = info; 943 } 944 returnEntries.trimToSize(); 945 return returnEntries; 946 } 947 948 @Override 949 public void onRebuildComplete(ArrayList<AppEntry> entries) { 950 if (mFilterMode == FILTER_APPS_POWER_WHITELIST || 951 mFilterMode == FILTER_APPS_POWER_WHITELIST_ALL) { 952 entries = removeDuplicateIgnoringUser(entries); 953 } 954 mBaseEntries = entries; 955 if (mBaseEntries != null) { 956 mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries); 957 rebuildSections(); 958 } else { 959 mEntries = null; 960 mSections = EMPTY_SECTIONS; 961 mPositionToSectionIndex = null; 962 } 963 964 notifyDataSetChanged(); 965 966 if (mSession.getAllApps().size() != 0 967 && mManageApplications.mListContainer.getVisibility() != View.VISIBLE) { 968 Utils.handleLoadingContainer(mManageApplications.mLoadingContainer, 969 mManageApplications.mListContainer, true, true); 970 } 971 if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) { 972 // No enabled or disabled filters for usage access. 973 return; 974 } 975 976 mManageApplications.setHasDisabled(mState.haveDisabledApps()); 977 } 978 979 private void rebuildSections() { 980 if (mEntries!= null && mManageApplications.mListView.isFastScrollEnabled()) { 981 // Rebuild sections 982 if (mIndex == null) { 983 LocaleList locales = mContext.getResources().getConfiguration().getLocales(); 984 if (locales.size() == 0) { 985 locales = new LocaleList(Locale.ENGLISH); 986 } 987 AlphabeticIndex index = new AlphabeticIndex<>(locales.get(0)); 988 int localeCount = locales.size(); 989 for (int i = 1; i < localeCount; i++) { 990 index.addLabels(locales.get(i)); 991 } 992 // Ensure we always have some base English locale buckets 993 index.addLabels(Locale.ENGLISH); 994 mIndex = index.buildImmutableIndex(); 995 } 996 997 ArrayList<SectionInfo> sections = new ArrayList<>(); 998 int lastSecId = -1; 999 int totalEntries = mEntries.size(); 1000 mPositionToSectionIndex = new int[totalEntries]; 1001 1002 for (int pos = 0; pos < totalEntries; pos++) { 1003 String label = mEntries.get(pos).label; 1004 int secId = mIndex.getBucketIndex(TextUtils.isEmpty(label) ? "" : label); 1005 if (secId != lastSecId) { 1006 lastSecId = secId; 1007 sections.add(new SectionInfo(mIndex.getBucket(secId).getLabel(), pos)); 1008 } 1009 mPositionToSectionIndex[pos] = sections.size() - 1; 1010 } 1011 mSections = sections.toArray(EMPTY_SECTIONS); 1012 } else { 1013 mSections = EMPTY_SECTIONS; 1014 mPositionToSectionIndex = null; 1015 } 1016 } 1017 1018 private void updateLoading() { 1019 Utils.handleLoadingContainer(mManageApplications.mLoadingContainer, 1020 mManageApplications.mListContainer, 1021 mHasReceivedLoadEntries && mSession.getAllApps().size() != 0, false); 1022 } 1023 1024 ArrayList<ApplicationsState.AppEntry> applyPrefixFilter(CharSequence prefix, 1025 ArrayList<ApplicationsState.AppEntry> origEntries) { 1026 if (prefix == null || prefix.length() == 0) { 1027 return origEntries; 1028 } else { 1029 String prefixStr = ApplicationsState.normalize(prefix.toString()); 1030 final String spacePrefixStr = " " + prefixStr; 1031 ArrayList<ApplicationsState.AppEntry> newEntries 1032 = new ArrayList<ApplicationsState.AppEntry>(); 1033 for (int i = 0; i < origEntries.size(); i++) { 1034 ApplicationsState.AppEntry entry = origEntries.get(i); 1035 String nlabel = entry.getNormalizedLabel(); 1036 if (nlabel.startsWith(prefixStr) || nlabel.indexOf(spacePrefixStr) != -1) { 1037 newEntries.add(entry); 1038 } 1039 } 1040 return newEntries; 1041 } 1042 } 1043 1044 @Override 1045 public void onExtraInfoUpdated() { 1046 mHasReceivedBridgeCallback = true; 1047 rebuild(false); 1048 } 1049 1050 @Override 1051 public void onRunningStateChanged(boolean running) { 1052 mManageApplications.getActivity().setProgressBarIndeterminateVisibility(running); 1053 } 1054 1055 @Override 1056 public void onPackageListChanged() { 1057 rebuild(false); 1058 } 1059 1060 @Override 1061 public void onPackageIconChanged() { 1062 // We ensure icons are loaded when their item is displayed, so 1063 // don't care about icons loaded in the background. 1064 } 1065 1066 @Override 1067 public void onLoadEntriesCompleted() { 1068 mHasReceivedLoadEntries = true; 1069 // We may have been skipping rebuilds until this came in, trigger one now. 1070 rebuild(false); 1071 } 1072 1073 @Override 1074 public void onPackageSizeChanged(String packageName) { 1075 for (int i = 0; i < mActive.size(); i++) { 1076 AppViewHolder holder = (AppViewHolder) mActive.get(i).getTag(); 1077 if (holder.entry.info.packageName.equals(packageName)) { 1078 synchronized (holder.entry) { 1079 updateSummary(holder); 1080 } 1081 if (holder.entry.info.packageName.equals(mManageApplications.mCurrentPkgName) 1082 && mLastSortMode == R.id.sort_order_size) { 1083 // We got the size information for the last app the 1084 // user viewed, and are sorting by size... they may 1085 // have cleared data, so we immediately want to resort 1086 // the list with the new size to reflect it to the user. 1087 rebuild(false); 1088 } 1089 return; 1090 } 1091 } 1092 } 1093 1094 @Override 1095 public void onLauncherInfoChanged() { 1096 if (!mManageApplications.mShowSystem) { 1097 rebuild(false); 1098 } 1099 } 1100 1101 @Override 1102 public void onAllSizesComputed() { 1103 if (mLastSortMode == R.id.sort_order_size) { 1104 rebuild(false); 1105 } 1106 } 1107 1108 public int getCount() { 1109 return mEntries != null ? mEntries.size() : 0; 1110 } 1111 1112 public Object getItem(int position) { 1113 return mEntries.get(position); 1114 } 1115 1116 public ApplicationsState.AppEntry getAppEntry(int position) { 1117 return mEntries.get(position); 1118 } 1119 1120 public long getItemId(int position) { 1121 return mEntries.get(position).id; 1122 } 1123 1124 @Override 1125 public boolean areAllItemsEnabled() { 1126 return false; 1127 } 1128 1129 @Override 1130 public boolean isEnabled(int position) { 1131 if (mManageApplications.mListType != LIST_TYPE_HIGH_POWER) { 1132 return true; 1133 } 1134 ApplicationsState.AppEntry entry = mEntries.get(position); 1135 return !PowerWhitelistBackend.getInstance().isSysWhitelisted(entry.info.packageName); 1136 } 1137 1138 public View getView(int position, View convertView, ViewGroup parent) { 1139 // A ViewHolder keeps references to children views to avoid unnecessary calls 1140 // to findViewById() on each row. 1141 AppViewHolder holder = AppViewHolder.createOrRecycle(mManageApplications.mInflater, 1142 convertView); 1143 convertView = holder.rootView; 1144 1145 // Bind the data efficiently with the holder 1146 ApplicationsState.AppEntry entry = mEntries.get(position); 1147 synchronized (entry) { 1148 holder.entry = entry; 1149 if (entry.label != null) { 1150 holder.appName.setText(entry.label); 1151 } 1152 mState.ensureIcon(entry); 1153 if (entry.icon != null) { 1154 holder.appIcon.setImageDrawable(entry.icon); 1155 } 1156 updateSummary(holder); 1157 if ((entry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { 1158 holder.disabled.setVisibility(View.VISIBLE); 1159 holder.disabled.setText(R.string.not_installed); 1160 } else if (!entry.info.enabled) { 1161 holder.disabled.setVisibility(View.VISIBLE); 1162 holder.disabled.setText(R.string.disabled); 1163 } else { 1164 holder.disabled.setVisibility(View.GONE); 1165 } 1166 } 1167 mActive.remove(convertView); 1168 mActive.add(convertView); 1169 convertView.setEnabled(isEnabled(position)); 1170 return convertView; 1171 } 1172 1173 private void updateSummary(AppViewHolder holder) { 1174 switch (mManageApplications.mListType) { 1175 case LIST_TYPE_NOTIFICATION: 1176 if (holder.entry.extraInfo != null) { 1177 holder.summary.setText(InstalledAppDetails.getNotificationSummary( 1178 (AppRow) holder.entry.extraInfo, mContext)); 1179 } else { 1180 holder.summary.setText(null); 1181 } 1182 break; 1183 1184 case LIST_TYPE_DOMAINS_URLS: 1185 holder.summary.setText(getDomainsSummary(holder.entry.info.packageName)); 1186 break; 1187 1188 case LIST_TYPE_USAGE_ACCESS: 1189 if (holder.entry.extraInfo != null) { 1190 holder.summary.setText((new UsageState((PermissionState) holder.entry 1191 .extraInfo)).isPermissible() ? R.string.switch_on_text : 1192 R.string.switch_off_text); 1193 } else { 1194 holder.summary.setText(null); 1195 } 1196 break; 1197 1198 case LIST_TYPE_HIGH_POWER: 1199 holder.summary.setText(HighPowerDetail.getSummary(mContext, holder.entry)); 1200 break; 1201 1202 case LIST_TYPE_OVERLAY: 1203 holder.summary.setText(DrawOverlayDetails.getSummary(mContext, holder.entry)); 1204 break; 1205 1206 case LIST_TYPE_WRITE_SETTINGS: 1207 holder.summary.setText(WriteSettingsDetails.getSummary(mContext, 1208 holder.entry)); 1209 break; 1210 1211 default: 1212 holder.updateSizeText(mManageApplications.mInvalidSizeStr, mWhichSize); 1213 break; 1214 } 1215 } 1216 1217 @Override 1218 public Filter getFilter() { 1219 return mFilter; 1220 } 1221 1222 @Override 1223 public void onMovedToScrapHeap(View view) { 1224 mActive.remove(view); 1225 } 1226 1227 private CharSequence getDomainsSummary(String packageName) { 1228 // If the user has explicitly said "no" for this package, that's the 1229 // string we should show. 1230 int domainStatus = mPm.getIntentVerificationStatusAsUser(packageName, UserHandle.myUserId()); 1231 if (domainStatus == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { 1232 return mContext.getString(R.string.domain_urls_summary_none); 1233 } 1234 // Otherwise, ask package manager for the domains for this package, 1235 // and show the first one (or none if there aren't any). 1236 ArraySet<String> result = Utils.getHandledDomains(mPm, packageName); 1237 if (result.size() == 0) { 1238 return mContext.getString(R.string.domain_urls_summary_none); 1239 } else if (result.size() == 1) { 1240 return mContext.getString(R.string.domain_urls_summary_one, result.valueAt(0)); 1241 } else { 1242 return mContext.getString(R.string.domain_urls_summary_some, result.valueAt(0)); 1243 } 1244 } 1245 1246 @Override 1247 public Object[] getSections() { 1248 return mSections; 1249 } 1250 1251 @Override 1252 public int getPositionForSection(int sectionIndex) { 1253 return mSections[sectionIndex].position; 1254 } 1255 1256 @Override 1257 public int getSectionForPosition(int position) { 1258 return mPositionToSectionIndex[position]; 1259 } 1260 } 1261 1262 private static class SummaryProvider implements SummaryLoader.SummaryProvider { 1263 1264 private final Context mContext; 1265 private final SummaryLoader mLoader; 1266 private ApplicationsState.Session mSession; 1267 1268 private SummaryProvider(Context context, SummaryLoader loader) { 1269 mContext = context; 1270 mLoader = loader; 1271 } 1272 1273 @Override 1274 public void setListening(boolean listening) { 1275 if (listening) { 1276 new AppCounter(mContext) { 1277 @Override 1278 protected void onCountComplete(int num) { 1279 mLoader.setSummary(SummaryProvider.this, 1280 mContext.getString(R.string.apps_summary, num)); 1281 } 1282 1283 @Override 1284 protected boolean includeInCount(ApplicationInfo info) { 1285 if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { 1286 return true; 1287 } else if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { 1288 return true; 1289 } 1290 Intent launchIntent = new Intent(Intent.ACTION_MAIN, null) 1291 .addCategory(Intent.CATEGORY_LAUNCHER) 1292 .setPackage(info.packageName); 1293 int userId = UserHandle.getUserId(info.uid); 1294 List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser( 1295 launchIntent, 1296 PackageManager.GET_DISABLED_COMPONENTS 1297 | PackageManager.MATCH_DIRECT_BOOT_AWARE 1298 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 1299 userId); 1300 return intents != null && intents.size() != 0; 1301 } 1302 }.execute(); 1303 } 1304 } 1305 } 1306 1307 private static class SectionInfo { 1308 final String label; 1309 final int position; 1310 1311 public SectionInfo(String label, int position) { 1312 this.label = label; 1313 this.position = position; 1314 } 1315 1316 @Override 1317 public String toString() { 1318 return label; 1319 } 1320 } 1321 1322 public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY 1323 = new SummaryLoader.SummaryProviderFactory() { 1324 @Override 1325 public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity, 1326 SummaryLoader summaryLoader) { 1327 return new SummaryProvider(activity, summaryLoader); 1328 } 1329 }; 1330 } 1331