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