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