1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.applications.manageapplications; 18 19 import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_DRAGGING; 20 import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE; 21 22 import static com.android.internal.jank.InteractionJankMonitor.CUJ_SETTINGS_PAGE_SCROLL; 23 import static com.android.settings.ChangeIds.CHANGE_RESTRICT_SAW_INTENT; 24 import static com.android.settings.Utils.PROPERTY_DELETE_ALL_APP_CLONES_ENABLED; 25 import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_ALL; 26 import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_BATTERY_OPTIMIZED; 27 import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_BATTERY_RESTRICTED; 28 import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_BATTERY_UNRESTRICTED; 29 import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_BLOCKED; 30 import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_DISABLED; 31 import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_ENABLED; 32 import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_FREQUENT; 33 import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_INSTANT; 34 import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_PERSONAL; 35 import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_POWER_ALLOWLIST; 36 import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_POWER_ALLOWLIST_ALL; 37 import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_RECENT; 38 import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_WORK; 39 import static com.android.settings.search.actionbar.SearchMenuController.MENU_SEARCH; 40 41 import android.annotation.StringRes; 42 import android.app.Activity; 43 import android.app.ActivityManager; 44 import android.app.settings.SettingsEnums; 45 import android.app.usage.IUsageStatsManager; 46 import android.content.Context; 47 import android.content.Intent; 48 import android.content.pm.ApplicationInfo; 49 import android.content.pm.PackageItemInfo; 50 import android.content.pm.UserInfo; 51 import android.content.res.Configuration; 52 import android.graphics.drawable.Drawable; 53 import android.net.Uri; 54 import android.os.Build; 55 import android.os.Bundle; 56 import android.os.Environment; 57 import android.os.IBinder; 58 import android.os.IUserManager; 59 import android.os.RemoteException; 60 import android.os.ServiceManager; 61 import android.os.UserHandle; 62 import android.os.UserManager; 63 import android.preference.PreferenceFrameLayout; 64 import android.provider.DeviceConfig; 65 import android.provider.Settings; 66 import android.text.TextUtils; 67 import android.util.ArraySet; 68 import android.util.IconDrawableFactory; 69 import android.util.Log; 70 import android.view.LayoutInflater; 71 import android.view.Menu; 72 import android.view.MenuInflater; 73 import android.view.MenuItem; 74 import android.view.View; 75 import android.view.ViewGroup; 76 import android.view.inputmethod.InputMethodManager; 77 import android.widget.AdapterView; 78 import android.widget.AdapterView.OnItemSelectedListener; 79 import android.widget.Filter; 80 import android.widget.FrameLayout; 81 import android.widget.SearchView; 82 import android.widget.Spinner; 83 import android.widget.Toast; 84 85 import androidx.annotation.NonNull; 86 import androidx.annotation.Nullable; 87 import androidx.annotation.VisibleForTesting; 88 import androidx.annotation.WorkerThread; 89 import androidx.coordinatorlayout.widget.CoordinatorLayout; 90 import androidx.core.view.ViewCompat; 91 import androidx.recyclerview.widget.LinearLayoutManager; 92 import androidx.recyclerview.widget.RecyclerView; 93 94 import com.android.internal.compat.IPlatformCompat; 95 import com.android.internal.jank.InteractionJankMonitor; 96 import com.android.settings.R; 97 import com.android.settings.Settings.AlarmsAndRemindersActivity; 98 import com.android.settings.Settings.AppBatteryUsageActivity; 99 import com.android.settings.Settings.ChangeNfcTagAppsActivity; 100 import com.android.settings.Settings.ChangeWifiStateActivity; 101 import com.android.settings.Settings.ClonedAppsListActivity; 102 import com.android.settings.Settings.HighPowerApplicationsActivity; 103 import com.android.settings.Settings.LongBackgroundTasksActivity; 104 import com.android.settings.Settings.ManageExternalSourcesActivity; 105 import com.android.settings.Settings.ManageExternalStorageActivity; 106 import com.android.settings.Settings.MediaManagementAppsActivity; 107 import com.android.settings.Settings.NotificationAppListActivity; 108 import com.android.settings.Settings.NotificationReviewPermissionsActivity; 109 import com.android.settings.Settings.OverlaySettingsActivity; 110 import com.android.settings.Settings.TurnScreenOnSettingsActivity; 111 import com.android.settings.Settings.UsageAccessSettingsActivity; 112 import com.android.settings.Settings.WriteSettingsActivity; 113 import com.android.settings.SettingsActivity; 114 import com.android.settings.Utils; 115 import com.android.settings.applications.AppInfoBase; 116 import com.android.settings.applications.AppStateAlarmsAndRemindersBridge; 117 import com.android.settings.applications.AppStateAppBatteryUsageBridge; 118 import com.android.settings.applications.AppStateAppOpsBridge.PermissionState; 119 import com.android.settings.applications.AppStateBaseBridge; 120 import com.android.settings.applications.AppStateClonedAppsBridge; 121 import com.android.settings.applications.AppStateInstallAppsBridge; 122 import com.android.settings.applications.AppStateLocaleBridge; 123 import com.android.settings.applications.AppStateLongBackgroundTasksBridge; 124 import com.android.settings.applications.AppStateManageExternalStorageBridge; 125 import com.android.settings.applications.AppStateMediaManagementAppsBridge; 126 import com.android.settings.applications.AppStateNotificationBridge; 127 import com.android.settings.applications.AppStateNotificationBridge.NotificationsSentState; 128 import com.android.settings.applications.AppStateOverlayBridge; 129 import com.android.settings.applications.AppStatePowerBridge; 130 import com.android.settings.applications.AppStateTurnScreenOnBridge; 131 import com.android.settings.applications.AppStateUsageBridge; 132 import com.android.settings.applications.AppStateUsageBridge.UsageState; 133 import com.android.settings.applications.AppStateWriteSettingsBridge; 134 import com.android.settings.applications.AppStorageSettings; 135 import com.android.settings.applications.UsageAccessDetails; 136 import com.android.settings.applications.appinfo.AlarmsAndRemindersDetails; 137 import com.android.settings.applications.appinfo.AppInfoDashboardFragment; 138 import com.android.settings.applications.appinfo.AppLocaleDetails; 139 import com.android.settings.applications.appinfo.DrawOverlayDetails; 140 import com.android.settings.applications.appinfo.ExternalSourcesDetails; 141 import com.android.settings.applications.appinfo.LongBackgroundTasksDetails; 142 import com.android.settings.applications.appinfo.ManageExternalStorageDetails; 143 import com.android.settings.applications.appinfo.MediaManagementAppsDetails; 144 import com.android.settings.applications.appinfo.TurnScreenOnDetails; 145 import com.android.settings.applications.appinfo.WriteSettingsDetails; 146 import com.android.settings.core.InstrumentedFragment; 147 import com.android.settings.core.SubSettingLauncher; 148 import com.android.settings.dashboard.profileselector.ProfileSelectFragment; 149 import com.android.settings.fuelgauge.AdvancedPowerUsageDetail; 150 import com.android.settings.fuelgauge.HighPowerDetail; 151 import com.android.settings.localepicker.AppLocalePickerActivity; 152 import com.android.settings.nfc.AppStateNfcTagAppsBridge; 153 import com.android.settings.nfc.ChangeNfcTagAppsStateDetails; 154 import com.android.settings.notification.ConfigureNotificationSettings; 155 import com.android.settings.notification.NotificationBackend; 156 import com.android.settings.notification.app.AppNotificationSettings; 157 import com.android.settings.spa.SpaActivity; 158 import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider; 159 import com.android.settings.spa.app.appinfo.CloneAppInfoSettingsProvider; 160 import com.android.settings.widget.LoadingViewController; 161 import com.android.settings.wifi.AppStateChangeWifiStateBridge; 162 import com.android.settings.wifi.ChangeWifiStateDetails; 163 import com.android.settingslib.RestrictedLockUtils; 164 import com.android.settingslib.RestrictedLockUtilsInternal; 165 import com.android.settingslib.applications.AppIconCacheManager; 166 import com.android.settingslib.applications.AppUtils; 167 import com.android.settingslib.applications.ApplicationsState; 168 import com.android.settingslib.applications.ApplicationsState.AppEntry; 169 import com.android.settingslib.applications.ApplicationsState.AppFilter; 170 import com.android.settingslib.applications.ApplicationsState.CompoundFilter; 171 import com.android.settingslib.applications.ApplicationsState.VolumeFilter; 172 import com.android.settingslib.fuelgauge.PowerAllowlistBackend; 173 import com.android.settingslib.utils.ThreadUtils; 174 import com.android.settingslib.widget.SettingsSpinnerAdapter; 175 176 import com.google.android.material.appbar.AppBarLayout; 177 178 import java.util.ArrayList; 179 import java.util.Arrays; 180 import java.util.Collections; 181 import java.util.Comparator; 182 import java.util.Set; 183 184 /** 185 * Activity to pick an application that will be used to display installation information and 186 * options to uninstall/delete user data for system applications. This activity 187 * can be launched through Settings or via the ACTION_MANAGE_PACKAGE_STORAGE 188 * intent. 189 */ 190 public class ManageApplications extends InstrumentedFragment 191 implements View.OnClickListener, OnItemSelectedListener, SearchView.OnQueryTextListener, 192 MenuItem.OnActionExpandListener { 193 194 static final String TAG = "ManageApplications"; 195 static final boolean DEBUG = Build.IS_DEBUGGABLE; 196 197 // Intent extras. 198 public static final String EXTRA_CLASSNAME = "classname"; 199 // Used for storage only. 200 public static final String EXTRA_VOLUME_UUID = "volumeUuid"; 201 public static final String EXTRA_VOLUME_NAME = "volumeName"; 202 public static final String EXTRA_STORAGE_TYPE = "storageType"; 203 public static final String EXTRA_WORK_ID = "workId"; 204 205 private static final String EXTRA_SORT_ORDER = "sortOrder"; 206 private static final String EXTRA_SHOW_SYSTEM = "showSystem"; 207 private static final String EXTRA_HAS_ENTRIES = "hasEntries"; 208 private static final String EXTRA_HAS_BRIDGE = "hasBridge"; 209 private static final String EXTRA_FILTER_TYPE = "filterType"; 210 @VisibleForTesting 211 static final String EXTRA_EXPAND_SEARCH_VIEW = "expand_search_view"; 212 213 // attributes used as keys when passing values to AppInfoDashboardFragment activity 214 public static final String APP_CHG = "chg"; 215 216 // constant value that can be used to check return code from sub activity. 217 private static final int INSTALLED_APP_DETAILS = 1; 218 private static final int ADVANCED_SETTINGS = 2; 219 220 public static final int SIZE_TOTAL = 0; 221 public static final int SIZE_INTERNAL = 1; 222 public static final int SIZE_EXTERNAL = 2; 223 224 // Storage types. Used to determine what the extra item in the list of preferences is. 225 public static final int STORAGE_TYPE_DEFAULT = 0; // Show all apps that are not categorized. 226 public static final int STORAGE_TYPE_LEGACY = 1; // Show apps even if they can be categorized. 227 228 // sort order 229 @VisibleForTesting 230 int mSortOrder = R.id.sort_order_alpha; 231 232 // whether showing system apps. 233 private boolean mShowSystem; 234 235 private ApplicationsState mApplicationsState; 236 237 public int mListType; 238 private AppFilterItem mFilter; 239 private ApplicationsAdapter mApplications; 240 241 private View mLoadingContainer; 242 private SearchView mSearchView; 243 244 // Size resource used for packages whose size computation failed for some reason 245 CharSequence mInvalidSizeStr; 246 247 private String mCurrentPkgName; 248 private int mCurrentUid; 249 250 private Menu mOptionsMenu; 251 252 public static final int LIST_TYPE_NONE = -1; 253 public static final int LIST_TYPE_MAIN = 0; 254 public static final int LIST_TYPE_NOTIFICATION = 1; 255 public static final int LIST_TYPE_STORAGE = 3; 256 public static final int LIST_TYPE_USAGE_ACCESS = 4; 257 public static final int LIST_TYPE_HIGH_POWER = 5; 258 public static final int LIST_TYPE_OVERLAY = 6; 259 public static final int LIST_TYPE_WRITE_SETTINGS = 7; 260 public static final int LIST_TYPE_MANAGE_SOURCES = 8; 261 public static final int LIST_TYPE_GAMES = 9; 262 public static final int LIST_TYPE_WIFI_ACCESS = 10; 263 public static final int LIST_MANAGE_EXTERNAL_STORAGE = 11; 264 public static final int LIST_TYPE_ALARMS_AND_REMINDERS = 12; 265 public static final int LIST_TYPE_MEDIA_MANAGEMENT_APPS = 13; 266 public static final int LIST_TYPE_APPS_LOCALE = 14; 267 public static final int LIST_TYPE_BATTERY_OPTIMIZATION = 15; 268 public static final int LIST_TYPE_LONG_BACKGROUND_TASKS = 16; 269 public static final int LIST_TYPE_CLONED_APPS = 17; 270 public static final int LIST_TYPE_NFC_TAG_APPS = 18; 271 public static final int LIST_TYPE_TURN_SCREEN_ON = 19; 272 public static final int LIST_TYPE_USER_ASPECT_RATIO_APPS = 20; 273 public static final int LIST_TYPE_NOTIFICATION_EXCLUDE_SUMMARIZATION = 21; 274 public static final int LIST_TYPE_NOTIFICATION_EXCLUDE_CLASSIFICATION = 22; 275 276 // List types that should show instant apps. 277 public static final Set<Integer> LIST_TYPES_WITH_INSTANT = new ArraySet<>(Arrays.asList( 278 LIST_TYPE_MAIN, 279 LIST_TYPE_STORAGE)); 280 281 @VisibleForTesting 282 View mSpinnerHeader; 283 @VisibleForTesting 284 FilterSpinnerAdapter mFilterAdapter; 285 @VisibleForTesting 286 RecyclerView mRecyclerView; 287 // Whether or not search view is expanded. 288 @VisibleForTesting 289 boolean mExpandSearch; 290 291 private View mRootView; 292 private Spinner mFilterSpinner; 293 private IUsageStatsManager mUsageStatsManager; 294 private UserManager mUserManager; 295 private NotificationBackend mNotificationBackend; 296 private ResetAppsHelper mResetAppsHelper; 297 private String mVolumeUuid; 298 private int mStorageType; 299 private boolean mIsWorkOnly; 300 private boolean mIsPrivateProfileOnly; 301 private int mWorkUserId; 302 private boolean mIsPersonalOnly; 303 private View mEmptyView; 304 private int mFilterType; 305 private AppBarLayout mAppBarLayout; 306 307 @Override onAttach(Context context)308 public void onAttach(Context context) { 309 super.onAttach(context); 310 311 mListType = getListType(); 312 final String spaDestination = ManageApplicationsUtil.getSpaDestination(context, mListType); 313 if (spaDestination != null) { 314 SpaActivity.startSpaActivity(context, spaDestination); 315 getActivity().finish(); 316 } 317 } 318 getListType()319 private int getListType() { 320 Bundle args = getArguments(); 321 final String className = getClassName(getActivity().getIntent(), args); 322 return ManageApplicationsUtil.LIST_TYPE_MAP.getOrDefault(className, LIST_TYPE_MAIN); 323 } 324 325 @Override onCreate(Bundle savedInstanceState)326 public void onCreate(Bundle savedInstanceState) { 327 super.onCreate(savedInstanceState); 328 final Activity activity = getActivity(); 329 if (activity.isFinishing()) { 330 return; 331 } 332 setHasOptionsMenu(true); 333 mUserManager = activity.getSystemService(UserManager.class); 334 mApplicationsState = ApplicationsState.getInstance(activity.getApplication()); 335 336 Intent intent = activity.getIntent(); 337 Bundle args = getArguments(); 338 final int screenTitle = getTitleResId(intent, args); 339 final String className = getClassName(intent, args); 340 switch (mListType) { 341 case LIST_TYPE_STORAGE: 342 if (args != null && args.containsKey(EXTRA_VOLUME_UUID)) { 343 mVolumeUuid = args.getString(EXTRA_VOLUME_UUID); 344 mStorageType = args.getInt(EXTRA_STORAGE_TYPE, STORAGE_TYPE_DEFAULT); 345 } else { 346 // No volume selected, display a normal list, sorted by size. 347 mListType = LIST_TYPE_MAIN; 348 } 349 mSortOrder = R.id.sort_order_size; 350 break; 351 case LIST_TYPE_HIGH_POWER: 352 // Default to showing system. 353 mShowSystem = true; 354 break; 355 case LIST_TYPE_OVERLAY: 356 reportIfRestrictedSawIntent(intent); 357 break; 358 case LIST_TYPE_GAMES: 359 mSortOrder = R.id.sort_order_size; 360 break; 361 case LIST_TYPE_NOTIFICATION: 362 mUsageStatsManager = IUsageStatsManager.Stub.asInterface( 363 ServiceManager.getService(Context.USAGE_STATS_SERVICE)); 364 mNotificationBackend = new NotificationBackend(); 365 mSortOrder = R.id.sort_order_recent_notification; 366 if (className.equals(NotificationReviewPermissionsActivity.class.getName())) { 367 // Special-case for a case where a user is directed to the all apps notification 368 // preferences page via a notification prompt to review permissions settings. 369 Settings.Global.putInt(getContext().getContentResolver(), 370 Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE, 371 1); // USER_INTERACTED 372 } 373 break; 374 case LIST_TYPE_NFC_TAG_APPS: 375 mShowSystem = true; 376 break; 377 } 378 final AppFilterRegistry appFilterRegistry = AppFilterRegistry.getInstance(); 379 mFilter = appFilterRegistry.get(appFilterRegistry.getDefaultFilterType(mListType)); 380 mIsPersonalOnly = args != null && args.getInt(ProfileSelectFragment.EXTRA_PROFILE) 381 == ProfileSelectFragment.ProfileType.PERSONAL; 382 mIsWorkOnly = args != null && args.getInt(ProfileSelectFragment.EXTRA_PROFILE) 383 == ProfileSelectFragment.ProfileType.WORK; 384 mIsPrivateProfileOnly = args != null && args.getInt(ProfileSelectFragment.EXTRA_PROFILE) 385 == ProfileSelectFragment.ProfileType.PRIVATE; 386 mWorkUserId = args != null ? args.getInt(EXTRA_WORK_ID) : UserHandle.myUserId(); 387 if (mIsWorkOnly && mWorkUserId == UserHandle.myUserId()) { 388 mWorkUserId = Utils.getManagedProfileId(mUserManager, UserHandle.myUserId()); 389 } 390 391 mExpandSearch = activity.getIntent().getBooleanExtra(EXTRA_EXPAND_SEARCH_VIEW, false); 392 393 if (savedInstanceState != null) { 394 mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder); 395 mShowSystem = savedInstanceState.getBoolean(EXTRA_SHOW_SYSTEM, mShowSystem); 396 mFilterType = 397 savedInstanceState.getInt(EXTRA_FILTER_TYPE, AppFilterRegistry.FILTER_APPS_ALL); 398 mExpandSearch = savedInstanceState.getBoolean(EXTRA_EXPAND_SEARCH_VIEW); 399 } 400 401 mInvalidSizeStr = activity.getText(R.string.invalid_size_value); 402 403 mResetAppsHelper = new ResetAppsHelper(activity); 404 405 if (screenTitle > 0) { 406 activity.setTitle(screenTitle); 407 } 408 } 409 reportIfRestrictedSawIntent(Intent intent)410 private void reportIfRestrictedSawIntent(Intent intent) { 411 try { 412 Uri data = intent.getData(); 413 if (data == null || !TextUtils.equals("package", data.getScheme())) { 414 // Not a restricted intent 415 return; 416 } 417 IBinder activityToken = getActivity().getActivityToken(); 418 int callingUid = ActivityManager.getService().getLaunchedFromUid(activityToken); 419 if (callingUid == -1) { 420 Log.w(TAG, "Error obtaining calling uid"); 421 return; 422 } 423 IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface( 424 ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); 425 if (platformCompat == null) { 426 Log.w(TAG, "Error obtaining IPlatformCompat service"); 427 return; 428 } 429 platformCompat.reportChangeByUid(CHANGE_RESTRICT_SAW_INTENT, callingUid); 430 } catch (RemoteException e) { 431 Log.w(TAG, "Error reporting SAW intent restriction", e); 432 } 433 } 434 435 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)436 public View onCreateView(LayoutInflater inflater, ViewGroup container, 437 Bundle savedInstanceState) { 438 if (getActivity().isFinishing()) { 439 return null; 440 } 441 if (mListType == LIST_TYPE_OVERLAY && !Utils.isSystemAlertWindowEnabled(getContext())) { 442 mRootView = inflater.inflate(R.layout.manage_applications_apps_unsupported, null); 443 setHasOptionsMenu(false); 444 return mRootView; 445 } 446 447 mRootView = inflater.inflate(R.layout.manage_applications_apps, null); 448 mLoadingContainer = mRootView.findViewById(R.id.loading_container); 449 mEmptyView = mRootView.findViewById(android.R.id.empty); 450 mRecyclerView = mRootView.findViewById(R.id.apps_list); 451 452 mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter, 453 savedInstanceState); 454 if (savedInstanceState != null) { 455 mApplications.mHasReceivedLoadEntries = 456 savedInstanceState.getBoolean(EXTRA_HAS_ENTRIES, false); 457 mApplications.mHasReceivedBridgeCallback = 458 savedInstanceState.getBoolean(EXTRA_HAS_BRIDGE, false); 459 } 460 mRecyclerView.setItemAnimator(null); 461 mRecyclerView.setLayoutManager(new LinearLayoutManager( 462 getContext(), RecyclerView.VERTICAL, false /* reverseLayout */)); 463 mRecyclerView.setAdapter(mApplications); 464 465 // We have to do this now because PreferenceFrameLayout looks at it 466 // only when the view is added. 467 if (container instanceof PreferenceFrameLayout) { 468 ((PreferenceFrameLayout.LayoutParams) mRootView.getLayoutParams()).removeBorders = true; 469 } 470 471 createHeader(); 472 473 mResetAppsHelper.onRestoreInstanceState(savedInstanceState); 474 475 mAppBarLayout = getActivity().findViewById(R.id.app_bar); 476 autoSetCollapsingToolbarLayoutScrolling(); 477 478 return mRootView; 479 } 480 481 @VisibleForTesting createHeader()482 void createHeader() { 483 final Activity activity = getActivity(); 484 final FrameLayout pinnedHeader = mRootView.findViewById(R.id.pinned_header); 485 mSpinnerHeader = activity.getLayoutInflater() 486 .inflate(R.layout.manage_apps_filter_spinner, pinnedHeader, false); 487 mFilterSpinner = mSpinnerHeader.findViewById(R.id.filter_spinner); 488 mFilterAdapter = new FilterSpinnerAdapter(this); 489 mFilterSpinner.setAdapter(mFilterAdapter); 490 mFilterSpinner.setOnItemSelectedListener(this); 491 pinnedHeader.addView(mSpinnerHeader, 0); 492 493 final AppFilterRegistry appFilterRegistry = AppFilterRegistry.getInstance(); 494 final int filterType = appFilterRegistry.getDefaultFilterType(mListType); 495 mFilterAdapter.enableFilter(filterType); 496 497 if (mListType == LIST_TYPE_MAIN) { 498 // Apply the personal and work filter only if new tab should be added 499 // for a given user profile. Else let it use the default all apps filter. 500 if (Utils.isNewTabNeeded(getActivity()) && !mIsWorkOnly && !mIsPersonalOnly) { 501 mFilterAdapter.enableFilter(FILTER_APPS_PERSONAL); 502 mFilterAdapter.enableFilter(FILTER_APPS_WORK); 503 } 504 } 505 if (mListType == LIST_TYPE_NOTIFICATION) { 506 mFilterAdapter.enableFilter(FILTER_APPS_RECENT); 507 mFilterAdapter.enableFilter(FILTER_APPS_FREQUENT); 508 mFilterAdapter.enableFilter(FILTER_APPS_BLOCKED); 509 mFilterAdapter.enableFilter(FILTER_APPS_ALL); 510 } 511 if (mListType == LIST_TYPE_HIGH_POWER) { 512 mFilterAdapter.enableFilter(FILTER_APPS_POWER_ALLOWLIST_ALL); 513 } 514 if (mListType == LIST_TYPE_BATTERY_OPTIMIZATION) { 515 mFilterAdapter.enableFilter(FILTER_APPS_ALL); 516 mFilterAdapter.enableFilter(FILTER_APPS_BATTERY_UNRESTRICTED); 517 mFilterAdapter.enableFilter(FILTER_APPS_BATTERY_OPTIMIZED); 518 mFilterAdapter.enableFilter(FILTER_APPS_BATTERY_RESTRICTED); 519 } 520 521 setCompositeFilter(); 522 } 523 524 @VisibleForTesting 525 @Nullable getCompositeFilter(int listType, int storageType, String volumeUuid)526 static AppFilter getCompositeFilter(int listType, int storageType, String volumeUuid) { 527 AppFilter filter = new VolumeFilter(volumeUuid); 528 if (listType == LIST_TYPE_STORAGE) { 529 if (storageType == STORAGE_TYPE_DEFAULT) { 530 filter = new CompoundFilter(ApplicationsState.FILTER_APPS_EXCEPT_GAMES, filter); 531 } 532 return filter; 533 } 534 if (listType == LIST_TYPE_GAMES) { 535 return new CompoundFilter(ApplicationsState.FILTER_GAMES, filter); 536 } 537 return null; 538 } 539 540 @Override getMetricsCategory()541 public int getMetricsCategory() { 542 switch (mListType) { 543 case LIST_TYPE_MAIN: 544 return SettingsEnums.MANAGE_APPLICATIONS; 545 case LIST_TYPE_NOTIFICATION: 546 return SettingsEnums.MANAGE_APPLICATIONS_NOTIFICATIONS; 547 case LIST_TYPE_STORAGE: 548 return SettingsEnums.APPLICATIONS_STORAGE_APPS; 549 case LIST_TYPE_GAMES: 550 return SettingsEnums.APPLICATIONS_STORAGE_GAMES; 551 case LIST_TYPE_USAGE_ACCESS: 552 return SettingsEnums.USAGE_ACCESS; 553 case LIST_TYPE_HIGH_POWER: 554 return SettingsEnums.APPLICATIONS_HIGH_POWER_APPS; 555 case LIST_TYPE_OVERLAY: 556 return SettingsEnums.SYSTEM_ALERT_WINDOW_APPS; 557 case LIST_TYPE_WRITE_SETTINGS: 558 return SettingsEnums.MODIFY_SYSTEM_SETTINGS; 559 case LIST_TYPE_MANAGE_SOURCES: 560 return SettingsEnums.MANAGE_EXTERNAL_SOURCES; 561 case LIST_TYPE_WIFI_ACCESS: 562 return SettingsEnums.CONFIGURE_WIFI; 563 case LIST_MANAGE_EXTERNAL_STORAGE: 564 return SettingsEnums.MANAGE_EXTERNAL_STORAGE; 565 case LIST_TYPE_ALARMS_AND_REMINDERS: 566 return SettingsEnums.ALARMS_AND_REMINDERS; 567 case LIST_TYPE_MEDIA_MANAGEMENT_APPS: 568 return SettingsEnums.MEDIA_MANAGEMENT_APPS; 569 case LIST_TYPE_APPS_LOCALE: 570 return SettingsEnums.APPS_LOCALE_LIST; 571 case LIST_TYPE_BATTERY_OPTIMIZATION: 572 return SettingsEnums.BATTERY_OPTIMIZED_APPS_LIST; 573 case LIST_TYPE_LONG_BACKGROUND_TASKS: 574 return SettingsEnums.LONG_BACKGROUND_TASKS; 575 case LIST_TYPE_CLONED_APPS: 576 return SettingsEnums.CLONED_APPS; 577 case LIST_TYPE_NFC_TAG_APPS: 578 return SettingsEnums.CONFIG_NFC_TAG_APP_PREF; 579 case LIST_TYPE_TURN_SCREEN_ON: 580 return SettingsEnums.SETTINGS_TURN_SCREEN_ON_ACCESS; 581 default: 582 return SettingsEnums.PAGE_UNKNOWN; 583 } 584 } 585 586 @Override onStart()587 public void onStart() { 588 super.onStart(); 589 updateView(); 590 if (mApplications != null) { 591 mApplications.updateLoading(); 592 } 593 } 594 595 @Override onResume()596 public void onResume() { 597 super.onResume(); 598 if (mApplications != null) { 599 mApplications.resume(mSortOrder); 600 } 601 } 602 603 @Override onSaveInstanceState(Bundle outState)604 public void onSaveInstanceState(Bundle outState) { 605 super.onSaveInstanceState(outState); 606 mResetAppsHelper.onSaveInstanceState(outState); 607 outState.putInt(EXTRA_SORT_ORDER, mSortOrder); 608 outState.putInt(EXTRA_FILTER_TYPE, mFilter.getFilterType()); 609 outState.putBoolean(EXTRA_SHOW_SYSTEM, mShowSystem); 610 if (mSearchView != null) { 611 outState.putBoolean(EXTRA_EXPAND_SEARCH_VIEW, !mSearchView.isIconified()); 612 } 613 if (mApplications != null) { 614 outState.putBoolean(EXTRA_HAS_ENTRIES, mApplications.mHasReceivedLoadEntries); 615 outState.putBoolean(EXTRA_HAS_BRIDGE, mApplications.mHasReceivedBridgeCallback); 616 mApplications.onSaveInstanceState(outState); 617 } 618 } 619 620 @Override onPause()621 public void onPause() { 622 super.onPause(); 623 if (mApplications != null) { 624 mApplications.pause(); 625 } 626 } 627 628 @Override onStop()629 public void onStop() { 630 super.onStop(); 631 if (mResetAppsHelper != null) { 632 mResetAppsHelper.stop(); 633 } 634 } 635 636 @Override onDestroyView()637 public void onDestroyView() { 638 super.onDestroyView(); 639 640 if (mApplications != null) { 641 mApplications.release(); 642 } 643 mRootView = null; 644 AppIconCacheManager.getInstance().release(); 645 } 646 647 @Override onActivityResult(int requestCode, int resultCode, Intent data)648 public void onActivityResult(int requestCode, int resultCode, Intent data) { 649 if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) { 650 if (mListType == LIST_TYPE_NOTIFICATION || mListType == LIST_TYPE_HIGH_POWER 651 || mListType == LIST_TYPE_OVERLAY || mListType == LIST_TYPE_WRITE_SETTINGS 652 || mListType == LIST_TYPE_NFC_TAG_APPS) { 653 mApplications.mExtraInfoBridge.forceUpdate(mCurrentPkgName, mCurrentUid); 654 } else { 655 mApplicationsState.requestSize(mCurrentPkgName, UserHandle.getUserId(mCurrentUid)); 656 } 657 } 658 } 659 setCompositeFilter()660 private void setCompositeFilter() { 661 AppFilter compositeFilter = getCompositeFilter(mListType, mStorageType, mVolumeUuid); 662 if (compositeFilter == null) { 663 compositeFilter = mFilter.getFilter(); 664 } 665 if (mIsWorkOnly) { 666 compositeFilter = new CompoundFilter(compositeFilter, ApplicationsState.FILTER_WORK); 667 } 668 if (mIsPrivateProfileOnly) { 669 compositeFilter = 670 new CompoundFilter(compositeFilter, ApplicationsState.FILTER_PRIVATE_PROFILE); 671 } 672 673 // We might not be showing the private tab even when there's a private profile present and 674 // there's only personal profile info to show, in which case we should still apply the 675 // personal filter. 676 if (mIsPersonalOnly || !(mIsWorkOnly || mIsPrivateProfileOnly)) { 677 compositeFilter = new CompoundFilter(compositeFilter, 678 ApplicationsState.FILTER_PERSONAL); 679 } 680 mApplications.setCompositeFilter(compositeFilter); 681 } 682 683 // utility method used to start sub activity startApplicationDetailsActivity()684 private void startApplicationDetailsActivity() { 685 switch (mListType) { 686 case LIST_TYPE_NOTIFICATION: 687 startAppInfoFragment(AppNotificationSettings.class, R.string.notifications_title); 688 break; 689 case LIST_TYPE_USAGE_ACCESS: 690 startAppInfoFragment(UsageAccessDetails.class, R.string.usage_access); 691 break; 692 case LIST_TYPE_STORAGE: 693 startAppInfoFragment(AppStorageSettings.class, R.string.storage_settings); 694 break; 695 case LIST_TYPE_HIGH_POWER: 696 HighPowerDetail.show(this, mCurrentUid, mCurrentPkgName, INSTALLED_APP_DETAILS); 697 break; 698 case LIST_TYPE_OVERLAY: 699 startAppInfoFragment(DrawOverlayDetails.class, R.string.overlay_settings); 700 break; 701 case LIST_TYPE_WRITE_SETTINGS: 702 startAppInfoFragment(WriteSettingsDetails.class, R.string.write_system_settings); 703 break; 704 case LIST_TYPE_MANAGE_SOURCES: 705 startAppInfoFragment(ExternalSourcesDetails.class, 706 com.android.settingslib.R.string.install_other_apps); 707 break; 708 case LIST_TYPE_GAMES: 709 startAppInfoFragment(AppStorageSettings.class, R.string.game_storage_settings); 710 break; 711 case LIST_TYPE_WIFI_ACCESS: 712 startAppInfoFragment(ChangeWifiStateDetails.class, 713 R.string.change_wifi_state_title); 714 break; 715 case LIST_MANAGE_EXTERNAL_STORAGE: 716 startAppInfoFragment(ManageExternalStorageDetails.class, 717 R.string.manage_external_storage_title); 718 break; 719 case LIST_TYPE_ALARMS_AND_REMINDERS: 720 startAppInfoFragment(AlarmsAndRemindersDetails.class, 721 com.android.settingslib.R.string.alarms_and_reminders_label); 722 break; 723 case LIST_TYPE_MEDIA_MANAGEMENT_APPS: 724 startAppInfoFragment(MediaManagementAppsDetails.class, 725 R.string.media_management_apps_title); 726 break; 727 case LIST_TYPE_APPS_LOCALE: 728 Intent intent = new Intent(getContext(), AppLocalePickerActivity.class); 729 intent.setData(Uri.parse("package:" + mCurrentPkgName)); 730 getContext().startActivityAsUser(intent, 731 UserHandle.getUserHandleForUid(mCurrentUid)); 732 break; 733 case LIST_TYPE_BATTERY_OPTIMIZATION: 734 AdvancedPowerUsageDetail.startBatteryDetailPage( 735 getActivity(), this, mCurrentPkgName, 736 UserHandle.getUserHandleForUid(mCurrentUid)); 737 break; 738 case LIST_TYPE_LONG_BACKGROUND_TASKS: 739 startAppInfoFragment(LongBackgroundTasksDetails.class, 740 R.string.long_background_tasks_label); 741 break; 742 case LIST_TYPE_CLONED_APPS: 743 int userId = UserHandle.getUserId(mCurrentUid); 744 UserInfo userInfo = mUserManager.getUserInfo(userId); 745 if (userInfo != null && !userInfo.isCloneProfile()) { 746 SpaActivity.startSpaActivity(getContext(), CloneAppInfoSettingsProvider.INSTANCE 747 .getRoute(mCurrentPkgName, userId)); 748 } else { 749 SpaActivity.startSpaActivity(getContext(), AppInfoSettingsProvider.INSTANCE 750 .getRoute(mCurrentPkgName, userId)); 751 } 752 break; 753 case LIST_TYPE_NFC_TAG_APPS: 754 startAppInfoFragment(ChangeNfcTagAppsStateDetails.class, 755 R.string.change_nfc_tag_apps_title); 756 break; 757 case LIST_TYPE_TURN_SCREEN_ON: 758 startAppInfoFragment(TurnScreenOnDetails.class, 759 com.android.settingslib.R.string.turn_screen_on_title); 760 break; 761 // TODO: Figure out if there is a way where we can spin up the profile's settings 762 // process ahead of time, to avoid a long load of data when user clicks on a managed 763 // app. Maybe when they load the list of apps that contains managed profile apps. 764 default: 765 startAppInfoFragment( 766 AppInfoDashboardFragment.class, R.string.application_info_label); 767 break; 768 } 769 } 770 startAppInfoFragment(Class<?> fragment, int titleRes)771 private void startAppInfoFragment(Class<?> fragment, int titleRes) { 772 AppInfoBase.startAppInfoFragment(fragment, getString(titleRes), mCurrentPkgName, 773 mCurrentUid, this, INSTALLED_APP_DETAILS, getMetricsCategory()); 774 } 775 776 @Override onCreateOptionsMenu(Menu menu, MenuInflater inflater)777 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 778 final Activity activity = getActivity(); 779 if (activity == null) { 780 return; 781 } 782 mOptionsMenu = menu; 783 inflater.inflate(R.menu.manage_apps, menu); 784 785 final MenuItem searchMenuItem = menu.findItem(R.id.search_app_list_menu); 786 if (searchMenuItem != null) { 787 searchMenuItem.setOnActionExpandListener(this); 788 mSearchView = (SearchView) searchMenuItem.getActionView(); 789 mSearchView.setQueryHint(getText(R.string.search_settings)); 790 mSearchView.setOnQueryTextListener(this); 791 mSearchView.setMaxWidth(Integer.MAX_VALUE); 792 if (mExpandSearch) { 793 searchMenuItem.expandActionView(); 794 } 795 } 796 797 updateOptionsMenu(); 798 } 799 800 @Override onMenuItemActionExpand(MenuItem item)801 public boolean onMenuItemActionExpand(MenuItem item) { 802 // To prevent a large space on tool bar. 803 mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/); 804 // To prevent user can expand the collapsing tool bar view. 805 ViewCompat.setNestedScrollingEnabled(mRecyclerView, false); 806 return true; 807 } 808 809 @Override onMenuItemActionCollapse(MenuItem item)810 public boolean onMenuItemActionCollapse(MenuItem item) { 811 // We keep the collapsed status after user cancel the search function. 812 mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/); 813 ViewCompat.setNestedScrollingEnabled(mRecyclerView, true); 814 return true; 815 } 816 817 @Override onPrepareOptionsMenu(Menu menu)818 public void onPrepareOptionsMenu(Menu menu) { 819 updateOptionsMenu(); 820 } 821 822 @Override onDestroyOptionsMenu()823 public void onDestroyOptionsMenu() { 824 mOptionsMenu = null; 825 } 826 827 @StringRes getHelpResource()828 int getHelpResource() { 829 switch (mListType) { 830 case LIST_TYPE_NOTIFICATION: 831 return R.string.help_uri_notifications; 832 case LIST_TYPE_USAGE_ACCESS: 833 return R.string.help_url_usage_access; 834 case LIST_TYPE_STORAGE: 835 return R.string.help_uri_apps_storage; 836 case LIST_TYPE_HIGH_POWER: 837 return R.string.help_uri_apps_high_power; 838 case LIST_TYPE_OVERLAY: 839 return R.string.help_uri_apps_overlay; 840 case LIST_TYPE_WRITE_SETTINGS: 841 return R.string.help_uri_apps_write_settings; 842 case LIST_TYPE_MANAGE_SOURCES: 843 return R.string.help_uri_apps_manage_sources; 844 case LIST_TYPE_GAMES: 845 return R.string.help_uri_apps_overlay; 846 case LIST_TYPE_WIFI_ACCESS: 847 return R.string.help_uri_apps_wifi_access; 848 case LIST_MANAGE_EXTERNAL_STORAGE: 849 return R.string.help_uri_manage_external_storage; 850 case LIST_TYPE_ALARMS_AND_REMINDERS: 851 return R.string.help_uri_alarms_and_reminders; 852 case LIST_TYPE_MEDIA_MANAGEMENT_APPS: 853 return R.string.help_uri_media_management_apps; 854 case LIST_TYPE_LONG_BACKGROUND_TASKS: 855 return R.string.help_uri_long_background_tasks; 856 default: 857 case LIST_TYPE_MAIN: 858 return R.string.help_uri_apps; 859 } 860 } 861 updateOptionsMenu()862 void updateOptionsMenu() { 863 if (mOptionsMenu == null) { 864 return; 865 } 866 mOptionsMenu.findItem(R.id.advanced).setVisible(false); 867 868 mOptionsMenu.findItem(R.id.sort_order_alpha).setVisible(mListType == LIST_TYPE_STORAGE 869 && mSortOrder != R.id.sort_order_alpha); 870 mOptionsMenu.findItem(R.id.sort_order_size).setVisible(mListType == LIST_TYPE_STORAGE 871 && mSortOrder != R.id.sort_order_size); 872 873 mOptionsMenu.findItem(R.id.show_system).setVisible(!mShowSystem 874 && mListType != LIST_TYPE_HIGH_POWER && mListType != LIST_TYPE_APPS_LOCALE 875 && mListType != LIST_TYPE_CLONED_APPS); 876 mOptionsMenu.findItem(R.id.hide_system).setVisible(mShowSystem 877 && mListType != LIST_TYPE_HIGH_POWER && mListType != LIST_TYPE_APPS_LOCALE 878 && mListType != LIST_TYPE_CLONED_APPS); 879 880 mOptionsMenu.findItem(R.id.reset_app_preferences).setVisible(mListType == LIST_TYPE_MAIN); 881 882 // Hide notification menu items, because sorting happens when filtering 883 mOptionsMenu.findItem(R.id.sort_order_recent_notification).setVisible(false); 884 mOptionsMenu.findItem(R.id.sort_order_frequent_notification).setVisible(false); 885 final MenuItem searchItem = mOptionsMenu.findItem(MENU_SEARCH); 886 if (searchItem != null) { 887 searchItem.setVisible(false); 888 } 889 890 mOptionsMenu.findItem(R.id.delete_all_app_clones) 891 .setVisible(mListType == LIST_TYPE_CLONED_APPS && DeviceConfig.getBoolean( 892 DeviceConfig.NAMESPACE_APP_CLONING, PROPERTY_DELETE_ALL_APP_CLONES_ENABLED, 893 true) && Utils.getCloneUserId(getContext()) != -1); 894 } 895 896 @Override onOptionsItemSelected(MenuItem item)897 public boolean onOptionsItemSelected(MenuItem item) { 898 int menuId = item.getItemId(); 899 int i = item.getItemId(); 900 if (i == R.id.sort_order_alpha || i == R.id.sort_order_size) { 901 if (mApplications != null) { 902 mApplications.rebuild(menuId, false); 903 } 904 } else if (i == R.id.show_system || i == R.id.hide_system) { 905 mShowSystem = !mShowSystem; 906 mApplications.rebuild(); 907 } else if (i == R.id.reset_app_preferences) { 908 final boolean appsControlDisallowedBySystem = 909 RestrictedLockUtilsInternal.hasBaseUserRestriction(getActivity(), 910 UserManager.DISALLOW_APPS_CONTROL, UserHandle.myUserId()); 911 final RestrictedLockUtils.EnforcedAdmin appsControlDisallowedAdmin = 912 RestrictedLockUtilsInternal.checkIfRestrictionEnforced(getActivity(), 913 UserManager.DISALLOW_APPS_CONTROL, UserHandle.myUserId()); 914 if (appsControlDisallowedAdmin != null && !appsControlDisallowedBySystem) { 915 RestrictedLockUtils.sendShowAdminSupportDetailsIntent( 916 getActivity(), appsControlDisallowedAdmin); 917 } else { 918 mResetAppsHelper.buildResetDialog(); 919 } 920 return true; 921 } else if (i == R.id.advanced) { 922 if (mListType == LIST_TYPE_NOTIFICATION) { 923 new SubSettingLauncher(getContext()) 924 .setDestination(ConfigureNotificationSettings.class.getName()) 925 .setTitleRes(R.string.configure_notification_settings) 926 .setSourceMetricsCategory(getMetricsCategory()) 927 .setResultListener(this, ADVANCED_SETTINGS) 928 .launch(); 929 } else { 930 Intent intent = new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS) 931 .setPackage(getContext() 932 .getPackageManager().getPermissionControllerPackageName()); 933 startActivityForResult(intent, ADVANCED_SETTINGS); 934 } 935 return true; 936 } else if (i == R.id.delete_all_app_clones) { 937 int clonedUserId = Utils.getCloneUserId(getContext()); 938 if (clonedUserId == -1) { 939 // No Apps Cloned Till now. Do Nothing. 940 return false; 941 } 942 IUserManager um = IUserManager.Stub.asInterface( 943 ServiceManager.getService(Context.USER_SERVICE)); 944 CloneBackend cloneBackend = CloneBackend.getInstance(getContext()); 945 try { 946 // Warning: This removes all the data, media & images present in cloned user. 947 if (um.removeUser(clonedUserId)) { 948 cloneBackend.resetCloneUserId(); 949 mApplications.rebuild(); 950 } else if (ManageApplications.DEBUG) { 951 Log.e(TAG, "Failed to remove cloned user"); 952 } 953 } catch (RemoteException e) { 954 Log.e(TAG, "Failed to remove cloned apps", e); 955 Toast.makeText(getContext(), 956 getContext().getString(R.string.delete_all_app_clones_failure), 957 Toast.LENGTH_LONG).show(); 958 } 959 } else {// Handle the home button 960 return false; 961 } 962 updateOptionsMenu(); 963 return true; 964 } 965 966 @Override onClick(View view)967 public void onClick(View view) { 968 if (mApplications == null) { 969 return; 970 } 971 final int applicationPosition = 972 ApplicationsAdapter.getApplicationPosition( 973 mListType, mRecyclerView.getChildAdapterPosition(view)); 974 975 if (applicationPosition == RecyclerView.NO_POSITION) { 976 Log.w(TAG, "Cannot find position for child, skipping onClick handling"); 977 return; 978 } 979 if (mApplications.getApplicationCount() > applicationPosition) { 980 ApplicationsState.AppEntry entry = mApplications.getAppEntry(applicationPosition); 981 mCurrentPkgName = entry.info.packageName; 982 mCurrentUid = entry.info.uid; 983 startApplicationDetailsActivity(); 984 // We disable the scrolling ability in onMenuItemActionCollapse, we should recover it 985 // if user selects any app item. 986 ViewCompat.setNestedScrollingEnabled(mRecyclerView, true); 987 } 988 } 989 990 @Override onItemSelected(AdapterView<?> parent, View view, int position, long id)991 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 992 mFilter = mFilterAdapter.getFilter(position); 993 setCompositeFilter(); 994 mApplications.setFilter(mFilter); 995 996 if (DEBUG) { 997 Log.d(TAG, "Selecting filter " + getContext().getText(mFilter.getTitle())); 998 } 999 } 1000 1001 @Override onNothingSelected(AdapterView<?> parent)1002 public void onNothingSelected(AdapterView<?> parent) { 1003 } 1004 1005 @Override onQueryTextSubmit(String query)1006 public boolean onQueryTextSubmit(String query) { 1007 return false; 1008 } 1009 1010 @Override onQueryTextChange(String newText)1011 public boolean onQueryTextChange(String newText) { 1012 mApplications.filterSearch(newText); 1013 return false; 1014 } 1015 updateView()1016 public void updateView() { 1017 updateOptionsMenu(); 1018 final Activity host = getActivity(); 1019 if (host != null) { 1020 host.invalidateOptionsMenu(); 1021 } 1022 } 1023 setHasDisabled(boolean hasDisabledApps)1024 public void setHasDisabled(boolean hasDisabledApps) { 1025 if (mListType != LIST_TYPE_MAIN) { 1026 return; 1027 } 1028 mFilterAdapter.setFilterEnabled(FILTER_APPS_ENABLED, hasDisabledApps); 1029 mFilterAdapter.setFilterEnabled(FILTER_APPS_DISABLED, hasDisabledApps); 1030 } 1031 setHasInstant(boolean haveInstantApps)1032 public void setHasInstant(boolean haveInstantApps) { 1033 if (LIST_TYPES_WITH_INSTANT.contains(mListType)) { 1034 mFilterAdapter.setFilterEnabled(FILTER_APPS_INSTANT, haveInstantApps); 1035 } 1036 } 1037 autoSetCollapsingToolbarLayoutScrolling()1038 private void autoSetCollapsingToolbarLayoutScrolling() { 1039 if (mAppBarLayout == null) { 1040 return; 1041 } 1042 final CoordinatorLayout.LayoutParams params = 1043 (CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams(); 1044 final AppBarLayout.Behavior behavior = new AppBarLayout.Behavior(); 1045 behavior.setDragCallback( 1046 new AppBarLayout.Behavior.DragCallback() { 1047 @Override 1048 public boolean canDrag(@NonNull AppBarLayout appBarLayout) { 1049 return appBarLayout.getResources().getConfiguration().orientation 1050 == Configuration.ORIENTATION_LANDSCAPE; 1051 } 1052 }); 1053 params.setBehavior(behavior); 1054 } 1055 1056 /** 1057 * Returns a resource ID of title based on what type of app list is 1058 * 1059 * @param intent the intent of the activity that might include a specified title 1060 * @param args the args that includes a class name of app list 1061 */ getTitleResId(@onNull Intent intent, Bundle args)1062 public static int getTitleResId(@NonNull Intent intent, Bundle args) { 1063 int screenTitle = intent.getIntExtra( 1064 SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, R.string.all_apps); 1065 String className = getClassName(intent, args); 1066 if (className.equals(UsageAccessSettingsActivity.class.getName())) { 1067 screenTitle = R.string.usage_access; 1068 } else if (className.equals(HighPowerApplicationsActivity.class.getName())) { 1069 screenTitle = R.string.high_power_apps; 1070 } else if (className.equals(OverlaySettingsActivity.class.getName())) { 1071 screenTitle = R.string.system_alert_window_settings; 1072 } else if (className.equals(WriteSettingsActivity.class.getName())) { 1073 screenTitle = R.string.write_settings; 1074 } else if (className.equals(ManageExternalSourcesActivity.class.getName())) { 1075 screenTitle = com.android.settingslib.R.string.install_other_apps; 1076 } else if (className.equals(ChangeWifiStateActivity.class.getName())) { 1077 screenTitle = R.string.change_wifi_state_title; 1078 } else if (className.equals(ManageExternalStorageActivity.class.getName())) { 1079 screenTitle = R.string.manage_external_storage_title; 1080 } else if (className.equals(MediaManagementAppsActivity.class.getName())) { 1081 screenTitle = R.string.media_management_apps_title; 1082 } else if (className.equals(AlarmsAndRemindersActivity.class.getName())) { 1083 screenTitle = com.android.settingslib.R.string.alarms_and_reminders_title; 1084 } else if (className.equals(NotificationAppListActivity.class.getName()) 1085 || className.equals( 1086 NotificationReviewPermissionsActivity.class.getName())) { 1087 screenTitle = R.string.app_notifications_title; 1088 } else if (className.equals(AppLocaleDetails.class.getName())) { 1089 screenTitle = R.string.app_locales_picker_menu_title; 1090 } else if (className.equals(AppBatteryUsageActivity.class.getName())) { 1091 screenTitle = R.string.app_battery_usage_title; 1092 } else if (className.equals(LongBackgroundTasksActivity.class.getName())) { 1093 screenTitle = R.string.long_background_tasks_title; 1094 } else if (className.equals(ClonedAppsListActivity.class.getName())) { 1095 screenTitle = R.string.cloned_apps_dashboard_title; 1096 } else if (className.equals(ChangeNfcTagAppsActivity.class.getName())) { 1097 screenTitle = R.string.change_nfc_tag_apps_title; 1098 } else if (className.equals(TurnScreenOnSettingsActivity.class.getName())) { 1099 screenTitle = com.android.settingslib.R.string.turn_screen_on_title; 1100 } else { 1101 if (screenTitle == -1) { 1102 screenTitle = R.string.all_apps; 1103 } 1104 } 1105 return screenTitle; 1106 } 1107 getClassName(@onNull Intent intent, Bundle args)1108 private static String getClassName(@NonNull Intent intent, Bundle args) { 1109 String className = args != null ? args.getString(EXTRA_CLASSNAME) : null; 1110 if (className == null) { 1111 className = intent.getComponent().getClassName(); 1112 } 1113 return className; 1114 } 1115 1116 static class FilterSpinnerAdapter extends SettingsSpinnerAdapter<CharSequence> { 1117 1118 private final ManageApplications mManageApplications; 1119 private final Context mContext; 1120 1121 // Use ArrayAdapter for view logic, but have our own list for managing 1122 // the options available. 1123 private final ArrayList<AppFilterItem> mFilterOptions = new ArrayList<>(); 1124 FilterSpinnerAdapter(ManageApplications manageApplications)1125 public FilterSpinnerAdapter(ManageApplications manageApplications) { 1126 super(manageApplications.getContext()); 1127 mContext = manageApplications.getContext(); 1128 mManageApplications = manageApplications; 1129 } 1130 getFilter(int position)1131 public AppFilterItem getFilter(int position) { 1132 return mFilterOptions.get(position); 1133 } 1134 setFilterEnabled(@ppFilterRegistry.FilterType int filter, boolean enabled)1135 public void setFilterEnabled(@AppFilterRegistry.FilterType int filter, boolean enabled) { 1136 if (enabled) { 1137 enableFilter(filter); 1138 } else { 1139 disableFilter(filter); 1140 } 1141 } 1142 enableFilter(@ppFilterRegistry.FilterType int filterType)1143 public void enableFilter(@AppFilterRegistry.FilterType int filterType) { 1144 final AppFilterItem filter = AppFilterRegistry.getInstance().get(filterType); 1145 if (mFilterOptions.contains(filter)) { 1146 return; 1147 } 1148 if (DEBUG) { 1149 Log.d(TAG, "Enabling filter " + mContext.getText(filter.getTitle())); 1150 } 1151 mFilterOptions.add(filter); 1152 Collections.sort(mFilterOptions); 1153 updateFilterView(mFilterOptions.size() > 1); 1154 notifyDataSetChanged(); 1155 if (mFilterOptions.size() == 1) { 1156 if (DEBUG) { 1157 Log.d(TAG, "Auto selecting filter " + filter + " " + mContext.getText( 1158 filter.getTitle())); 1159 } 1160 mManageApplications.mFilterSpinner.setSelection(0); 1161 mManageApplications.onItemSelected(null, null, 0, 0); 1162 } 1163 if (mFilterOptions.size() > 1) { 1164 final AppFilterItem previousFilter = AppFilterRegistry.getInstance().get( 1165 mManageApplications.mFilterType); 1166 final int index = mFilterOptions.indexOf(previousFilter); 1167 if (index != -1) { 1168 mManageApplications.mFilterSpinner.setSelection(index); 1169 mManageApplications.onItemSelected(null, null, index, 0); 1170 } 1171 } 1172 } 1173 disableFilter(@ppFilterRegistry.FilterType int filterType)1174 public void disableFilter(@AppFilterRegistry.FilterType int filterType) { 1175 final AppFilterItem filter = AppFilterRegistry.getInstance().get(filterType); 1176 if (!mFilterOptions.remove(filter)) { 1177 return; 1178 } 1179 if (DEBUG) { 1180 Log.d(TAG, "Disabling filter " + filter + " " + mContext.getText( 1181 filter.getTitle())); 1182 } 1183 Collections.sort(mFilterOptions); 1184 updateFilterView(mFilterOptions.size() > 1); 1185 notifyDataSetChanged(); 1186 if (mManageApplications.mFilter == filter) { 1187 if (mFilterOptions.size() > 0) { 1188 if (DEBUG) { 1189 Log.d(TAG, "Auto selecting filter " + mFilterOptions.get(0) 1190 + mContext.getText(mFilterOptions.get(0).getTitle())); 1191 } 1192 mManageApplications.mFilterSpinner.setSelection(0); 1193 mManageApplications.onItemSelected(null, null, 0, 0); 1194 } 1195 } 1196 } 1197 1198 @Override getCount()1199 public int getCount() { 1200 return mFilterOptions.size(); 1201 } 1202 1203 @Override getItem(int position)1204 public CharSequence getItem(int position) { 1205 return mContext.getText(mFilterOptions.get(position).getTitle()); 1206 } 1207 1208 @VisibleForTesting updateFilterView(boolean hasFilter)1209 void updateFilterView(boolean hasFilter) { 1210 // If we need to add a floating filter in this screen, we should have an extra top 1211 // padding for putting floating filter view. Otherwise, the content of list will be 1212 // overlapped by floating filter. 1213 if (hasFilter) { 1214 mManageApplications.mSpinnerHeader.setVisibility(View.VISIBLE); 1215 } else { 1216 mManageApplications.mSpinnerHeader.setVisibility(View.GONE); 1217 } 1218 } 1219 } 1220 1221 static class ApplicationsAdapter extends RecyclerView.Adapter<ApplicationViewHolder> 1222 implements ApplicationsState.Callbacks, AppStateBaseBridge.Callback { 1223 1224 private static final String STATE_LAST_SCROLL_INDEX = "state_last_scroll_index"; 1225 private static final int VIEW_TYPE_APP = 0; 1226 private static final int VIEW_TYPE_EXTRA_VIEW = 1; 1227 private static final int VIEW_TYPE_APP_HEADER = 2; 1228 private static final int VIEW_TYPE_TWO_TARGET = 3; 1229 1230 private final ApplicationsState mState; 1231 private final ApplicationsState.Session mSession; 1232 private final ManageApplications mManageApplications; 1233 private final Context mContext; 1234 private final AppStateBaseBridge mExtraInfoBridge; 1235 private final LoadingViewController mLoadingViewController; 1236 private final IconDrawableFactory mIconDrawableFactory; 1237 1238 private AppFilterItem mAppFilter; 1239 private ArrayList<ApplicationsState.AppEntry> mEntries; 1240 private ArrayList<ApplicationsState.AppEntry> mOriginalEntries; 1241 private boolean mResumed; 1242 private int mLastSortMode = -1; 1243 private int mWhichSize = SIZE_TOTAL; 1244 private AppFilter mCompositeFilter; 1245 private boolean mHasReceivedLoadEntries; 1246 private boolean mHasReceivedBridgeCallback; 1247 private SearchFilter mSearchFilter; 1248 private PowerAllowlistBackend mBackend; 1249 1250 // This is to remember and restore the last scroll position when this 1251 // fragment is paused. We need this special handling because app entries are added gradually 1252 // when we rebuild the list after the user made some changes, like uninstalling an app. 1253 private int mLastIndex = -1; 1254 1255 @VisibleForTesting 1256 OnScrollListener mOnScrollListener; 1257 private RecyclerView mRecyclerView; 1258 1259 ApplicationsAdapter(ApplicationsState state, ManageApplications manageApplications, AppFilterItem appFilter, Bundle savedInstanceState)1260 public ApplicationsAdapter(ApplicationsState state, ManageApplications manageApplications, 1261 AppFilterItem appFilter, Bundle savedInstanceState) { 1262 setHasStableIds(true); 1263 mState = state; 1264 mSession = state.newSession(this); 1265 mManageApplications = manageApplications; 1266 mLoadingViewController = new LoadingViewController( 1267 mManageApplications.mLoadingContainer, 1268 mManageApplications.mRecyclerView, 1269 mManageApplications.mEmptyView 1270 ); 1271 mContext = manageApplications.getActivity(); 1272 mIconDrawableFactory = IconDrawableFactory.newInstance(mContext); 1273 mAppFilter = appFilter; 1274 mBackend = PowerAllowlistBackend.getInstance(mContext); 1275 if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) { 1276 mExtraInfoBridge = new AppStateNotificationBridge(mContext, mState, this, 1277 manageApplications.mUsageStatsManager, 1278 manageApplications.mUserManager, 1279 manageApplications.mNotificationBackend); 1280 } else if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) { 1281 mExtraInfoBridge = new AppStateUsageBridge(mContext, mState, this); 1282 } else if (mManageApplications.mListType == LIST_TYPE_HIGH_POWER) { 1283 mBackend.refreshList(); 1284 mExtraInfoBridge = new AppStatePowerBridge(mContext, mState, this); 1285 } else if (mManageApplications.mListType == LIST_TYPE_OVERLAY) { 1286 mExtraInfoBridge = new AppStateOverlayBridge(mContext, mState, this); 1287 } else if (mManageApplications.mListType == LIST_TYPE_WRITE_SETTINGS) { 1288 mExtraInfoBridge = new AppStateWriteSettingsBridge(mContext, mState, this); 1289 } else if (mManageApplications.mListType == LIST_TYPE_MANAGE_SOURCES) { 1290 mExtraInfoBridge = new AppStateInstallAppsBridge(mContext, mState, this); 1291 } else if (mManageApplications.mListType == LIST_TYPE_WIFI_ACCESS) { 1292 mExtraInfoBridge = new AppStateChangeWifiStateBridge(mContext, mState, this); 1293 } else if (mManageApplications.mListType == LIST_MANAGE_EXTERNAL_STORAGE) { 1294 mExtraInfoBridge = new AppStateManageExternalStorageBridge(mContext, mState, this); 1295 } else if (mManageApplications.mListType == LIST_TYPE_ALARMS_AND_REMINDERS) { 1296 mExtraInfoBridge = new AppStateAlarmsAndRemindersBridge(mContext, mState, this); 1297 } else if (mManageApplications.mListType == LIST_TYPE_MEDIA_MANAGEMENT_APPS) { 1298 mExtraInfoBridge = new AppStateMediaManagementAppsBridge(mContext, mState, this); 1299 } else if (mManageApplications.mListType == LIST_TYPE_APPS_LOCALE) { 1300 mExtraInfoBridge = new AppStateLocaleBridge(mContext, mState, this, 1301 mManageApplications.mUserManager); 1302 } else if (mManageApplications.mListType == LIST_TYPE_BATTERY_OPTIMIZATION) { 1303 mExtraInfoBridge = new AppStateAppBatteryUsageBridge(mContext, mState, this); 1304 } else if (mManageApplications.mListType == LIST_TYPE_LONG_BACKGROUND_TASKS) { 1305 mExtraInfoBridge = new AppStateLongBackgroundTasksBridge(mContext, mState, this); 1306 } else if (mManageApplications.mListType == LIST_TYPE_CLONED_APPS) { 1307 mExtraInfoBridge = new AppStateClonedAppsBridge(mContext, mState, this); 1308 } else if (mManageApplications.mListType == LIST_TYPE_NFC_TAG_APPS) { 1309 mExtraInfoBridge = new AppStateNfcTagAppsBridge(mContext, mState, this); 1310 } else if (mManageApplications.mListType == LIST_TYPE_TURN_SCREEN_ON) { 1311 mExtraInfoBridge = new AppStateTurnScreenOnBridge(mContext, mState, this); 1312 } else { 1313 mExtraInfoBridge = null; 1314 } 1315 if (savedInstanceState != null) { 1316 mLastIndex = savedInstanceState.getInt(STATE_LAST_SCROLL_INDEX); 1317 } 1318 } 1319 1320 @Override onAttachedToRecyclerView(@onNull RecyclerView recyclerView)1321 public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { 1322 super.onAttachedToRecyclerView(recyclerView); 1323 final String className = 1324 mManageApplications.getClass().getName() + "_" + mManageApplications.mListType; 1325 mRecyclerView = recyclerView; 1326 mOnScrollListener = new OnScrollListener(this, className); 1327 mRecyclerView.addOnScrollListener(mOnScrollListener); 1328 } 1329 1330 @Override onDetachedFromRecyclerView(@onNull RecyclerView recyclerView)1331 public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) { 1332 super.onDetachedFromRecyclerView(recyclerView); 1333 mRecyclerView.removeOnScrollListener(mOnScrollListener); 1334 mOnScrollListener = null; 1335 mRecyclerView = null; 1336 } 1337 setCompositeFilter(AppFilter compositeFilter)1338 public void setCompositeFilter(AppFilter compositeFilter) { 1339 mCompositeFilter = compositeFilter; 1340 rebuild(); 1341 } 1342 setFilter(AppFilterItem appFilter)1343 public void setFilter(AppFilterItem appFilter) { 1344 mAppFilter = appFilter; 1345 final int filterType = appFilter.getFilterType(); 1346 1347 // Notification filters require resorting the list 1348 if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) { 1349 if (FILTER_APPS_FREQUENT == filterType) { 1350 rebuild(R.id.sort_order_frequent_notification, false); 1351 } else if (FILTER_APPS_RECENT == filterType) { 1352 rebuild(R.id.sort_order_recent_notification, false); 1353 } else if (FILTER_APPS_BLOCKED == filterType) { 1354 rebuild(R.id.sort_order_alpha, true); 1355 } else { 1356 rebuild(R.id.sort_order_alpha, true); 1357 } 1358 return; 1359 } 1360 1361 if (mManageApplications.mListType == LIST_TYPE_BATTERY_OPTIMIZATION) { 1362 logAppBatteryUsage(filterType); 1363 } 1364 1365 rebuild(); 1366 } 1367 resume(int sort)1368 public void resume(int sort) { 1369 if (DEBUG) Log.i(TAG, "Resume! mResumed=" + mResumed); 1370 if (!mResumed) { 1371 mResumed = true; 1372 mSession.onResume(); 1373 mLastSortMode = sort; 1374 if (mExtraInfoBridge != null) { 1375 mExtraInfoBridge.resume(false /* forceLoadAllApps */); 1376 } 1377 rebuild(); 1378 } else { 1379 rebuild(sort, false); 1380 } 1381 } 1382 pause()1383 public void pause() { 1384 if (mResumed) { 1385 mResumed = false; 1386 mSession.onPause(); 1387 if (mExtraInfoBridge != null) { 1388 mExtraInfoBridge.pause(); 1389 } 1390 } 1391 } 1392 onSaveInstanceState(Bundle outState)1393 public void onSaveInstanceState(Bundle outState) { 1394 // Record the current scroll position before pausing. 1395 final LinearLayoutManager layoutManager = 1396 (LinearLayoutManager) mManageApplications.mRecyclerView.getLayoutManager(); 1397 outState.putInt(STATE_LAST_SCROLL_INDEX, layoutManager.findFirstVisibleItemPosition()); 1398 } 1399 release()1400 public void release() { 1401 mSession.onDestroy(); 1402 if (mExtraInfoBridge != null) { 1403 mExtraInfoBridge.release(); 1404 } 1405 } 1406 rebuild(int sort, boolean force)1407 public void rebuild(int sort, boolean force) { 1408 if (sort == mLastSortMode && !force) { 1409 return; 1410 } 1411 mManageApplications.mSortOrder = sort; 1412 mLastSortMode = sort; 1413 rebuild(); 1414 } 1415 1416 @Override onCreateViewHolder(ViewGroup parent, int viewType)1417 public ApplicationViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 1418 final View view; 1419 if (mManageApplications.mListType == LIST_TYPE_APPS_LOCALE 1420 && viewType == VIEW_TYPE_APP_HEADER) { 1421 view = ApplicationViewHolder.newHeader(parent, 1422 R.string.desc_app_locale_selection_supported); 1423 } else if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) { 1424 view = ApplicationViewHolder.newView(parent, true /* twoTarget */, 1425 LIST_TYPE_NOTIFICATION); 1426 } else if (mManageApplications.mListType == LIST_TYPE_CLONED_APPS 1427 && viewType == VIEW_TYPE_APP_HEADER) { 1428 view = ApplicationViewHolder.newHeaderWithAnimation(mContext, parent, 1429 R.string.desc_cloned_apps_intro_text, R.raw.app_cloning, 1430 R.string.desc_cloneable_app_list_text); 1431 } else if (mManageApplications.mListType == LIST_TYPE_CLONED_APPS 1432 && viewType == VIEW_TYPE_TWO_TARGET) { 1433 view = ApplicationViewHolder.newView( 1434 parent, true, LIST_TYPE_CLONED_APPS); 1435 } else { 1436 view = ApplicationViewHolder.newView(parent, false /* twoTarget */, 1437 mManageApplications.mListType); 1438 } 1439 return new ApplicationViewHolder(view); 1440 } 1441 1442 @Override getItemViewType(int position)1443 public int getItemViewType(int position) { 1444 if (position == 0 && (mManageApplications.mListType == LIST_TYPE_APPS_LOCALE 1445 || mManageApplications.mListType == LIST_TYPE_CLONED_APPS)) { 1446 return VIEW_TYPE_APP_HEADER; 1447 } else if (mManageApplications.mListType == LIST_TYPE_CLONED_APPS) { 1448 return VIEW_TYPE_TWO_TARGET; 1449 } 1450 return VIEW_TYPE_APP; 1451 } 1452 rebuild()1453 public void rebuild() { 1454 if (!mHasReceivedLoadEntries 1455 || (mExtraInfoBridge != null && !mHasReceivedBridgeCallback)) { 1456 // Don't rebuild the list until all the app entries are loaded. 1457 if (DEBUG) { 1458 Log.d(TAG, "Not rebuilding until all the app entries loaded." 1459 + " !mHasReceivedLoadEntries=" + !mHasReceivedLoadEntries 1460 + " !mExtraInfoBridgeNull=" + (mExtraInfoBridge != null) 1461 + " !mHasReceivedBridgeCallback=" + !mHasReceivedBridgeCallback); 1462 } 1463 return; 1464 } 1465 ApplicationsState.AppFilter filterObj; 1466 Comparator<AppEntry> comparatorObj; 1467 boolean emulated = Environment.isExternalStorageEmulated(); 1468 if (emulated) { 1469 mWhichSize = SIZE_TOTAL; 1470 } else { 1471 mWhichSize = SIZE_INTERNAL; 1472 } 1473 filterObj = mAppFilter.getFilter(); 1474 if (mCompositeFilter != null) { 1475 filterObj = new CompoundFilter(filterObj, mCompositeFilter); 1476 } 1477 if (!mManageApplications.mShowSystem) { 1478 if (LIST_TYPES_WITH_INSTANT.contains(mManageApplications.mListType)) { 1479 filterObj = new CompoundFilter(filterObj, 1480 ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT); 1481 } else { 1482 filterObj = new CompoundFilter(filterObj, 1483 ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER); 1484 } 1485 } 1486 if (mLastSortMode == R.id.sort_order_size) { 1487 switch (mWhichSize) { 1488 case SIZE_INTERNAL: 1489 comparatorObj = ApplicationsState.INTERNAL_SIZE_COMPARATOR; 1490 break; 1491 case SIZE_EXTERNAL: 1492 comparatorObj = ApplicationsState.EXTERNAL_SIZE_COMPARATOR; 1493 break; 1494 default: 1495 comparatorObj = ApplicationsState.SIZE_COMPARATOR; 1496 break; 1497 } 1498 } else if (mLastSortMode == R.id.sort_order_recent_notification) { 1499 comparatorObj = AppStateNotificationBridge.RECENT_NOTIFICATION_COMPARATOR; 1500 } else if (mLastSortMode == R.id.sort_order_frequent_notification) { 1501 comparatorObj = AppStateNotificationBridge.FREQUENCY_NOTIFICATION_COMPARATOR; 1502 } else { 1503 comparatorObj = ApplicationsState.ALPHA_COMPARATOR; 1504 } 1505 1506 final AppFilter finalFilterObj = new CompoundFilter(filterObj, 1507 ApplicationsState.FILTER_NOT_HIDE); 1508 ThreadUtils.postOnBackgroundThread(() -> { 1509 mSession.rebuild(finalFilterObj, comparatorObj, false); 1510 }); 1511 } 1512 logAppBatteryUsage(int filterType)1513 private void logAppBatteryUsage(int filterType) { 1514 switch (filterType) { 1515 case FILTER_APPS_BATTERY_UNRESTRICTED: 1516 logAction(SettingsEnums.ACTION_BATTERY_OPTIMIZED_APPS_FILTER_UNRESTRICTED); 1517 break; 1518 case FILTER_APPS_BATTERY_OPTIMIZED: 1519 logAction(SettingsEnums.ACTION_BATTERY_OPTIMIZED_APPS_FILTER_OPTIMIZED); 1520 break; 1521 case FILTER_APPS_BATTERY_RESTRICTED: 1522 logAction(SettingsEnums.ACTION_BATTERY_OPTIMIZED_APPS_FILTER_RESTRICTED); 1523 break; 1524 default: 1525 logAction(SettingsEnums.ACTION_BATTERY_OPTIMIZED_APPS_FILTER_ALL_APPS); 1526 } 1527 } 1528 logAction(int action)1529 private void logAction(int action) { 1530 mManageApplications.mMetricsFeatureProvider.action(mContext, action); 1531 } 1532 1533 @VisibleForTesting filterSearch(String query)1534 void filterSearch(String query) { 1535 if (mSearchFilter == null) { 1536 mSearchFilter = new SearchFilter(); 1537 } 1538 // If we haven't load apps list completely, don't filter anything. 1539 if (mOriginalEntries == null) { 1540 Log.w(TAG, "Apps haven't loaded completely yet, so nothing can be filtered"); 1541 return; 1542 } 1543 mSearchFilter.filter(query); 1544 } 1545 packageNameEquals(PackageItemInfo info1, PackageItemInfo info2)1546 private static boolean packageNameEquals(PackageItemInfo info1, PackageItemInfo info2) { 1547 if (info1 == null || info2 == null) { 1548 return false; 1549 } 1550 if (info1.packageName == null || info2.packageName == null) { 1551 return false; 1552 } 1553 return info1.packageName.equals(info2.packageName); 1554 } 1555 removeDuplicateIgnoringUser( ArrayList<ApplicationsState.AppEntry> entries)1556 private ArrayList<ApplicationsState.AppEntry> removeDuplicateIgnoringUser( 1557 ArrayList<ApplicationsState.AppEntry> entries) { 1558 int size = entries.size(); 1559 // returnList will not have more entries than entries 1560 ArrayList<ApplicationsState.AppEntry> returnEntries = new ArrayList<>(size); 1561 1562 // assume appinfo of same package but different users are grouped together 1563 PackageItemInfo lastInfo = null; 1564 for (int i = 0; i < size; i++) { 1565 AppEntry appEntry = entries.get(i); 1566 PackageItemInfo info = appEntry.info; 1567 if (!packageNameEquals(lastInfo, appEntry.info)) { 1568 returnEntries.add(appEntry); 1569 } 1570 lastInfo = info; 1571 } 1572 returnEntries.trimToSize(); 1573 return returnEntries; 1574 } 1575 1576 @Override onRebuildComplete(ArrayList<AppEntry> entries)1577 public void onRebuildComplete(ArrayList<AppEntry> entries) { 1578 if (DEBUG) { 1579 Log.d(TAG, "onRebuildComplete size=" + entries.size()); 1580 } 1581 1582 // Preload top visible icons of app list. 1583 AppUtils.preloadTopIcons(mContext, entries, 1584 mContext.getResources().getInteger(R.integer.config_num_visible_app_icons)); 1585 1586 final int filterType = mAppFilter.getFilterType(); 1587 if (filterType == FILTER_APPS_POWER_ALLOWLIST 1588 || filterType == FILTER_APPS_POWER_ALLOWLIST_ALL) { 1589 entries = removeDuplicateIgnoringUser(entries); 1590 } 1591 mEntries = entries; 1592 mOriginalEntries = entries; 1593 notifyDataSetChanged(); 1594 if (getItemCount() == 0) { 1595 mLoadingViewController.showEmpty(false /* animate */); 1596 } else { 1597 mLoadingViewController.showContent(false /* animate */); 1598 1599 if (mManageApplications.mSearchView != null 1600 && mManageApplications.mSearchView.isVisibleToUser()) { 1601 final CharSequence query = mManageApplications.mSearchView.getQuery(); 1602 if (!TextUtils.isEmpty(query)) { 1603 filterSearch(query.toString()); 1604 } 1605 } 1606 } 1607 // Restore the last scroll position if the number of entries added so far is bigger than 1608 // it. 1609 if (mLastIndex != -1 && getItemCount() > mLastIndex) { 1610 mManageApplications.mRecyclerView.getLayoutManager().scrollToPosition(mLastIndex); 1611 mLastIndex = -1; 1612 } 1613 1614 if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) { 1615 // No enabled or disabled filters for usage access. 1616 return; 1617 } 1618 1619 mManageApplications.setHasDisabled(mState.haveDisabledApps()); 1620 mManageApplications.setHasInstant(mState.haveInstantApps()); 1621 } 1622 1623 @VisibleForTesting updateLoading()1624 void updateLoading() { 1625 final boolean appLoaded = mHasReceivedLoadEntries && mSession.getAllApps().size() != 0; 1626 if (appLoaded) { 1627 mLoadingViewController.showContent(false /* animate */); 1628 } else { 1629 mLoadingViewController.showLoadingViewDelayed(); 1630 } 1631 } 1632 1633 @Override onExtraInfoUpdated()1634 public void onExtraInfoUpdated() { 1635 mHasReceivedBridgeCallback = true; 1636 rebuild(); 1637 } 1638 1639 @Override onRunningStateChanged(boolean running)1640 public void onRunningStateChanged(boolean running) { 1641 mManageApplications.getActivity().setProgressBarIndeterminateVisibility(running); 1642 } 1643 1644 @Override onPackageListChanged()1645 public void onPackageListChanged() { 1646 rebuild(); 1647 } 1648 1649 @Override onPackageIconChanged()1650 public void onPackageIconChanged() { 1651 // We ensure icons are loaded when their item is displayed, so 1652 // don't care about icons loaded in the background. 1653 } 1654 1655 @Override onLoadEntriesCompleted()1656 public void onLoadEntriesCompleted() { 1657 mHasReceivedLoadEntries = true; 1658 // We may have been skipping rebuilds until this came in, trigger one now. 1659 rebuild(); 1660 } 1661 1662 @Override onPackageSizeChanged(String packageName)1663 public void onPackageSizeChanged(String packageName) { 1664 if (mEntries == null) { 1665 return; 1666 } 1667 final int size = mEntries.size(); 1668 for (int i = 0; i < size; i++) { 1669 final AppEntry entry = mEntries.get(i); 1670 final ApplicationInfo info = entry.info; 1671 if (info == null && !TextUtils.equals(packageName, info.packageName)) { 1672 continue; 1673 } 1674 if (TextUtils.equals(mManageApplications.mCurrentPkgName, info.packageName)) { 1675 // We got the size information for the last app the 1676 // user viewed, and are sorting by size... they may 1677 // have cleared data, so we immediately want to resort 1678 // the list with the new size to reflect it to the user. 1679 rebuild(); 1680 return; 1681 } else { 1682 mOnScrollListener.postNotifyItemChange(i); 1683 } 1684 } 1685 } 1686 1687 @Override onLauncherInfoChanged()1688 public void onLauncherInfoChanged() { 1689 if (!mManageApplications.mShowSystem) { 1690 rebuild(); 1691 } 1692 } 1693 1694 @Override onAllSizesComputed()1695 public void onAllSizesComputed() { 1696 if (mLastSortMode == R.id.sort_order_size) { 1697 rebuild(); 1698 } 1699 } 1700 1701 /** 1702 * Item count include all items. If UI has a header on the app list, it shall shift 1 to 1703 * application count for the total item count. 1704 */ 1705 @Override getItemCount()1706 public int getItemCount() { 1707 int count = getApplicationCount(); 1708 if (count != 0 && (mManageApplications.mListType == LIST_TYPE_APPS_LOCALE 1709 || mManageApplications.mListType == LIST_TYPE_CLONED_APPS)) { 1710 count++; 1711 } 1712 return count; 1713 } 1714 getApplicationCount()1715 public int getApplicationCount() { 1716 return mEntries != null ? mEntries.size() : 0; 1717 } 1718 getAppEntry(int applicationPosition)1719 public AppEntry getAppEntry(int applicationPosition) { 1720 return mEntries.get(applicationPosition); 1721 } 1722 1723 /** 1724 * Item Id follows all item on the app list. If UI has a header on the list, it shall 1725 * shift 1 to the position for correct app entry. 1726 */ 1727 @Override getItemId(int position)1728 public long getItemId(int position) { 1729 int applicationPosition = 1730 getApplicationPosition(mManageApplications.mListType, position); 1731 if (applicationPosition == mEntries.size() 1732 || applicationPosition == RecyclerView.NO_POSITION) { 1733 return -1; 1734 } 1735 return mEntries.get(applicationPosition).id; 1736 } 1737 1738 /** 1739 * Check item in the list shall enable or disable. 1740 * 1741 * @param position The item position in the list 1742 */ isEnabled(int position)1743 public boolean isEnabled(int position) { 1744 int itemViewType = getItemViewType(position); 1745 if (itemViewType == VIEW_TYPE_EXTRA_VIEW || itemViewType == VIEW_TYPE_APP_HEADER 1746 || mManageApplications.mListType != LIST_TYPE_HIGH_POWER) { 1747 return true; 1748 } 1749 1750 int applicationPosition = 1751 getApplicationPosition(mManageApplications.mListType, position); 1752 if (applicationPosition == RecyclerView.NO_POSITION) { 1753 return true; 1754 } 1755 ApplicationsState.AppEntry entry = mEntries.get(applicationPosition); 1756 1757 return !mBackend.isSysAllowlisted(entry.info.packageName) 1758 && !mBackend.isDefaultActiveApp(entry.info.packageName, entry.info.uid); 1759 } 1760 1761 @Override onBindViewHolder(ApplicationViewHolder holder, int position)1762 public void onBindViewHolder(ApplicationViewHolder holder, int position) { 1763 if (getItemViewType(position) == VIEW_TYPE_APP_HEADER) { 1764 // It does not bind holder here, due to header view. 1765 return; 1766 } 1767 1768 int applicationPosition = 1769 getApplicationPosition(mManageApplications.mListType, position); 1770 if (applicationPosition == RecyclerView.NO_POSITION) { 1771 return; 1772 } 1773 // Bind the data efficiently with the holder 1774 // If there is a header on the list, the position shall be shifted. Thus, it shall use 1775 // #getApplicationPosition to get real application position for the app entry. 1776 final ApplicationsState.AppEntry entry = mEntries.get(applicationPosition); 1777 1778 synchronized (entry) { 1779 mState.ensureLabelDescription(entry); 1780 holder.setTitle(entry.label, entry.labelDescription); 1781 updateIcon(holder, entry); 1782 updateSummary(holder, entry); 1783 updateSwitch(holder, entry); 1784 holder.updateDisableView(entry.info); 1785 } 1786 holder.setEnabled(isEnabled(position)); 1787 1788 holder.itemView.setOnClickListener(mManageApplications); 1789 } 1790 updateIcon(ApplicationViewHolder holder, AppEntry entry)1791 private void updateIcon(ApplicationViewHolder holder, AppEntry entry) { 1792 final Drawable cachedIcon = AppUtils.getIconFromCache(entry); 1793 if (cachedIcon != null && entry.mounted) { 1794 holder.setIcon(cachedIcon); 1795 } else { 1796 ThreadUtils.postOnBackgroundThread(() -> { 1797 final Drawable icon = AppUtils.getIcon(mContext, entry); 1798 if (icon != null) { 1799 ThreadUtils.postOnMainThread(() -> holder.setIcon(icon)); 1800 } 1801 }); 1802 } 1803 } 1804 updateSummary(ApplicationViewHolder holder, AppEntry entry)1805 private void updateSummary(ApplicationViewHolder holder, AppEntry entry) { 1806 switch (mManageApplications.mListType) { 1807 case LIST_TYPE_NOTIFICATION: 1808 if (entry.extraInfo != null 1809 && entry.extraInfo instanceof NotificationsSentState) { 1810 holder.setSummary(AppStateNotificationBridge.getSummary(mContext, 1811 (NotificationsSentState) entry.extraInfo, mLastSortMode)); 1812 } else { 1813 holder.setSummary(null); 1814 } 1815 break; 1816 case LIST_TYPE_USAGE_ACCESS: 1817 if (entry.extraInfo != null) { 1818 holder.setSummary( 1819 (new UsageState((PermissionState) entry.extraInfo)).isPermissible() 1820 ? R.string.app_permission_summary_allowed 1821 : R.string.app_permission_summary_not_allowed); 1822 } else { 1823 holder.setSummary(null); 1824 } 1825 break; 1826 case LIST_TYPE_HIGH_POWER: 1827 holder.setSummary(HighPowerDetail.getSummary(mContext, entry)); 1828 break; 1829 case LIST_TYPE_OVERLAY: 1830 holder.setSummary(DrawOverlayDetails.getSummary(mContext, entry)); 1831 break; 1832 case LIST_TYPE_WRITE_SETTINGS: 1833 holder.setSummary(WriteSettingsDetails.getSummary(mContext, entry)); 1834 break; 1835 case LIST_TYPE_MANAGE_SOURCES: 1836 holder.setSummary(ExternalSourcesDetails.getPreferenceSummary(mContext, entry)); 1837 break; 1838 case LIST_TYPE_WIFI_ACCESS: 1839 holder.setSummary(ChangeWifiStateDetails.getSummary(mContext, entry)); 1840 break; 1841 case LIST_MANAGE_EXTERNAL_STORAGE: 1842 holder.setSummary(ManageExternalStorageDetails.getSummary(mContext, entry)); 1843 break; 1844 case LIST_TYPE_ALARMS_AND_REMINDERS: 1845 holder.setSummary(AlarmsAndRemindersDetails.getSummary(mContext, entry)); 1846 break; 1847 case LIST_TYPE_MEDIA_MANAGEMENT_APPS: 1848 holder.setSummary(MediaManagementAppsDetails.getSummary(mContext, entry)); 1849 break; 1850 case LIST_TYPE_APPS_LOCALE: 1851 holder.setSummary(AppLocaleDetails.getSummary(mContext, entry.info)); 1852 break; 1853 case LIST_TYPE_BATTERY_OPTIMIZATION: 1854 holder.setSummary(null); 1855 break; 1856 case LIST_TYPE_LONG_BACKGROUND_TASKS: 1857 holder.setSummary(LongBackgroundTasksDetails.getSummary(mContext, entry)); 1858 break; 1859 case LIST_TYPE_CLONED_APPS: 1860 holder.setSummary(null); 1861 break; 1862 case LIST_TYPE_NFC_TAG_APPS: 1863 holder.setSummary( 1864 ChangeNfcTagAppsStateDetails.getSummary(mContext, entry)); 1865 break; 1866 case LIST_TYPE_TURN_SCREEN_ON: 1867 holder.setSummary(TurnScreenOnDetails.getSummary(mContext, entry)); 1868 break; 1869 default: 1870 holder.updateSizeText(entry, mManageApplications.mInvalidSizeStr, mWhichSize); 1871 break; 1872 } 1873 } 1874 updateSwitch(ApplicationViewHolder holder, AppEntry entry)1875 private void updateSwitch(ApplicationViewHolder holder, AppEntry entry) { 1876 switch (mManageApplications.mListType) { 1877 case LIST_TYPE_NOTIFICATION: 1878 holder.updateSwitch(((AppStateNotificationBridge) mExtraInfoBridge) 1879 .getSwitchOnCheckedListener(entry), 1880 AppStateNotificationBridge.enableSwitch(entry), 1881 AppStateNotificationBridge.checkSwitch(entry)); 1882 if (entry.extraInfo != null 1883 && entry.extraInfo instanceof NotificationsSentState) { 1884 holder.setSummary(AppStateNotificationBridge.getSummary(mContext, 1885 (NotificationsSentState) entry.extraInfo, mLastSortMode)); 1886 } else { 1887 holder.setSummary(null); 1888 } 1889 break; 1890 case LIST_TYPE_CLONED_APPS: 1891 holder.updateAppCloneWidget(mContext, 1892 holder.appCloneOnClickListener(entry, this, 1893 mManageApplications.getActivity()), entry); 1894 break; 1895 } 1896 } 1897 1898 /** 1899 * Adjusts position if this list adds a header. 1900 * TODO(b/232533002) Add a header view on adapter of RecyclerView may not a good idea since 1901 * ManageApplication is a generic purpose. In the future, here shall look for 1902 * a better way to add a header without using recyclerView or any other ways 1903 * to achieve the goal. 1904 */ getApplicationPosition(int listType, int position)1905 public static int getApplicationPosition(int listType, int position) { 1906 int applicationPosition = position; 1907 // Adjust position due to header added. 1908 if (listType == LIST_TYPE_APPS_LOCALE || listType == LIST_TYPE_CLONED_APPS) { 1909 applicationPosition = position > 0 ? position - 1 : RecyclerView.NO_POSITION; 1910 } 1911 return applicationPosition; 1912 } 1913 1914 public static class OnScrollListener extends RecyclerView.OnScrollListener { 1915 private int mScrollState = SCROLL_STATE_IDLE; 1916 private boolean mDelayNotifyDataChange; 1917 private ApplicationsAdapter mAdapter; 1918 private InputMethodManager mInputMethodManager; 1919 private InteractionJankMonitor mMonitor; 1920 private String mClassName; 1921 OnScrollListener(ApplicationsAdapter adapter, String className)1922 public OnScrollListener(ApplicationsAdapter adapter, String className) { 1923 mAdapter = adapter; 1924 mInputMethodManager = mAdapter.mContext.getSystemService( 1925 InputMethodManager.class); 1926 mMonitor = InteractionJankMonitor.getInstance(); 1927 mClassName = className; 1928 } 1929 1930 @Override onScrollStateChanged(@onNull RecyclerView recyclerView, int newState)1931 public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { 1932 mScrollState = newState; 1933 if (mScrollState == SCROLL_STATE_IDLE && mDelayNotifyDataChange) { 1934 mDelayNotifyDataChange = false; 1935 mAdapter.notifyDataSetChanged(); 1936 } else if (mScrollState == SCROLL_STATE_DRAGGING) { 1937 // Hide keyboard when user start scrolling 1938 if (mInputMethodManager != null && mInputMethodManager.isActive()) { 1939 mInputMethodManager.hideSoftInputFromWindow(recyclerView.getWindowToken(), 1940 0); 1941 } 1942 // Start jank monitoring during page scrolling. 1943 final InteractionJankMonitor.Configuration.Builder builder = 1944 InteractionJankMonitor.Configuration.Builder.withView( 1945 CUJ_SETTINGS_PAGE_SCROLL, recyclerView) 1946 .setTag(mClassName); 1947 mMonitor.begin(builder); 1948 } else if (mScrollState == SCROLL_STATE_IDLE) { 1949 // Stop jank monitoring on page scrolling. 1950 mMonitor.end(CUJ_SETTINGS_PAGE_SCROLL); 1951 } 1952 } 1953 postNotifyItemChange(int index)1954 public void postNotifyItemChange(int index) { 1955 if (mScrollState == SCROLL_STATE_IDLE) { 1956 mAdapter.notifyItemChanged(index); 1957 } else { 1958 mDelayNotifyDataChange = true; 1959 } 1960 } 1961 } 1962 1963 /** 1964 * An array filter that constrains the content of the array adapter with a substring. 1965 * Item that does not contains the specified substring will be removed from the list.</p> 1966 */ 1967 private class SearchFilter extends Filter { 1968 @WorkerThread 1969 @Override performFiltering(CharSequence query)1970 protected FilterResults performFiltering(CharSequence query) { 1971 final ArrayList<ApplicationsState.AppEntry> matchedEntries; 1972 if (TextUtils.isEmpty(query)) { 1973 matchedEntries = mOriginalEntries; 1974 } else { 1975 matchedEntries = new ArrayList<>(); 1976 for (ApplicationsState.AppEntry entry : mOriginalEntries) { 1977 if (entry.label.toLowerCase().contains(query.toString().toLowerCase())) { 1978 matchedEntries.add(entry); 1979 } 1980 } 1981 } 1982 final FilterResults results = new FilterResults(); 1983 results.values = matchedEntries; 1984 results.count = matchedEntries.size(); 1985 return results; 1986 } 1987 1988 @Override publishResults(CharSequence constraint, FilterResults results)1989 protected void publishResults(CharSequence constraint, FilterResults results) { 1990 mEntries = (ArrayList<ApplicationsState.AppEntry>) results.values; 1991 notifyDataSetChanged(); 1992 } 1993 } 1994 } 1995 } 1996