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