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