ManageApplications.java revision 0ced3704270fbc5d2713102617f49e8bdeff1294
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.manageapplications; 18 19import static com.android.settings.applications.manageapplications.AppFilterRegistry 20 .FILTER_APPS_BLOCKED; 21import static com.android.settings.applications.manageapplications.AppFilterRegistry 22 .FILTER_APPS_DISABLED; 23import static com.android.settings.applications.manageapplications.AppFilterRegistry 24 .FILTER_APPS_ENABLED; 25import static com.android.settings.applications.manageapplications.AppFilterRegistry 26 .FILTER_APPS_INSTANT; 27import static com.android.settings.applications.manageapplications.AppFilterRegistry 28 .FILTER_APPS_PERSONAL; 29import static com.android.settings.applications.manageapplications.AppFilterRegistry 30 .FILTER_APPS_POWER_WHITELIST; 31import static com.android.settings.applications.manageapplications.AppFilterRegistry 32 .FILTER_APPS_POWER_WHITELIST_ALL; 33import static com.android.settings.applications.manageapplications.AppFilterRegistry 34 .FILTER_APPS_WORK; 35 36import android.annotation.Nullable; 37import android.annotation.StringRes; 38import android.app.Activity; 39import android.content.Context; 40import android.content.Intent; 41import android.content.pm.ApplicationInfo; 42import android.content.pm.PackageItemInfo; 43import android.os.Bundle; 44import android.os.Environment; 45import android.os.UserHandle; 46import android.os.UserManager; 47import android.preference.PreferenceFrameLayout; 48import android.support.annotation.VisibleForTesting; 49import android.support.v7.widget.LinearLayoutManager; 50import android.support.v7.widget.RecyclerView; 51import android.text.TextUtils; 52import android.util.ArraySet; 53import android.util.Log; 54import android.view.LayoutInflater; 55import android.view.Menu; 56import android.view.MenuInflater; 57import android.view.MenuItem; 58import android.view.View; 59import android.view.ViewGroup; 60import android.widget.AdapterView; 61import android.widget.AdapterView.OnItemSelectedListener; 62import android.widget.ArrayAdapter; 63import android.widget.FrameLayout; 64import android.widget.Spinner; 65 66import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 67import com.android.settings.R; 68import com.android.settings.Settings; 69import com.android.settings.Settings.GamesStorageActivity; 70import com.android.settings.Settings.HighPowerApplicationsActivity; 71import com.android.settings.Settings.ManageExternalSourcesActivity; 72import com.android.settings.Settings.MoviesStorageActivity; 73import com.android.settings.Settings.OverlaySettingsActivity; 74import com.android.settings.Settings.StorageUseActivity; 75import com.android.settings.Settings.UsageAccessSettingsActivity; 76import com.android.settings.Settings.WriteSettingsActivity; 77import com.android.settings.SettingsActivity; 78import com.android.settings.applications.AppInfoBase; 79import com.android.settings.applications.AppStateAppOpsBridge.PermissionState; 80import com.android.settings.applications.AppStateBaseBridge; 81import com.android.settings.applications.AppStateDirectoryAccessBridge; 82import com.android.settings.applications.AppStateInstallAppsBridge; 83import com.android.settings.applications.AppStateNotificationBridge; 84import com.android.settings.applications.AppStateOverlayBridge; 85import com.android.settings.applications.AppStatePowerBridge; 86import com.android.settings.applications.AppStateUsageBridge; 87import com.android.settings.applications.AppStateUsageBridge.UsageState; 88import com.android.settings.applications.AppStateWriteSettingsBridge; 89import com.android.settings.applications.AppStorageSettings; 90import com.android.settings.applications.DefaultAppSettings; 91import com.android.settings.applications.DirectoryAccessDetails; 92import com.android.settings.applications.InstalledAppCounter; 93import com.android.settings.applications.UsageAccessDetails; 94import com.android.settings.applications.appinfo.AppInfoDashboardFragment; 95import com.android.settings.applications.appinfo.AppNotificationPreferenceController; 96import com.android.settings.applications.appinfo.DrawOverlayDetails; 97import com.android.settings.applications.appinfo.ExternalSourcesDetails; 98import com.android.settings.applications.appinfo.WriteSettingsDetails; 99import com.android.settings.core.InstrumentedFragment; 100import com.android.settings.core.SubSettingLauncher; 101import com.android.settings.dashboard.SummaryLoader; 102import com.android.settings.fuelgauge.HighPowerDetail; 103import com.android.settings.notification.AppNotificationSettings; 104import com.android.settings.notification.ConfigureNotificationSettings; 105import com.android.settings.notification.NotificationBackend; 106import com.android.settings.notification.NotificationBackend.AppRow; 107import com.android.settings.widget.LoadingViewController; 108import com.android.settings.wifi.AppStateChangeWifiStateBridge; 109import com.android.settings.wifi.ChangeWifiStateDetails; 110import com.android.settingslib.HelpUtils; 111import com.android.settingslib.applications.ApplicationsState; 112import com.android.settingslib.applications.ApplicationsState.AppEntry; 113import com.android.settingslib.applications.ApplicationsState.AppFilter; 114import com.android.settingslib.applications.ApplicationsState.CompoundFilter; 115import com.android.settingslib.applications.ApplicationsState.VolumeFilter; 116import com.android.settingslib.applications.StorageStatsSource; 117import com.android.settingslib.fuelgauge.PowerWhitelistBackend; 118import com.android.settingslib.utils.ThreadUtils; 119import com.android.settingslib.wrapper.PackageManagerWrapper; 120 121import java.util.ArrayList; 122import java.util.Arrays; 123import java.util.Collections; 124import java.util.Comparator; 125import java.util.Set; 126 127/** 128 * Activity to pick an application that will be used to display installation information and 129 * options to uninstall/delete user data for system applications. This activity 130 * can be launched through Settings or via the ACTION_MANAGE_PACKAGE_STORAGE 131 * intent. 132 */ 133public class ManageApplications extends InstrumentedFragment 134 implements View.OnClickListener, OnItemSelectedListener { 135 136 static final String TAG = "ManageApplications"; 137 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 138 139 // Intent extras. 140 public static final String EXTRA_CLASSNAME = "classname"; 141 // Used for storage only. 142 public static final String EXTRA_VOLUME_UUID = "volumeUuid"; 143 public static final String EXTRA_VOLUME_NAME = "volumeName"; 144 public static final String EXTRA_STORAGE_TYPE = "storageType"; 145 public static final String EXTRA_WORK_ONLY = "workProfileOnly"; 146 public static final String EXTRA_WORK_ID = "workId"; 147 148 private static final String EXTRA_SORT_ORDER = "sortOrder"; 149 private static final String EXTRA_SHOW_SYSTEM = "showSystem"; 150 private static final String EXTRA_HAS_ENTRIES = "hasEntries"; 151 private static final String EXTRA_HAS_BRIDGE = "hasBridge"; 152 153 // attributes used as keys when passing values to AppInfoDashboardFragment activity 154 public static final String APP_CHG = "chg"; 155 156 // constant value that can be used to check return code from sub activity. 157 private static final int INSTALLED_APP_DETAILS = 1; 158 private static final int ADVANCED_SETTINGS = 2; 159 160 public static final int SIZE_TOTAL = 0; 161 public static final int SIZE_INTERNAL = 1; 162 public static final int SIZE_EXTERNAL = 2; 163 164 // Storage types. Used to determine what the extra item in the list of preferences is. 165 public static final int STORAGE_TYPE_DEFAULT = 0; // Show all apps that are not categorized. 166 public static final int STORAGE_TYPE_MUSIC = 1; 167 public static final int STORAGE_TYPE_LEGACY = 2; // Show apps even if they can be categorized. 168 public static final int STORAGE_TYPE_PHOTOS_VIDEOS = 3; 169 170 private static final int NO_USER_SPECIFIED = -1; 171 172 // sort order 173 private int mSortOrder = R.id.sort_order_alpha; 174 175 // whether showing system apps. 176 private boolean mShowSystem; 177 178 private ApplicationsState mApplicationsState; 179 180 public int mListType; 181 private AppFilterItem mFilter; 182 private ApplicationsAdapter mApplications; 183 184 private View mLoadingContainer; 185 186 private View mListContainer; 187 private RecyclerView mRecyclerView; 188 189 // Size resource used for packages whose size computation failed for some reason 190 CharSequence mInvalidSizeStr; 191 192 private String mCurrentPkgName; 193 private int mCurrentUid; 194 195 private Menu mOptionsMenu; 196 197 public static final int LIST_TYPE_MAIN = 0; 198 public static final int LIST_TYPE_NOTIFICATION = 1; 199 public static final int LIST_TYPE_STORAGE = 3; 200 public static final int LIST_TYPE_USAGE_ACCESS = 4; 201 public static final int LIST_TYPE_HIGH_POWER = 5; 202 public static final int LIST_TYPE_OVERLAY = 6; 203 public static final int LIST_TYPE_WRITE_SETTINGS = 7; 204 public static final int LIST_TYPE_MANAGE_SOURCES = 8; 205 public static final int LIST_TYPE_GAMES = 9; 206 public static final int LIST_TYPE_MOVIES = 10; 207 public static final int LIST_TYPE_PHOTOGRAPHY = 11; 208 public static final int LIST_TYPE_DIRECTORY_ACCESS = 12; 209 public static final int LIST_TYPE_WIFI_ACCESS = 13; 210 211 // List types that should show instant apps. 212 public static final Set<Integer> LIST_TYPES_WITH_INSTANT = new ArraySet<>(Arrays.asList( 213 LIST_TYPE_MAIN, 214 LIST_TYPE_STORAGE)); 215 216 private View mRootView; 217 private View mSpinnerHeader; 218 private Spinner mFilterSpinner; 219 private FilterSpinnerAdapter mFilterAdapter; 220 private NotificationBackend mNotifBackend; 221 private ResetAppsHelper mResetAppsHelper; 222 private String mVolumeUuid; 223 private int mStorageType; 224 private boolean mIsWorkOnly; 225 private int mWorkUserId; 226 private View mEmptyView; 227 228 @Override 229 public void onCreate(Bundle savedInstanceState) { 230 super.onCreate(savedInstanceState); 231 setHasOptionsMenu(true); 232 final Activity activity = getActivity(); 233 mApplicationsState = ApplicationsState.getInstance(activity.getApplication()); 234 235 Intent intent = activity.getIntent(); 236 Bundle args = getArguments(); 237 int screenTitle = intent.getIntExtra( 238 SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, R.string.application_info_label); 239 String className = args != null ? args.getString(EXTRA_CLASSNAME) : null; 240 if (className == null) { 241 className = intent.getComponent().getClassName(); 242 } 243 if (className.equals(StorageUseActivity.class.getName())) { 244 if (args != null && args.containsKey(EXTRA_VOLUME_UUID)) { 245 mVolumeUuid = args.getString(EXTRA_VOLUME_UUID); 246 mStorageType = args.getInt(EXTRA_STORAGE_TYPE, STORAGE_TYPE_DEFAULT); 247 mListType = LIST_TYPE_STORAGE; 248 } else { 249 // No volume selected, display a normal list, sorted by size. 250 mListType = LIST_TYPE_MAIN; 251 } 252 mSortOrder = R.id.sort_order_size; 253 } else if (className.equals(UsageAccessSettingsActivity.class.getName())) { 254 mListType = LIST_TYPE_USAGE_ACCESS; 255 screenTitle = R.string.usage_access; 256 } else if (className.equals(HighPowerApplicationsActivity.class.getName())) { 257 mListType = LIST_TYPE_HIGH_POWER; 258 // Default to showing system. 259 mShowSystem = true; 260 screenTitle = R.string.high_power_apps; 261 } else if (className.equals(OverlaySettingsActivity.class.getName())) { 262 mListType = LIST_TYPE_OVERLAY; 263 screenTitle = R.string.system_alert_window_settings; 264 } else if (className.equals(WriteSettingsActivity.class.getName())) { 265 mListType = LIST_TYPE_WRITE_SETTINGS; 266 screenTitle = R.string.write_settings; 267 } else if (className.equals(ManageExternalSourcesActivity.class.getName())) { 268 mListType = LIST_TYPE_MANAGE_SOURCES; 269 screenTitle = R.string.install_other_apps; 270 } else if (className.equals(GamesStorageActivity.class.getName())) { 271 mListType = LIST_TYPE_GAMES; 272 mSortOrder = R.id.sort_order_size; 273 } else if (className.equals(MoviesStorageActivity.class.getName())) { 274 mListType = LIST_TYPE_MOVIES; 275 mSortOrder = R.id.sort_order_size; 276 } else if (className.equals(Settings.PhotosStorageActivity.class.getName())) { 277 mListType = LIST_TYPE_PHOTOGRAPHY; 278 mSortOrder = R.id.sort_order_size; 279 mStorageType = args.getInt(EXTRA_STORAGE_TYPE, STORAGE_TYPE_DEFAULT); 280 } else if (className.equals(Settings.DirectoryAccessSettingsActivity.class.getName())) { 281 mListType = LIST_TYPE_DIRECTORY_ACCESS; 282 screenTitle = R.string.directory_access; 283 } else if (className.equals(Settings.ChangeWifiStateActivity.class.getName())) { 284 mListType = LIST_TYPE_WIFI_ACCESS; 285 screenTitle = R.string.change_wifi_state_title; 286 } else { 287 if (screenTitle == -1) { 288 screenTitle = R.string.application_info_label; 289 } 290 mListType = LIST_TYPE_MAIN; 291 } 292 final AppFilterRegistry appFilterRegistry = AppFilterRegistry.getInstance(); 293 mFilter = appFilterRegistry.get(appFilterRegistry.getDefaultFilterType(mListType)); 294 mIsWorkOnly = args != null ? args.getBoolean(EXTRA_WORK_ONLY) : false; 295 mWorkUserId = args != null ? args.getInt(EXTRA_WORK_ID) : NO_USER_SPECIFIED; 296 297 if (savedInstanceState != null) { 298 mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder); 299 mShowSystem = savedInstanceState.getBoolean(EXTRA_SHOW_SYSTEM, mShowSystem); 300 } 301 302 mInvalidSizeStr = activity.getText(R.string.invalid_size_value); 303 304 mResetAppsHelper = new ResetAppsHelper(activity); 305 306 if (screenTitle > 0) { 307 activity.setTitle(screenTitle); 308 } 309 } 310 311 @Override 312 public View onCreateView(LayoutInflater inflater, ViewGroup container, 313 Bundle savedInstanceState) { 314 mRootView = inflater.inflate(R.layout.manage_applications_apps, null); 315 mLoadingContainer = mRootView.findViewById(R.id.loading_container); 316 mListContainer = mRootView.findViewById(R.id.list_container); 317 if (mListContainer != null) { 318 // Create adapter and list view here 319 mEmptyView = mListContainer.findViewById(android.R.id.empty); 320 mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter, 321 savedInstanceState); 322 if (savedInstanceState != null) { 323 mApplications.mHasReceivedLoadEntries = 324 savedInstanceState.getBoolean(EXTRA_HAS_ENTRIES, false); 325 mApplications.mHasReceivedBridgeCallback = 326 savedInstanceState.getBoolean(EXTRA_HAS_BRIDGE, false); 327 } 328 int userId = mIsWorkOnly ? mWorkUserId : UserHandle.getUserId(mCurrentUid); 329 if (mStorageType == STORAGE_TYPE_MUSIC) { 330 Context context = getContext(); 331 mApplications.setExtraViewController( 332 new MusicViewHolderController( 333 context, 334 new StorageStatsSource(context), 335 mVolumeUuid, 336 UserHandle.of(userId))); 337 } else if (mStorageType == STORAGE_TYPE_PHOTOS_VIDEOS) { 338 Context context = getContext(); 339 mApplications.setExtraViewController( 340 new PhotosViewHolderController( 341 context, 342 new StorageStatsSource(context), 343 mVolumeUuid, 344 UserHandle.of(userId))); 345 } 346 mRecyclerView = mListContainer.findViewById(R.id.apps_list); 347 mRecyclerView.setLayoutManager(new LinearLayoutManager( 348 getContext(), RecyclerView.VERTICAL, false /* reverseLayout */)); 349 mRecyclerView.setAdapter(mApplications); 350 } 351 352 // We have to do this now because PreferenceFrameLayout looks at it 353 // only when the view is added. 354 if (container instanceof PreferenceFrameLayout) { 355 ((PreferenceFrameLayout.LayoutParams) mRootView.getLayoutParams()).removeBorders = true; 356 } 357 358 createHeader(); 359 360 mResetAppsHelper.onRestoreInstanceState(savedInstanceState); 361 362 return mRootView; 363 } 364 365 @VisibleForTesting 366 void createHeader() { 367 final Activity activity = getActivity(); 368 final FrameLayout pinnedHeader = mRootView.findViewById(R.id.pinned_header); 369 mSpinnerHeader = activity.getLayoutInflater() 370 .inflate(R.layout.apps_filter_spinner, pinnedHeader, false); 371 mFilterSpinner = mSpinnerHeader.findViewById(R.id.filter_spinner); 372 mFilterAdapter = new FilterSpinnerAdapter(this); 373 mFilterSpinner.setAdapter(mFilterAdapter); 374 mFilterSpinner.setOnItemSelectedListener(this); 375 pinnedHeader.addView(mSpinnerHeader, 0); 376 377 final AppFilterRegistry appFilterRegistry = AppFilterRegistry.getInstance(); 378 mFilterAdapter.enableFilter(appFilterRegistry.getDefaultFilterType(mListType)); 379 if (mListType == LIST_TYPE_MAIN) { 380 if (UserManager.get(getActivity()).getUserProfiles().size() > 1) { 381 mFilterAdapter.enableFilter(FILTER_APPS_PERSONAL); 382 mFilterAdapter.enableFilter(FILTER_APPS_WORK); 383 } 384 } 385 if (mListType == LIST_TYPE_NOTIFICATION) { 386 mFilterAdapter.enableFilter(FILTER_APPS_BLOCKED); 387 } 388 if (mListType == LIST_TYPE_HIGH_POWER) { 389 mFilterAdapter.enableFilter(FILTER_APPS_POWER_WHITELIST_ALL); 390 } 391 392 AppFilter compositeFilter = getCompositeFilter(mListType, mStorageType, mVolumeUuid); 393 if (mIsWorkOnly) { 394 final AppFilter workFilter = appFilterRegistry.get(FILTER_APPS_WORK).getFilter(); 395 compositeFilter = new CompoundFilter(compositeFilter, workFilter); 396 } 397 if (compositeFilter != null) { 398 mApplications.setCompositeFilter(compositeFilter); 399 } 400 } 401 402 @VisibleForTesting 403 @Nullable 404 static AppFilter getCompositeFilter(int listType, int storageType, String volumeUuid) { 405 AppFilter filter = new VolumeFilter(volumeUuid); 406 if (listType == LIST_TYPE_STORAGE) { 407 if (storageType == STORAGE_TYPE_MUSIC) { 408 filter = new CompoundFilter(ApplicationsState.FILTER_AUDIO, filter); 409 } else if (storageType == STORAGE_TYPE_DEFAULT) { 410 filter = new CompoundFilter(ApplicationsState.FILTER_OTHER_APPS, filter); 411 } 412 return filter; 413 } 414 if (listType == LIST_TYPE_GAMES) { 415 return new CompoundFilter(ApplicationsState.FILTER_GAMES, filter); 416 } else if (listType == LIST_TYPE_MOVIES) { 417 return new CompoundFilter(ApplicationsState.FILTER_MOVIES, filter); 418 } else if (listType == LIST_TYPE_PHOTOGRAPHY) { 419 return new CompoundFilter(ApplicationsState.FILTER_PHOTOS, filter); 420 } 421 422 return null; 423 } 424 425 @Override 426 public int getMetricsCategory() { 427 switch (mListType) { 428 case LIST_TYPE_MAIN: 429 return MetricsEvent.MANAGE_APPLICATIONS; 430 case LIST_TYPE_NOTIFICATION: 431 return MetricsEvent.MANAGE_APPLICATIONS_NOTIFICATIONS; 432 case LIST_TYPE_STORAGE: 433 if (mStorageType == STORAGE_TYPE_MUSIC) { 434 return MetricsEvent.APPLICATIONS_STORAGE_MUSIC; 435 } 436 return MetricsEvent.APPLICATIONS_STORAGE_APPS; 437 case LIST_TYPE_GAMES: 438 return MetricsEvent.APPLICATIONS_STORAGE_GAMES; 439 case LIST_TYPE_MOVIES: 440 return MetricsEvent.APPLICATIONS_STORAGE_MOVIES; 441 case LIST_TYPE_PHOTOGRAPHY: 442 return MetricsEvent.APPLICATIONS_STORAGE_PHOTOS; 443 case LIST_TYPE_USAGE_ACCESS: 444 return MetricsEvent.USAGE_ACCESS; 445 case LIST_TYPE_HIGH_POWER: 446 return MetricsEvent.APPLICATIONS_HIGH_POWER_APPS; 447 case LIST_TYPE_OVERLAY: 448 return MetricsEvent.SYSTEM_ALERT_WINDOW_APPS; 449 case LIST_TYPE_WRITE_SETTINGS: 450 return MetricsEvent.SYSTEM_ALERT_WINDOW_APPS; 451 case LIST_TYPE_MANAGE_SOURCES: 452 return MetricsEvent.MANAGE_EXTERNAL_SOURCES; 453 case LIST_TYPE_DIRECTORY_ACCESS: 454 return MetricsEvent.DIRECTORY_ACCESS; 455 case LIST_TYPE_WIFI_ACCESS: 456 return MetricsEvent.CONFIGURE_WIFI; 457 default: 458 return MetricsEvent.VIEW_UNKNOWN; 459 } 460 } 461 462 @Override 463 public void onStart() { 464 super.onStart(); 465 updateView(); 466 if (mApplications != null) { 467 mApplications.resume(mSortOrder); 468 mApplications.updateLoading(); 469 } 470 } 471 472 @Override 473 public void onSaveInstanceState(Bundle outState) { 474 super.onSaveInstanceState(outState); 475 mResetAppsHelper.onSaveInstanceState(outState); 476 outState.putInt(EXTRA_SORT_ORDER, mSortOrder); 477 outState.putBoolean(EXTRA_SHOW_SYSTEM, mShowSystem); 478 outState.putBoolean(EXTRA_HAS_ENTRIES, mApplications.mHasReceivedLoadEntries); 479 outState.putBoolean(EXTRA_HAS_BRIDGE, mApplications.mHasReceivedBridgeCallback); 480 if (mApplications != null) { 481 mApplications.onSaveInstanceState(outState); 482 } 483 } 484 485 @Override 486 public void onStop() { 487 super.onStop(); 488 if (mApplications != null) { 489 mApplications.pause(); 490 } 491 mResetAppsHelper.stop(); 492 } 493 494 @Override 495 public void onDestroyView() { 496 super.onDestroyView(); 497 498 if (mApplications != null) { 499 mApplications.release(); 500 } 501 mRootView = null; 502 } 503 504 @Override 505 public void onActivityResult(int requestCode, int resultCode, Intent data) { 506 if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) { 507 if (mListType == LIST_TYPE_NOTIFICATION) { 508 mApplications.mExtraInfoBridge.forceUpdate(mCurrentPkgName, mCurrentUid); 509 } else if (mListType == LIST_TYPE_HIGH_POWER || mListType == LIST_TYPE_OVERLAY 510 || mListType == LIST_TYPE_WRITE_SETTINGS) { 511 mApplications.mExtraInfoBridge.forceUpdate(mCurrentPkgName, mCurrentUid); 512 } else { 513 mApplicationsState.requestSize(mCurrentPkgName, UserHandle.getUserId(mCurrentUid)); 514 } 515 } 516 } 517 518 // utility method used to start sub activity 519 private void startApplicationDetailsActivity() { 520 switch (mListType) { 521 case LIST_TYPE_NOTIFICATION: 522 startAppInfoFragment(AppNotificationSettings.class, R.string.notifications_title); 523 break; 524 case LIST_TYPE_USAGE_ACCESS: 525 startAppInfoFragment(UsageAccessDetails.class, R.string.usage_access); 526 break; 527 case LIST_TYPE_STORAGE: 528 startAppInfoFragment(AppStorageSettings.class, R.string.storage_settings); 529 break; 530 case LIST_TYPE_HIGH_POWER: 531 HighPowerDetail.show(this, mCurrentPkgName, INSTALLED_APP_DETAILS); 532 break; 533 case LIST_TYPE_OVERLAY: 534 startAppInfoFragment(DrawOverlayDetails.class, R.string.overlay_settings); 535 break; 536 case LIST_TYPE_WRITE_SETTINGS: 537 startAppInfoFragment(WriteSettingsDetails.class, R.string.write_system_settings); 538 break; 539 case LIST_TYPE_MANAGE_SOURCES: 540 startAppInfoFragment(ExternalSourcesDetails.class, R.string.install_other_apps); 541 break; 542 case LIST_TYPE_GAMES: 543 startAppInfoFragment(AppStorageSettings.class, R.string.game_storage_settings); 544 break; 545 case LIST_TYPE_MOVIES: 546 startAppInfoFragment(AppStorageSettings.class, R.string.storage_movies_tv); 547 break; 548 case LIST_TYPE_PHOTOGRAPHY: 549 startAppInfoFragment(AppStorageSettings.class, R.string.storage_photos_videos); 550 break; 551 case LIST_TYPE_DIRECTORY_ACCESS: 552 startAppInfoFragment(DirectoryAccessDetails.class, R.string.directory_access); 553 break; 554 case LIST_TYPE_WIFI_ACCESS: 555 startAppInfoFragment(ChangeWifiStateDetails.class, 556 R.string.change_wifi_state_title); 557 break; 558 // TODO: Figure out if there is a way where we can spin up the profile's settings 559 // process ahead of time, to avoid a long load of data when user clicks on a managed 560 // app. Maybe when they load the list of apps that contains managed profile apps. 561 default: 562 startAppInfoFragment( 563 AppInfoDashboardFragment.class, R.string.application_info_label); 564 break; 565 } 566 } 567 568 private void startAppInfoFragment(Class<?> fragment, int titleRes) { 569 AppInfoBase.startAppInfoFragment(fragment, titleRes, mCurrentPkgName, mCurrentUid, this, 570 INSTALLED_APP_DETAILS, getMetricsCategory()); 571 } 572 573 @Override 574 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 575 final Activity activity = getActivity(); 576 if (activity == null) { 577 return; 578 } 579 HelpUtils.prepareHelpMenuItem(activity, menu, getHelpResource(), getClass().getName()); 580 mOptionsMenu = menu; 581 inflater.inflate(R.menu.manage_apps, menu); 582 updateOptionsMenu(); 583 } 584 585 @Override 586 public void onPrepareOptionsMenu(Menu menu) { 587 updateOptionsMenu(); 588 } 589 590 @Override 591 public void onDestroyOptionsMenu() { 592 mOptionsMenu = null; 593 } 594 595 @StringRes 596 int getHelpResource() { 597 if (mListType == LIST_TYPE_MAIN) { 598 return R.string.help_uri_apps; 599 } else if (mListType == LIST_TYPE_USAGE_ACCESS) { 600 return R.string.help_url_usage_access; 601 } else { 602 return R.string.help_uri_notifications; 603 } 604 } 605 606 void updateOptionsMenu() { 607 if (mOptionsMenu == null) { 608 return; 609 } 610 mOptionsMenu.findItem(R.id.advanced).setVisible(false); 611 612 mOptionsMenu.findItem(R.id.sort_order_alpha).setVisible(mListType == LIST_TYPE_STORAGE 613 && mSortOrder != R.id.sort_order_alpha); 614 mOptionsMenu.findItem(R.id.sort_order_size).setVisible(mListType == LIST_TYPE_STORAGE 615 && mSortOrder != R.id.sort_order_size); 616 617 mOptionsMenu.findItem(R.id.show_system).setVisible(!mShowSystem 618 && mListType != LIST_TYPE_HIGH_POWER); 619 mOptionsMenu.findItem(R.id.hide_system).setVisible(mShowSystem 620 && mListType != LIST_TYPE_HIGH_POWER); 621 622 mOptionsMenu.findItem(R.id.reset_app_preferences).setVisible(mListType == LIST_TYPE_MAIN); 623 } 624 625 @Override 626 public boolean onOptionsItemSelected(MenuItem item) { 627 int menuId = item.getItemId(); 628 switch (item.getItemId()) { 629 case R.id.sort_order_alpha: 630 case R.id.sort_order_size: 631 mSortOrder = menuId; 632 if (mApplications != null) { 633 mApplications.rebuild(mSortOrder); 634 } 635 break; 636 case R.id.show_system: 637 case R.id.hide_system: 638 mShowSystem = !mShowSystem; 639 mApplications.rebuild(); 640 break; 641 case R.id.reset_app_preferences: 642 mResetAppsHelper.buildResetDialog(); 643 return true; 644 case R.id.advanced: 645 if (mListType == LIST_TYPE_NOTIFICATION) { 646 new SubSettingLauncher(getContext()) 647 .setDestination(ConfigureNotificationSettings.class.getName()) 648 .setTitle(R.string.configure_notification_settings) 649 .setSourceMetricsCategory(getMetricsCategory()) 650 .setResultListener(this, ADVANCED_SETTINGS) 651 .launch(); 652 } else { 653 new SubSettingLauncher(getContext()) 654 .setDestination(DefaultAppSettings.class.getName()) 655 .setTitle(R.string.configure_apps) 656 .setSourceMetricsCategory(getMetricsCategory()) 657 .setResultListener(this, ADVANCED_SETTINGS) 658 .launch(); 659 } 660 return true; 661 default: 662 // Handle the home button 663 return false; 664 } 665 updateOptionsMenu(); 666 return true; 667 } 668 669 @Override 670 public void onClick(View view) { 671 if (mApplications == null) { 672 return; 673 } 674 final int position = mRecyclerView.getChildAdapterPosition(view); 675 676 if (position == RecyclerView.NO_POSITION) { 677 Log.w(TAG, "Cannot find position for child, skipping onClick handling"); 678 return; 679 } 680 if (mApplications.getApplicationCount() > position) { 681 ApplicationsState.AppEntry entry = mApplications.getAppEntry(position); 682 mCurrentPkgName = entry.info.packageName; 683 mCurrentUid = entry.info.uid; 684 startApplicationDetailsActivity(); 685 } else { 686 mApplications.mExtraViewController.onClick(this); 687 } 688 } 689 690 @Override 691 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 692 mFilter = mFilterAdapter.getFilter(position); 693 mApplications.setFilter(mFilter); 694 if (DEBUG) Log.d(TAG, "Selecting filter " + mFilter); 695 } 696 697 @Override 698 public void onNothingSelected(AdapterView<?> parent) { 699 } 700 701 public void updateView() { 702 updateOptionsMenu(); 703 final Activity host = getActivity(); 704 if (host != null) { 705 host.invalidateOptionsMenu(); 706 } 707 } 708 709 public void setHasDisabled(boolean hasDisabledApps) { 710 if (mListType != LIST_TYPE_MAIN) { 711 return; 712 } 713 mFilterAdapter.setFilterEnabled(FILTER_APPS_ENABLED, hasDisabledApps); 714 mFilterAdapter.setFilterEnabled(FILTER_APPS_DISABLED, hasDisabledApps); 715 } 716 717 public void setHasInstant(boolean haveInstantApps) { 718 if (LIST_TYPES_WITH_INSTANT.contains(mListType)) { 719 mFilterAdapter.setFilterEnabled(FILTER_APPS_INSTANT, haveInstantApps); 720 } 721 } 722 723 static class FilterSpinnerAdapter extends ArrayAdapter<CharSequence> { 724 725 private final ManageApplications mManageApplications; 726 private final Context mContext; 727 728 // Use ArrayAdapter for view logic, but have our own list for managing 729 // the options available. 730 private final ArrayList<AppFilterItem> mFilterOptions = new ArrayList<>(); 731 732 public FilterSpinnerAdapter(ManageApplications manageApplications) { 733 super(manageApplications.getContext(), R.layout.filter_spinner_item); 734 mContext = manageApplications.getContext(); 735 mManageApplications = manageApplications; 736 setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 737 } 738 739 public AppFilterItem getFilter(int position) { 740 return mFilterOptions.get(position); 741 } 742 743 public void setFilterEnabled(@AppFilterRegistry.FilterType int filter, boolean enabled) { 744 if (enabled) { 745 enableFilter(filter); 746 } else { 747 disableFilter(filter); 748 } 749 } 750 751 public void enableFilter(@AppFilterRegistry.FilterType int filterType) { 752 final AppFilterItem filter = AppFilterRegistry.getInstance().get(filterType); 753 if (mFilterOptions.contains(filter)) { 754 return; 755 } 756 if (DEBUG) { 757 Log.d(TAG, "Enabling filter " + filter); 758 } 759 mFilterOptions.add(filter); 760 Collections.sort(mFilterOptions); 761 mManageApplications.mSpinnerHeader.setVisibility( 762 mFilterOptions.size() > 1 ? View.VISIBLE : View.GONE); 763 notifyDataSetChanged(); 764 if (mFilterOptions.size() == 1) { 765 if (DEBUG) { 766 Log.d(TAG, "Auto selecting filter " + filter); 767 } 768 mManageApplications.mFilterSpinner.setSelection(0); 769 mManageApplications.onItemSelected(null, null, 0, 0); 770 } 771 } 772 773 public void disableFilter(@AppFilterRegistry.FilterType int filterType) { 774 final AppFilterItem filter = AppFilterRegistry.getInstance().get(filterType); 775 if (!mFilterOptions.remove(filter)) { 776 return; 777 } 778 if (DEBUG) { 779 Log.d(TAG, "Disabling filter " + filter); 780 } 781 Collections.sort(mFilterOptions); 782 mManageApplications.mSpinnerHeader.setVisibility( 783 mFilterOptions.size() > 1 ? View.VISIBLE : View.GONE); 784 notifyDataSetChanged(); 785 if (mManageApplications.mFilter == filter) { 786 if (mFilterOptions.size() > 0) { 787 if (DEBUG) { 788 Log.d(TAG, "Auto selecting filter " + mFilterOptions.get(0)); 789 } 790 mManageApplications.mFilterSpinner.setSelection(0); 791 mManageApplications.onItemSelected(null, null, 0, 0); 792 } 793 } 794 } 795 796 @Override 797 public int getCount() { 798 return mFilterOptions.size(); 799 } 800 801 @Override 802 public CharSequence getItem(int position) { 803 return mContext.getText(mFilterOptions.get(position).getTitle()); 804 } 805 } 806 807 static class ApplicationsAdapter extends RecyclerView.Adapter<ApplicationViewHolder> 808 implements ApplicationsState.Callbacks, AppStateBaseBridge.Callback { 809 810 private static final String STATE_LAST_SCROLL_INDEX = "state_last_scroll_index"; 811 private static final int VIEW_TYPE_APP = 0; 812 private static final int VIEW_TYPE_EXTRA_VIEW = 1; 813 814 private final ApplicationsState mState; 815 private final ApplicationsState.Session mSession; 816 private final ManageApplications mManageApplications; 817 private final Context mContext; 818 private final AppStateBaseBridge mExtraInfoBridge; 819 private final LoadingViewController mLoadingViewController; 820 821 private AppFilterItem mAppFilter; 822 private ArrayList<ApplicationsState.AppEntry> mEntries; 823 private boolean mResumed; 824 private int mLastSortMode = -1; 825 private int mWhichSize = SIZE_TOTAL; 826 private AppFilter mCompositeFilter; 827 private boolean mHasReceivedLoadEntries; 828 private boolean mHasReceivedBridgeCallback; 829 private FileViewHolderController mExtraViewController; 830 831 // These two variables are used to remember and restore the last scroll position when this 832 // fragment is paused. We need this special handling because app entries are added gradually 833 // when we rebuild the list after the user made some changes, like uninstalling an app. 834 private int mLastIndex = -1; 835 836 public ApplicationsAdapter(ApplicationsState state, ManageApplications manageApplications, 837 AppFilterItem appFilter, Bundle savedInstanceState) { 838 setHasStableIds(true); 839 mState = state; 840 mSession = state.newSession(this); 841 mManageApplications = manageApplications; 842 mLoadingViewController = new LoadingViewController( 843 mManageApplications.mLoadingContainer, 844 mManageApplications.mListContainer 845 ); 846 mContext = manageApplications.getActivity(); 847 mAppFilter = appFilter; 848 if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) { 849 mExtraInfoBridge = new AppStateNotificationBridge(mContext, mState, this, 850 manageApplications.mNotifBackend); 851 } else if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) { 852 mExtraInfoBridge = new AppStateUsageBridge(mContext, mState, this); 853 } else if (mManageApplications.mListType == LIST_TYPE_HIGH_POWER) { 854 mExtraInfoBridge = new AppStatePowerBridge(mState, this); 855 } else if (mManageApplications.mListType == LIST_TYPE_OVERLAY) { 856 mExtraInfoBridge = new AppStateOverlayBridge(mContext, mState, this); 857 } else if (mManageApplications.mListType == LIST_TYPE_WRITE_SETTINGS) { 858 mExtraInfoBridge = new AppStateWriteSettingsBridge(mContext, mState, this); 859 } else if (mManageApplications.mListType == LIST_TYPE_MANAGE_SOURCES) { 860 mExtraInfoBridge = new AppStateInstallAppsBridge(mContext, mState, this); 861 } else if (mManageApplications.mListType == LIST_TYPE_DIRECTORY_ACCESS) { 862 mExtraInfoBridge = new AppStateDirectoryAccessBridge(mState, this); 863 } else if (mManageApplications.mListType == LIST_TYPE_WIFI_ACCESS) { 864 mExtraInfoBridge = new AppStateChangeWifiStateBridge(mContext, mState, this); 865 } else { 866 mExtraInfoBridge = null; 867 } 868 if (savedInstanceState != null) { 869 mLastIndex = savedInstanceState.getInt(STATE_LAST_SCROLL_INDEX); 870 } 871 } 872 873 public void setCompositeFilter(AppFilter compositeFilter) { 874 mCompositeFilter = compositeFilter; 875 rebuild(); 876 } 877 878 public void setFilter(AppFilterItem appFilter) { 879 mAppFilter = appFilter; 880 rebuild(); 881 } 882 883 public void setExtraViewController(FileViewHolderController extraViewController) { 884 mExtraViewController = extraViewController; 885 // Start to query extra view's stats on background, and once done post result to main 886 // thread. 887 ThreadUtils.postOnBackgroundThread(() -> { 888 mExtraViewController.queryStats(); 889 ThreadUtils.postOnMainThread(() -> { 890 onExtraViewCompleted(); 891 }); 892 }); 893 } 894 895 public void resume(int sort) { 896 if (DEBUG) Log.i(TAG, "Resume! mResumed=" + mResumed); 897 if (!mResumed) { 898 mResumed = true; 899 mSession.onResume(); 900 mLastSortMode = sort; 901 if (mExtraInfoBridge != null) { 902 mExtraInfoBridge.resume(); 903 } 904 rebuild(); 905 } else { 906 rebuild(sort); 907 } 908 } 909 910 public void pause() { 911 if (mResumed) { 912 mResumed = false; 913 mSession.onPause(); 914 if (mExtraInfoBridge != null) { 915 mExtraInfoBridge.pause(); 916 } 917 } 918 } 919 920 public void onSaveInstanceState(Bundle outState) { 921 // Record the current scroll position before pausing. 922 final LinearLayoutManager layoutManager = 923 (LinearLayoutManager) mManageApplications.mRecyclerView.getLayoutManager(); 924 outState.putInt(STATE_LAST_SCROLL_INDEX, layoutManager.findFirstVisibleItemPosition()); 925 } 926 927 public void release() { 928 mSession.onDestroy(); 929 if (mExtraInfoBridge != null) { 930 mExtraInfoBridge.release(); 931 } 932 } 933 934 public void rebuild(int sort) { 935 if (sort == mLastSortMode) { 936 return; 937 } 938 mLastSortMode = sort; 939 rebuild(); 940 } 941 942 @Override 943 public ApplicationViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 944 final View view = ApplicationViewHolder.newView(parent); 945 return new ApplicationViewHolder(view, 946 shouldUseStableItemHeight(mManageApplications.mListType)); 947 } 948 949 @Override 950 public int getItemViewType(int position) { 951 boolean isLastItem = (getItemCount() - 1) == position; 952 return hasExtraView() && isLastItem 953 ? VIEW_TYPE_EXTRA_VIEW 954 : VIEW_TYPE_APP; 955 } 956 957 public void rebuild() { 958 if (!mHasReceivedLoadEntries 959 || (mExtraInfoBridge != null && !mHasReceivedBridgeCallback)) { 960 // Don't rebuild the list until all the app entries are loaded. 961 return; 962 } 963 ApplicationsState.AppFilter filterObj; 964 Comparator<AppEntry> comparatorObj; 965 boolean emulated = Environment.isExternalStorageEmulated(); 966 if (emulated) { 967 mWhichSize = SIZE_TOTAL; 968 } else { 969 mWhichSize = SIZE_INTERNAL; 970 } 971 filterObj = mAppFilter.getFilter(); 972 if (mCompositeFilter != null) { 973 filterObj = new CompoundFilter(filterObj, mCompositeFilter); 974 } 975 if (!mManageApplications.mShowSystem) { 976 if (LIST_TYPES_WITH_INSTANT.contains(mManageApplications.mListType)) { 977 filterObj = new CompoundFilter(filterObj, 978 ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT); 979 } else { 980 filterObj = new CompoundFilter(filterObj, 981 ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER); 982 } 983 } 984 switch (mLastSortMode) { 985 case R.id.sort_order_size: 986 switch (mWhichSize) { 987 case SIZE_INTERNAL: 988 comparatorObj = ApplicationsState.INTERNAL_SIZE_COMPARATOR; 989 break; 990 case SIZE_EXTERNAL: 991 comparatorObj = ApplicationsState.EXTERNAL_SIZE_COMPARATOR; 992 break; 993 default: 994 comparatorObj = ApplicationsState.SIZE_COMPARATOR; 995 break; 996 } 997 break; 998 default: 999 comparatorObj = ApplicationsState.ALPHA_COMPARATOR; 1000 break; 1001 } 1002 1003 filterObj = new CompoundFilter(filterObj, ApplicationsState.FILTER_NOT_HIDE); 1004 AppFilter finalFilterObj = filterObj; 1005 ThreadUtils.postOnBackgroundThread(() -> { 1006 final ArrayList<AppEntry> entries = mSession.rebuild(finalFilterObj, 1007 comparatorObj, false); 1008 if (entries != null) { 1009 ThreadUtils.postOnMainThread(() -> onRebuildComplete(entries)); 1010 } 1011 }); 1012 } 1013 1014 @VisibleForTesting 1015 static boolean shouldUseStableItemHeight(int listType) { 1016 switch (listType) { 1017 case LIST_TYPE_NOTIFICATION: 1018 // Most entries in notification type has no summary. Don't use stable height 1019 // so height is short for most entries. 1020 return false; 1021 default: 1022 // Other types have non-empty summary, so keep the height as we expect summary 1023 // to fill in. 1024 return true; 1025 } 1026 } 1027 1028 private static boolean packageNameEquals(PackageItemInfo info1, PackageItemInfo info2) { 1029 if (info1 == null || info2 == null) { 1030 return false; 1031 } 1032 if (info1.packageName == null || info2.packageName == null) { 1033 return false; 1034 } 1035 return info1.packageName.equals(info2.packageName); 1036 } 1037 1038 private ArrayList<ApplicationsState.AppEntry> removeDuplicateIgnoringUser( 1039 ArrayList<ApplicationsState.AppEntry> entries) { 1040 int size = entries.size(); 1041 // returnList will not have more entries than entries 1042 ArrayList<ApplicationsState.AppEntry> returnEntries = new ArrayList<>(size); 1043 1044 // assume appinfo of same package but different users are grouped together 1045 PackageItemInfo lastInfo = null; 1046 for (int i = 0; i < size; i++) { 1047 AppEntry appEntry = entries.get(i); 1048 PackageItemInfo info = appEntry.info; 1049 if (!packageNameEquals(lastInfo, appEntry.info)) { 1050 returnEntries.add(appEntry); 1051 } 1052 lastInfo = info; 1053 } 1054 returnEntries.trimToSize(); 1055 return returnEntries; 1056 } 1057 1058 @Override 1059 public void onRebuildComplete(ArrayList<AppEntry> entries) { 1060 final int filterType = mAppFilter.getFilterType(); 1061 if (filterType == FILTER_APPS_POWER_WHITELIST || 1062 filterType == FILTER_APPS_POWER_WHITELIST_ALL) { 1063 entries = removeDuplicateIgnoringUser(entries); 1064 } 1065 mEntries = entries; 1066 notifyDataSetChanged(); 1067 if (getItemCount() == 0) { 1068 mManageApplications.mRecyclerView.setVisibility(View.GONE); 1069 mManageApplications.mEmptyView.setVisibility(View.VISIBLE); 1070 } else { 1071 mManageApplications.mEmptyView.setVisibility(View.GONE); 1072 mManageApplications.mRecyclerView.setVisibility(View.VISIBLE); 1073 } 1074 // Restore the last scroll position if the number of entries added so far is bigger than 1075 // it. 1076 if (mLastIndex != -1 && getItemCount() > mLastIndex) { 1077 mManageApplications.mRecyclerView.getLayoutManager().scrollToPosition(mLastIndex); 1078 mLastIndex = -1; 1079 } 1080 1081 if (mSession.getAllApps().size() != 0 1082 && mManageApplications.mListContainer.getVisibility() != View.VISIBLE) { 1083 mLoadingViewController.showContent(true /* animate */); 1084 } 1085 if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) { 1086 // No enabled or disabled filters for usage access. 1087 return; 1088 } 1089 1090 mManageApplications.setHasDisabled(mState.haveDisabledApps()); 1091 mManageApplications.setHasInstant(mState.haveInstantApps()); 1092 } 1093 1094 @VisibleForTesting 1095 void updateLoading() { 1096 final boolean appLoaded = mHasReceivedLoadEntries && mSession.getAllApps().size() != 0; 1097 if (appLoaded) { 1098 mLoadingViewController.showContent(false /* animate */); 1099 } else { 1100 mLoadingViewController.showLoadingViewDelayed(); 1101 } 1102 } 1103 1104 @Override 1105 public void onExtraInfoUpdated() { 1106 mHasReceivedBridgeCallback = true; 1107 rebuild(); 1108 } 1109 1110 @Override 1111 public void onRunningStateChanged(boolean running) { 1112 mManageApplications.getActivity().setProgressBarIndeterminateVisibility(running); 1113 } 1114 1115 @Override 1116 public void onPackageListChanged() { 1117 rebuild(); 1118 } 1119 1120 @Override 1121 public void onPackageIconChanged() { 1122 // We ensure icons are loaded when their item is displayed, so 1123 // don't care about icons loaded in the background. 1124 } 1125 1126 @Override 1127 public void onLoadEntriesCompleted() { 1128 mHasReceivedLoadEntries = true; 1129 // We may have been skipping rebuilds until this came in, trigger one now. 1130 rebuild(); 1131 } 1132 1133 @Override 1134 public void onPackageSizeChanged(String packageName) { 1135 if (mEntries == null) { 1136 return; 1137 } 1138 final int size = mEntries.size(); 1139 for (int i = 0; i < size; i++) { 1140 final AppEntry entry = mEntries.get(i); 1141 final ApplicationInfo info = entry.info; 1142 if (info == null && !TextUtils.equals(packageName, info.packageName)) { 1143 continue; 1144 } 1145 if (TextUtils.equals(mManageApplications.mCurrentPkgName, info.packageName)) { 1146 // We got the size information for the last app the 1147 // user viewed, and are sorting by size... they may 1148 // have cleared data, so we immediately want to resort 1149 // the list with the new size to reflect it to the user. 1150 rebuild(); 1151 return; 1152 } else { 1153 notifyItemChanged(i); 1154 } 1155 1156 } 1157 } 1158 1159 @Override 1160 public void onLauncherInfoChanged() { 1161 if (!mManageApplications.mShowSystem) { 1162 rebuild(); 1163 } 1164 } 1165 1166 @Override 1167 public void onAllSizesComputed() { 1168 if (mLastSortMode == R.id.sort_order_size) { 1169 rebuild(); 1170 } 1171 } 1172 1173 public void onExtraViewCompleted() { 1174 if (!hasExtraView()) { 1175 return; 1176 } 1177 // Update last item - this is assumed to be the extra view. 1178 notifyItemChanged(getItemCount() - 1); 1179 } 1180 1181 @Override 1182 public int getItemCount() { 1183 if (mEntries == null) { 1184 return 0; 1185 } 1186 return mEntries.size() + (hasExtraView() ? 1 : 0); 1187 } 1188 1189 public int getApplicationCount() { 1190 return mEntries != null ? mEntries.size() : 0; 1191 } 1192 1193 public AppEntry getAppEntry(int position) { 1194 return mEntries.get(position); 1195 } 1196 1197 @Override 1198 public long getItemId(int position) { 1199 if (position == mEntries.size()) { 1200 return -1; 1201 } 1202 return mEntries.get(position).id; 1203 } 1204 1205 public boolean isEnabled(int position) { 1206 if (getItemViewType(position) == VIEW_TYPE_EXTRA_VIEW 1207 || mManageApplications.mListType != LIST_TYPE_HIGH_POWER) { 1208 return true; 1209 } 1210 ApplicationsState.AppEntry entry = mEntries.get(position); 1211 return !PowerWhitelistBackend.getInstance().isSysWhitelisted(entry.info.packageName); 1212 } 1213 1214 @Override 1215 public void onBindViewHolder(ApplicationViewHolder holder, int position) { 1216 if (mEntries != null && mExtraViewController != null && position == mEntries.size()) { 1217 // set up view for extra view controller 1218 mExtraViewController.setupView(holder); 1219 } else { 1220 // Bind the data efficiently with the holder 1221 ApplicationsState.AppEntry entry = mEntries.get(position); 1222 synchronized (entry) { 1223 holder.setTitle(entry.label); 1224 mState.ensureIcon(entry); 1225 holder.setIcon(entry.icon); 1226 updateSummary(holder, entry); 1227 holder.updateDisableView(entry.info); 1228 } 1229 holder.setEnabled(isEnabled(position)); 1230 } 1231 holder.itemView.setOnClickListener(mManageApplications); 1232 } 1233 1234 private void updateSummary(ApplicationViewHolder holder, AppEntry entry) { 1235 switch (mManageApplications.mListType) { 1236 case LIST_TYPE_NOTIFICATION: 1237 if (entry.extraInfo != null) { 1238 holder.setSummary( 1239 AppNotificationPreferenceController.getNotificationSummary( 1240 (AppRow) entry.extraInfo, mContext)); 1241 } else { 1242 holder.setSummary(null); 1243 } 1244 break; 1245 case LIST_TYPE_USAGE_ACCESS: 1246 if (entry.extraInfo != null) { 1247 holder.setSummary( 1248 (new UsageState((PermissionState) entry.extraInfo)).isPermissible() 1249 ? R.string.app_permission_summary_allowed 1250 : R.string.app_permission_summary_not_allowed); 1251 } else { 1252 holder.setSummary(null); 1253 } 1254 break; 1255 case LIST_TYPE_HIGH_POWER: 1256 holder.setSummary(HighPowerDetail.getSummary(mContext, entry)); 1257 break; 1258 case LIST_TYPE_OVERLAY: 1259 holder.setSummary(DrawOverlayDetails.getSummary(mContext, entry)); 1260 break; 1261 case LIST_TYPE_WRITE_SETTINGS: 1262 holder.setSummary(WriteSettingsDetails.getSummary(mContext, entry)); 1263 break; 1264 case LIST_TYPE_MANAGE_SOURCES: 1265 holder.setSummary(ExternalSourcesDetails.getPreferenceSummary(mContext, entry)); 1266 break; 1267 case LIST_TYPE_DIRECTORY_ACCESS: 1268 holder.setSummary(null); 1269 break; 1270 case LIST_TYPE_WIFI_ACCESS: 1271 holder.setSummary(ChangeWifiStateDetails.getSummary(mContext, entry)); 1272 break; 1273 default: 1274 holder.updateSizeText(entry, mManageApplications.mInvalidSizeStr, mWhichSize); 1275 break; 1276 } 1277 } 1278 1279 private boolean hasExtraView() { 1280 return mExtraViewController != null 1281 && mExtraViewController.shouldShow(); 1282 } 1283 } 1284 1285 private static class SummaryProvider implements SummaryLoader.SummaryProvider { 1286 1287 private final Context mContext; 1288 private final SummaryLoader mLoader; 1289 private ApplicationsState.Session mSession; 1290 1291 private SummaryProvider(Context context, SummaryLoader loader) { 1292 mContext = context; 1293 mLoader = loader; 1294 } 1295 1296 @Override 1297 public void setListening(boolean listening) { 1298 if (listening) { 1299 new InstalledAppCounter(mContext, InstalledAppCounter.IGNORE_INSTALL_REASON, 1300 new PackageManagerWrapper(mContext.getPackageManager())) { 1301 @Override 1302 protected void onCountComplete(int num) { 1303 mLoader.setSummary(SummaryProvider.this, 1304 mContext.getString(R.string.apps_summary, num)); 1305 } 1306 }.execute(); 1307 } 1308 } 1309 } 1310 1311 public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY 1312 = new SummaryLoader.SummaryProviderFactory() { 1313 @Override 1314 public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity, 1315 SummaryLoader summaryLoader) { 1316 return new SummaryProvider(activity, summaryLoader); 1317 } 1318 }; 1319} 1320