blob: b23a9670e6680ece3d9761a618992d11c11e08f7 [file] [log] [blame]
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications.manageapplications;
import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
import static com.android.settings.applications.manageapplications.AppFilterRegistry
.FILTER_APPS_ALL;
import static com.android.settings.applications.manageapplications.AppFilterRegistry
.FILTER_APPS_DISABLED;
import static com.android.settings.applications.manageapplications.AppFilterRegistry
.FILTER_APPS_ENABLED;
import static com.android.settings.applications.manageapplications.AppFilterRegistry
.FILTER_APPS_FREQUENT;
import static com.android.settings.applications.manageapplications.AppFilterRegistry
.FILTER_APPS_INSTANT;
import static com.android.settings.applications.manageapplications.AppFilterRegistry
.FILTER_APPS_PERSONAL;
import static com.android.settings.applications.manageapplications.AppFilterRegistry
.FILTER_APPS_POWER_WHITELIST;
import static com.android.settings.applications.manageapplications.AppFilterRegistry
.FILTER_APPS_POWER_WHITELIST_ALL;
import static com.android.settings.applications.manageapplications.AppFilterRegistry
.FILTER_APPS_RECENT;
import static com.android.settings.applications.manageapplications.AppFilterRegistry
.FILTER_APPS_WORK;
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.app.Activity;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageStatsManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageItemInfo;
import android.os.Bundle;
import android.os.Environment;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.preference.PreferenceFrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.FrameLayout;
import android.widget.Spinner;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.Settings;
import com.android.settings.Settings.GamesStorageActivity;
import com.android.settings.Settings.HighPowerApplicationsActivity;
import com.android.settings.Settings.ManageExternalSourcesActivity;
import com.android.settings.Settings.MoviesStorageActivity;
import com.android.settings.Settings.OverlaySettingsActivity;
import com.android.settings.Settings.StorageUseActivity;
import com.android.settings.Settings.UsageAccessSettingsActivity;
import com.android.settings.Settings.WriteSettingsActivity;
import com.android.settings.SettingsActivity;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
import com.android.settings.applications.AppStateBaseBridge;
import com.android.settings.applications.AppStateDirectoryAccessBridge;
import com.android.settings.applications.AppStateInstallAppsBridge;
import com.android.settings.applications.AppStateNotificationBridge;
import com.android.settings.applications.AppStateNotificationBridge.NotificationsSentState;
import com.android.settings.applications.AppStateOverlayBridge;
import com.android.settings.applications.AppStatePowerBridge;
import com.android.settings.applications.AppStateUsageBridge;
import com.android.settings.applications.AppStateUsageBridge.UsageState;
import com.android.settings.applications.AppStateWriteSettingsBridge;
import com.android.settings.applications.AppStorageSettings;
import com.android.settings.applications.DefaultAppSettings;
import com.android.settings.applications.DirectoryAccessDetails;
import com.android.settings.applications.InstalledAppCounter;
import com.android.settings.applications.UsageAccessDetails;
import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
import com.android.settings.applications.appinfo.DrawOverlayDetails;
import com.android.settings.applications.appinfo.ExternalSourcesDetails;
import com.android.settings.applications.appinfo.WriteSettingsDetails;
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.fuelgauge.HighPowerDetail;
import com.android.settings.notification.AppNotificationSettings;
import com.android.settings.notification.ConfigureNotificationSettings;
import com.android.settings.notification.NotificationBackend;
import com.android.settings.widget.LoadingViewController;
import com.android.settings.wifi.AppStateChangeWifiStateBridge;
import com.android.settings.wifi.ChangeWifiStateDetails;
import com.android.settingslib.HelpUtils;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.AppFilter;
import com.android.settingslib.applications.ApplicationsState.CompoundFilter;
import com.android.settingslib.applications.ApplicationsState.VolumeFilter;
import com.android.settingslib.applications.StorageStatsSource;
import com.android.settingslib.fuelgauge.PowerWhitelistBackend;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.wrapper.PackageManagerWrapper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Set;
/**
* Activity to pick an application that will be used to display installation information and
* options to uninstall/delete user data for system applications. This activity
* can be launched through Settings or via the ACTION_MANAGE_PACKAGE_STORAGE
* intent.
*/
public class ManageApplications extends InstrumentedFragment
implements View.OnClickListener, OnItemSelectedListener {
static final String TAG = "ManageApplications";
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
// Intent extras.
public static final String EXTRA_CLASSNAME = "classname";
// Used for storage only.
public static final String EXTRA_VOLUME_UUID = "volumeUuid";
public static final String EXTRA_VOLUME_NAME = "volumeName";
public static final String EXTRA_STORAGE_TYPE = "storageType";
public static final String EXTRA_WORK_ONLY = "workProfileOnly";
public static final String EXTRA_WORK_ID = "workId";
private static final String EXTRA_SORT_ORDER = "sortOrder";
private static final String EXTRA_SHOW_SYSTEM = "showSystem";
private static final String EXTRA_HAS_ENTRIES = "hasEntries";
private static final String EXTRA_HAS_BRIDGE = "hasBridge";
// attributes used as keys when passing values to AppInfoDashboardFragment activity
public static final String APP_CHG = "chg";
// constant value that can be used to check return code from sub activity.
private static final int INSTALLED_APP_DETAILS = 1;
private static final int ADVANCED_SETTINGS = 2;
public static final int SIZE_TOTAL = 0;
public static final int SIZE_INTERNAL = 1;
public static final int SIZE_EXTERNAL = 2;
// Storage types. Used to determine what the extra item in the list of preferences is.
public static final int STORAGE_TYPE_DEFAULT = 0; // Show all apps that are not categorized.
public static final int STORAGE_TYPE_MUSIC = 1;
public static final int STORAGE_TYPE_LEGACY = 2; // Show apps even if they can be categorized.
public static final int STORAGE_TYPE_PHOTOS_VIDEOS = 3;
private static final int NO_USER_SPECIFIED = -1;
// sort order
@VisibleForTesting
int mSortOrder = R.id.sort_order_alpha;
// whether showing system apps.
private boolean mShowSystem;
private ApplicationsState mApplicationsState;
public int mListType;
private AppFilterItem mFilter;
private ApplicationsAdapter mApplications;
private View mLoadingContainer;
private View mListContainer;
private RecyclerView mRecyclerView;
// Size resource used for packages whose size computation failed for some reason
CharSequence mInvalidSizeStr;
private String mCurrentPkgName;
private int mCurrentUid;
private Menu mOptionsMenu;
public static final int LIST_TYPE_MAIN = 0;
public static final int LIST_TYPE_NOTIFICATION = 1;
public static final int LIST_TYPE_STORAGE = 3;
public static final int LIST_TYPE_USAGE_ACCESS = 4;
public static final int LIST_TYPE_HIGH_POWER = 5;
public static final int LIST_TYPE_OVERLAY = 6;
public static final int LIST_TYPE_WRITE_SETTINGS = 7;
public static final int LIST_TYPE_MANAGE_SOURCES = 8;
public static final int LIST_TYPE_GAMES = 9;
public static final int LIST_TYPE_MOVIES = 10;
public static final int LIST_TYPE_PHOTOGRAPHY = 11;
public static final int LIST_TYPE_DIRECTORY_ACCESS = 12;
public static final int LIST_TYPE_WIFI_ACCESS = 13;
// List types that should show instant apps.
public static final Set<Integer> LIST_TYPES_WITH_INSTANT = new ArraySet<>(Arrays.asList(
LIST_TYPE_MAIN,
LIST_TYPE_STORAGE));
private View mRootView;
private View mSpinnerHeader;
private Spinner mFilterSpinner;
private FilterSpinnerAdapter mFilterAdapter;
private IUsageStatsManager mUsageStatsManager;
private UserManager mUserManager;
private NotificationBackend mNotificationBackend;
private ResetAppsHelper mResetAppsHelper;
private String mVolumeUuid;
private int mStorageType;
private boolean mIsWorkOnly;
private int mWorkUserId;
private View mEmptyView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
final Activity activity = getActivity();
mApplicationsState = ApplicationsState.getInstance(activity.getApplication());
Intent intent = activity.getIntent();
Bundle args = getArguments();
int screenTitle = intent.getIntExtra(
SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, R.string.application_info_label);
String className = args != null ? args.getString(EXTRA_CLASSNAME) : null;
if (className == null) {
className = intent.getComponent().getClassName();
}
if (className.equals(StorageUseActivity.class.getName())) {
if (args != null && args.containsKey(EXTRA_VOLUME_UUID)) {
mVolumeUuid = args.getString(EXTRA_VOLUME_UUID);
mStorageType = args.getInt(EXTRA_STORAGE_TYPE, STORAGE_TYPE_DEFAULT);
mListType = LIST_TYPE_STORAGE;
} else {
// No volume selected, display a normal list, sorted by size.
mListType = LIST_TYPE_MAIN;
}
mSortOrder = R.id.sort_order_size;
} else if (className.equals(UsageAccessSettingsActivity.class.getName())) {
mListType = LIST_TYPE_USAGE_ACCESS;
screenTitle = R.string.usage_access;
} else if (className.equals(HighPowerApplicationsActivity.class.getName())) {
mListType = LIST_TYPE_HIGH_POWER;
// Default to showing system.
mShowSystem = true;
screenTitle = R.string.high_power_apps;
} else if (className.equals(OverlaySettingsActivity.class.getName())) {
mListType = LIST_TYPE_OVERLAY;
screenTitle = R.string.system_alert_window_settings;
} else if (className.equals(WriteSettingsActivity.class.getName())) {
mListType = LIST_TYPE_WRITE_SETTINGS;
screenTitle = R.string.write_settings;
} else if (className.equals(ManageExternalSourcesActivity.class.getName())) {
mListType = LIST_TYPE_MANAGE_SOURCES;
screenTitle = R.string.install_other_apps;
} else if (className.equals(GamesStorageActivity.class.getName())) {
mListType = LIST_TYPE_GAMES;
mSortOrder = R.id.sort_order_size;
} else if (className.equals(MoviesStorageActivity.class.getName())) {
mListType = LIST_TYPE_MOVIES;
mSortOrder = R.id.sort_order_size;
} else if (className.equals(Settings.PhotosStorageActivity.class.getName())) {
mListType = LIST_TYPE_PHOTOGRAPHY;
mSortOrder = R.id.sort_order_size;
mStorageType = args.getInt(EXTRA_STORAGE_TYPE, STORAGE_TYPE_DEFAULT);
} else if (className.equals(Settings.DirectoryAccessSettingsActivity.class.getName())) {
mListType = LIST_TYPE_DIRECTORY_ACCESS;
screenTitle = R.string.directory_access;
} else if (className.equals(Settings.ChangeWifiStateActivity.class.getName())) {
mListType = LIST_TYPE_WIFI_ACCESS;
screenTitle = R.string.change_wifi_state_title;
} else if (className.equals(Settings.NotificationAppListActivity.class.getName())) {
mListType = LIST_TYPE_NOTIFICATION;
mUsageStatsManager = IUsageStatsManager.Stub.asInterface(
ServiceManager.getService(Context.USAGE_STATS_SERVICE));
mUserManager = UserManager.get(getContext());
mNotificationBackend = new NotificationBackend();
mSortOrder = R.id.sort_order_recent_notification;
screenTitle = R.string.app_notifications_title;
} else {
if (screenTitle == -1) {
screenTitle = R.string.application_info_label;
}
mListType = LIST_TYPE_MAIN;
}
final AppFilterRegistry appFilterRegistry = AppFilterRegistry.getInstance();
mFilter = appFilterRegistry.get(appFilterRegistry.getDefaultFilterType(mListType));
mIsWorkOnly = args != null ? args.getBoolean(EXTRA_WORK_ONLY) : false;
mWorkUserId = args != null ? args.getInt(EXTRA_WORK_ID) : NO_USER_SPECIFIED;
if (savedInstanceState != null) {
mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder);
mShowSystem = savedInstanceState.getBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
}
mInvalidSizeStr = activity.getText(R.string.invalid_size_value);
mResetAppsHelper = new ResetAppsHelper(activity);
if (screenTitle > 0) {
activity.setTitle(screenTitle);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mRootView = inflater.inflate(R.layout.manage_applications_apps, null);
mLoadingContainer = mRootView.findViewById(R.id.loading_container);
mListContainer = mRootView.findViewById(R.id.list_container);
if (mListContainer != null) {
// Create adapter and list view here
mEmptyView = mListContainer.findViewById(android.R.id.empty);
mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter,
savedInstanceState);
if (savedInstanceState != null) {
mApplications.mHasReceivedLoadEntries =
savedInstanceState.getBoolean(EXTRA_HAS_ENTRIES, false);
mApplications.mHasReceivedBridgeCallback =
savedInstanceState.getBoolean(EXTRA_HAS_BRIDGE, false);
}
int userId = mIsWorkOnly ? mWorkUserId : UserHandle.getUserId(mCurrentUid);
if (mStorageType == STORAGE_TYPE_MUSIC) {
Context context = getContext();
mApplications.setExtraViewController(
new MusicViewHolderController(
context,
new StorageStatsSource(context),
mVolumeUuid,
UserHandle.of(userId)));
} else if (mStorageType == STORAGE_TYPE_PHOTOS_VIDEOS) {
Context context = getContext();
mApplications.setExtraViewController(
new PhotosViewHolderController(
context,
new StorageStatsSource(context),
mVolumeUuid,
UserHandle.of(userId)));
}
mRecyclerView = mListContainer.findViewById(R.id.apps_list);
mRecyclerView.setLayoutManager(new LinearLayoutManager(
getContext(), RecyclerView.VERTICAL, false /* reverseLayout */));
mRecyclerView.setAdapter(mApplications);
}
// We have to do this now because PreferenceFrameLayout looks at it
// only when the view is added.
if (container instanceof PreferenceFrameLayout) {
((PreferenceFrameLayout.LayoutParams) mRootView.getLayoutParams()).removeBorders = true;
}
createHeader();
mResetAppsHelper.onRestoreInstanceState(savedInstanceState);
return mRootView;
}
@VisibleForTesting
void createHeader() {
final Activity activity = getActivity();
final FrameLayout pinnedHeader = mRootView.findViewById(R.id.pinned_header);
mSpinnerHeader = activity.getLayoutInflater()
.inflate(R.layout.apps_filter_spinner, pinnedHeader, false);
mFilterSpinner = mSpinnerHeader.findViewById(R.id.filter_spinner);
mFilterAdapter = new FilterSpinnerAdapter(this);
mFilterSpinner.setAdapter(mFilterAdapter);
mFilterSpinner.setOnItemSelectedListener(this);
pinnedHeader.addView(mSpinnerHeader, 0);
final AppFilterRegistry appFilterRegistry = AppFilterRegistry.getInstance();
mFilterAdapter.enableFilter(appFilterRegistry.getDefaultFilterType(mListType));
if (mListType == LIST_TYPE_MAIN) {
if (UserManager.get(getActivity()).getUserProfiles().size() > 1) {
mFilterAdapter.enableFilter(FILTER_APPS_PERSONAL);
mFilterAdapter.enableFilter(FILTER_APPS_WORK);
}
}
if (mListType == LIST_TYPE_NOTIFICATION) {
mFilterAdapter.enableFilter(FILTER_APPS_RECENT);
mFilterAdapter.enableFilter(FILTER_APPS_FREQUENT);
mFilterAdapter.disableFilter(FILTER_APPS_ALL);
}
if (mListType == LIST_TYPE_HIGH_POWER) {
mFilterAdapter.enableFilter(FILTER_APPS_POWER_WHITELIST_ALL);
}
AppFilter compositeFilter = getCompositeFilter(mListType, mStorageType, mVolumeUuid);
if (mIsWorkOnly) {
final AppFilter workFilter = appFilterRegistry.get(FILTER_APPS_WORK).getFilter();
compositeFilter = new CompoundFilter(compositeFilter, workFilter);
}
if (compositeFilter != null) {
mApplications.setCompositeFilter(compositeFilter);
}
}
@VisibleForTesting
@Nullable
static AppFilter getCompositeFilter(int listType, int storageType, String volumeUuid) {
AppFilter filter = new VolumeFilter(volumeUuid);
if (listType == LIST_TYPE_STORAGE) {
if (storageType == STORAGE_TYPE_MUSIC) {
filter = new CompoundFilter(ApplicationsState.FILTER_AUDIO, filter);
} else if (storageType == STORAGE_TYPE_DEFAULT) {
filter = new CompoundFilter(ApplicationsState.FILTER_OTHER_APPS, filter);
}
return filter;
}
if (listType == LIST_TYPE_GAMES) {
return new CompoundFilter(ApplicationsState.FILTER_GAMES, filter);
} else if (listType == LIST_TYPE_MOVIES) {
return new CompoundFilter(ApplicationsState.FILTER_MOVIES, filter);
} else if (listType == LIST_TYPE_PHOTOGRAPHY) {
return new CompoundFilter(ApplicationsState.FILTER_PHOTOS, filter);
}
return null;
}
@Override
public int getMetricsCategory() {
switch (mListType) {
case LIST_TYPE_MAIN:
return MetricsEvent.MANAGE_APPLICATIONS;
case LIST_TYPE_NOTIFICATION:
return MetricsEvent.MANAGE_APPLICATIONS_NOTIFICATIONS;
case LIST_TYPE_STORAGE:
if (mStorageType == STORAGE_TYPE_MUSIC) {
return MetricsEvent.APPLICATIONS_STORAGE_MUSIC;
}
return MetricsEvent.APPLICATIONS_STORAGE_APPS;
case LIST_TYPE_GAMES:
return MetricsEvent.APPLICATIONS_STORAGE_GAMES;
case LIST_TYPE_MOVIES:
return MetricsEvent.APPLICATIONS_STORAGE_MOVIES;
case LIST_TYPE_PHOTOGRAPHY:
return MetricsEvent.APPLICATIONS_STORAGE_PHOTOS;
case LIST_TYPE_USAGE_ACCESS:
return MetricsEvent.USAGE_ACCESS;
case LIST_TYPE_HIGH_POWER:
return MetricsEvent.APPLICATIONS_HIGH_POWER_APPS;
case LIST_TYPE_OVERLAY:
return MetricsEvent.SYSTEM_ALERT_WINDOW_APPS;
case LIST_TYPE_WRITE_SETTINGS:
return MetricsEvent.SYSTEM_ALERT_WINDOW_APPS;
case LIST_TYPE_MANAGE_SOURCES:
return MetricsEvent.MANAGE_EXTERNAL_SOURCES;
case LIST_TYPE_DIRECTORY_ACCESS:
return MetricsEvent.DIRECTORY_ACCESS;
case LIST_TYPE_WIFI_ACCESS:
return MetricsEvent.CONFIGURE_WIFI;
default:
return MetricsEvent.VIEW_UNKNOWN;
}
}
@Override
public void onStart() {
super.onStart();
updateView();
if (mApplications != null) {
mApplications.resume(mSortOrder);
mApplications.updateLoading();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mResetAppsHelper.onSaveInstanceState(outState);
outState.putInt(EXTRA_SORT_ORDER, mSortOrder);
outState.putBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
outState.putBoolean(EXTRA_HAS_ENTRIES, mApplications.mHasReceivedLoadEntries);
outState.putBoolean(EXTRA_HAS_BRIDGE, mApplications.mHasReceivedBridgeCallback);
if (mApplications != null) {
mApplications.onSaveInstanceState(outState);
}
}
@Override
public void onStop() {
super.onStop();
if (mApplications != null) {
mApplications.pause();
}
mResetAppsHelper.stop();
}
@Override
public void onDestroyView() {
super.onDestroyView();
if (mApplications != null) {
mApplications.release();
}
mRootView = null;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) {
if (mListType == LIST_TYPE_NOTIFICATION) {
mApplications.mExtraInfoBridge.forceUpdate(mCurrentPkgName, mCurrentUid);
} else if (mListType == LIST_TYPE_HIGH_POWER || mListType == LIST_TYPE_OVERLAY
|| mListType == LIST_TYPE_WRITE_SETTINGS) {
mApplications.mExtraInfoBridge.forceUpdate(mCurrentPkgName, mCurrentUid);
} else {
mApplicationsState.requestSize(mCurrentPkgName, UserHandle.getUserId(mCurrentUid));
}
}
}
// utility method used to start sub activity
private void startApplicationDetailsActivity() {
switch (mListType) {
case LIST_TYPE_NOTIFICATION:
startAppInfoFragment(AppNotificationSettings.class, R.string.notifications_title);
break;
case LIST_TYPE_USAGE_ACCESS:
startAppInfoFragment(UsageAccessDetails.class, R.string.usage_access);
break;
case LIST_TYPE_STORAGE:
startAppInfoFragment(AppStorageSettings.class, R.string.storage_settings);
break;
case LIST_TYPE_HIGH_POWER:
HighPowerDetail.show(this, mCurrentUid, mCurrentPkgName, INSTALLED_APP_DETAILS);
break;
case LIST_TYPE_OVERLAY:
startAppInfoFragment(DrawOverlayDetails.class, R.string.overlay_settings);
break;
case LIST_TYPE_WRITE_SETTINGS:
startAppInfoFragment(WriteSettingsDetails.class, R.string.write_system_settings);
break;
case LIST_TYPE_MANAGE_SOURCES:
startAppInfoFragment(ExternalSourcesDetails.class, R.string.install_other_apps);
break;
case LIST_TYPE_GAMES:
startAppInfoFragment(AppStorageSettings.class, R.string.game_storage_settings);
break;
case LIST_TYPE_MOVIES:
startAppInfoFragment(AppStorageSettings.class, R.string.storage_movies_tv);
break;
case LIST_TYPE_PHOTOGRAPHY:
startAppInfoFragment(AppStorageSettings.class, R.string.storage_photos_videos);
break;
case LIST_TYPE_DIRECTORY_ACCESS:
startAppInfoFragment(DirectoryAccessDetails.class, R.string.directory_access);
break;
case LIST_TYPE_WIFI_ACCESS:
startAppInfoFragment(ChangeWifiStateDetails.class,
R.string.change_wifi_state_title);
break;
// TODO: Figure out if there is a way where we can spin up the profile's settings
// process ahead of time, to avoid a long load of data when user clicks on a managed
// app. Maybe when they load the list of apps that contains managed profile apps.
default:
startAppInfoFragment(
AppInfoDashboardFragment.class, R.string.application_info_label);
break;
}
}
private void startAppInfoFragment(Class<?> fragment, int titleRes) {
AppInfoBase.startAppInfoFragment(fragment, titleRes, mCurrentPkgName, mCurrentUid, this,
INSTALLED_APP_DETAILS, getMetricsCategory());
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
final Activity activity = getActivity();
if (activity == null) {
return;
}
HelpUtils.prepareHelpMenuItem(activity, menu, getHelpResource(), getClass().getName());
mOptionsMenu = menu;
inflater.inflate(R.menu.manage_apps, menu);
updateOptionsMenu();
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
updateOptionsMenu();
}
@Override
public void onDestroyOptionsMenu() {
mOptionsMenu = null;
}
@StringRes
int getHelpResource() {
if (mListType == LIST_TYPE_MAIN) {
return R.string.help_uri_apps;
} else if (mListType == LIST_TYPE_USAGE_ACCESS) {
return R.string.help_url_usage_access;
} else {
return R.string.help_uri_notifications;
}
}
void updateOptionsMenu() {
if (mOptionsMenu == null) {
return;
}
mOptionsMenu.findItem(R.id.advanced).setVisible(false);
mOptionsMenu.findItem(R.id.sort_order_alpha).setVisible(mListType == LIST_TYPE_STORAGE
&& mSortOrder != R.id.sort_order_alpha);
mOptionsMenu.findItem(R.id.sort_order_size).setVisible(mListType == LIST_TYPE_STORAGE
&& mSortOrder != R.id.sort_order_size);
mOptionsMenu.findItem(R.id.show_system).setVisible(!mShowSystem
&& mListType != LIST_TYPE_HIGH_POWER);
mOptionsMenu.findItem(R.id.hide_system).setVisible(mShowSystem
&& mListType != LIST_TYPE_HIGH_POWER);
mOptionsMenu.findItem(R.id.reset_app_preferences).setVisible(mListType == LIST_TYPE_MAIN);
// Hide notification menu items, because sorting happens when filtering
mOptionsMenu.findItem(R.id.sort_order_recent_notification).setVisible(false);
mOptionsMenu.findItem(R.id.sort_order_frequent_notification).setVisible(false);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int menuId = item.getItemId();
switch (item.getItemId()) {
case R.id.sort_order_alpha:
case R.id.sort_order_size:
if (mApplications != null) {
mApplications.rebuild(menuId);
}
break;
case R.id.show_system:
case R.id.hide_system:
mShowSystem = !mShowSystem;
mApplications.rebuild();
break;
case R.id.reset_app_preferences:
mResetAppsHelper.buildResetDialog();
return true;
case R.id.advanced:
if (mListType == LIST_TYPE_NOTIFICATION) {
new SubSettingLauncher(getContext())
.setDestination(ConfigureNotificationSettings.class.getName())
.setTitle(R.string.configure_notification_settings)
.setSourceMetricsCategory(getMetricsCategory())
.setResultListener(this, ADVANCED_SETTINGS)
.launch();
} else {
new SubSettingLauncher(getContext())
.setDestination(DefaultAppSettings.class.getName())
.setTitle(R.string.configure_apps)
.setSourceMetricsCategory(getMetricsCategory())
.setResultListener(this, ADVANCED_SETTINGS)
.launch();
}
return true;
default:
// Handle the home button
return false;
}
updateOptionsMenu();
return true;
}
@Override
public void onClick(View view) {
if (mApplications == null) {
return;
}
final int position = mRecyclerView.getChildAdapterPosition(view);
if (position == RecyclerView.NO_POSITION) {
Log.w(TAG, "Cannot find position for child, skipping onClick handling");
return;
}
if (mApplications.getApplicationCount() > position) {
ApplicationsState.AppEntry entry = mApplications.getAppEntry(position);
mCurrentPkgName = entry.info.packageName;
mCurrentUid = entry.info.uid;
startApplicationDetailsActivity();
} else {
mApplications.mExtraViewController.onClick(this);
}
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
mFilter = mFilterAdapter.getFilter(position);
mApplications.setFilter(mFilter);
if (DEBUG) Log.d(TAG, "Selecting filter " + mFilter);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
public void updateView() {
updateOptionsMenu();
final Activity host = getActivity();
if (host != null) {
host.invalidateOptionsMenu();
}
}
public void setHasDisabled(boolean hasDisabledApps) {
if (mListType != LIST_TYPE_MAIN) {
return;
}
mFilterAdapter.setFilterEnabled(FILTER_APPS_ENABLED, hasDisabledApps);
mFilterAdapter.setFilterEnabled(FILTER_APPS_DISABLED, hasDisabledApps);
}
public void setHasInstant(boolean haveInstantApps) {
if (LIST_TYPES_WITH_INSTANT.contains(mListType)) {
mFilterAdapter.setFilterEnabled(FILTER_APPS_INSTANT, haveInstantApps);
}
}
static class FilterSpinnerAdapter extends ArrayAdapter<CharSequence> {
private final ManageApplications mManageApplications;
private final Context mContext;
// Use ArrayAdapter for view logic, but have our own list for managing
// the options available.
private final ArrayList<AppFilterItem> mFilterOptions = new ArrayList<>();
public FilterSpinnerAdapter(ManageApplications manageApplications) {
super(manageApplications.getContext(), R.layout.filter_spinner_item);
mContext = manageApplications.getContext();
mManageApplications = manageApplications;
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
}
public AppFilterItem getFilter(int position) {
return mFilterOptions.get(position);
}
public void setFilterEnabled(@AppFilterRegistry.FilterType int filter, boolean enabled) {
if (enabled) {
enableFilter(filter);
} else {
disableFilter(filter);
}
}
public void enableFilter(@AppFilterRegistry.FilterType int filterType) {
final AppFilterItem filter = AppFilterRegistry.getInstance().get(filterType);
if (mFilterOptions.contains(filter)) {
return;
}
if (DEBUG) {
Log.d(TAG, "Enabling filter " + filter);
}
mFilterOptions.add(filter);
Collections.sort(mFilterOptions);
mManageApplications.mSpinnerHeader.setVisibility(
mFilterOptions.size() > 1 ? View.VISIBLE : View.GONE);
notifyDataSetChanged();
if (mFilterOptions.size() == 1) {
if (DEBUG) {
Log.d(TAG, "Auto selecting filter " + filter);
}
mManageApplications.mFilterSpinner.setSelection(0);
mManageApplications.onItemSelected(null, null, 0, 0);
}
}
public void disableFilter(@AppFilterRegistry.FilterType int filterType) {
final AppFilterItem filter = AppFilterRegistry.getInstance().get(filterType);
if (!mFilterOptions.remove(filter)) {
return;
}
if (DEBUG) {
Log.d(TAG, "Disabling filter " + filter);
}
Collections.sort(mFilterOptions);
mManageApplications.mSpinnerHeader.setVisibility(
mFilterOptions.size() > 1 ? View.VISIBLE : View.GONE);
notifyDataSetChanged();
if (mManageApplications.mFilter == filter) {
if (mFilterOptions.size() > 0) {
if (DEBUG) {
Log.d(TAG, "Auto selecting filter " + mFilterOptions.get(0));
}
mManageApplications.mFilterSpinner.setSelection(0);
mManageApplications.onItemSelected(null, null, 0, 0);
}
}
}
@Override
public int getCount() {
return mFilterOptions.size();
}
@Override
public CharSequence getItem(int position) {
return mContext.getText(mFilterOptions.get(position).getTitle());
}
}
static class ApplicationsAdapter extends RecyclerView.Adapter<ApplicationViewHolder>
implements ApplicationsState.Callbacks, AppStateBaseBridge.Callback {
private static final String STATE_LAST_SCROLL_INDEX = "state_last_scroll_index";
private static final int VIEW_TYPE_APP = 0;
private static final int VIEW_TYPE_EXTRA_VIEW = 1;
private final ApplicationsState mState;
private final ApplicationsState.Session mSession;
private final ManageApplications mManageApplications;
private final Context mContext;
private final AppStateBaseBridge mExtraInfoBridge;
private final LoadingViewController mLoadingViewController;
private AppFilterItem mAppFilter;
private ArrayList<ApplicationsState.AppEntry> mEntries;
private boolean mResumed;
private int mLastSortMode = -1;
private int mWhichSize = SIZE_TOTAL;
private AppFilter mCompositeFilter;
private boolean mHasReceivedLoadEntries;
private boolean mHasReceivedBridgeCallback;
private FileViewHolderController mExtraViewController;
// This is to remember and restore the last scroll position when this
// fragment is paused. We need this special handling because app entries are added gradually
// when we rebuild the list after the user made some changes, like uninstalling an app.
private int mLastIndex = -1;
@VisibleForTesting
OnScrollListener mOnScrollListener;
private RecyclerView mRecyclerView;
public ApplicationsAdapter(ApplicationsState state, ManageApplications manageApplications,
AppFilterItem appFilter, Bundle savedInstanceState) {
setHasStableIds(true);
mState = state;
mSession = state.newSession(this);
mManageApplications = manageApplications;
mLoadingViewController = new LoadingViewController(
mManageApplications.mLoadingContainer,
mManageApplications.mListContainer
);
mContext = manageApplications.getActivity();
mAppFilter = appFilter;
if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) {
mExtraInfoBridge = new AppStateNotificationBridge(mContext, mState, this,
manageApplications.mUsageStatsManager,
manageApplications.mUserManager,
manageApplications.mNotificationBackend);
} else if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) {
mExtraInfoBridge = new AppStateUsageBridge(mContext, mState, this);
} else if (mManageApplications.mListType == LIST_TYPE_HIGH_POWER) {
mExtraInfoBridge = new AppStatePowerBridge(mContext, mState, this);
} else if (mManageApplications.mListType == LIST_TYPE_OVERLAY) {
mExtraInfoBridge = new AppStateOverlayBridge(mContext, mState, this);
} else if (mManageApplications.mListType == LIST_TYPE_WRITE_SETTINGS) {
mExtraInfoBridge = new AppStateWriteSettingsBridge(mContext, mState, this);
} else if (mManageApplications.mListType == LIST_TYPE_MANAGE_SOURCES) {
mExtraInfoBridge = new AppStateInstallAppsBridge(mContext, mState, this);
} else if (mManageApplications.mListType == LIST_TYPE_DIRECTORY_ACCESS) {
mExtraInfoBridge = new AppStateDirectoryAccessBridge(mState, this);
} else if (mManageApplications.mListType == LIST_TYPE_WIFI_ACCESS) {
mExtraInfoBridge = new AppStateChangeWifiStateBridge(mContext, mState, this);
} else {
mExtraInfoBridge = null;
}
if (savedInstanceState != null) {
mLastIndex = savedInstanceState.getInt(STATE_LAST_SCROLL_INDEX);
}
}
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
mRecyclerView = recyclerView;
mOnScrollListener = new OnScrollListener(this);
mRecyclerView.addOnScrollListener(mOnScrollListener);
}
@Override
public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
super.onDetachedFromRecyclerView(recyclerView);
mRecyclerView.removeOnScrollListener(mOnScrollListener);
mOnScrollListener = null;
mRecyclerView = null;
}
public void setCompositeFilter(AppFilter compositeFilter) {
mCompositeFilter = compositeFilter;
rebuild();
}
public void setFilter(AppFilterItem appFilter) {
mAppFilter = appFilter;
// Notification filters require resorting the list
if (FILTER_APPS_FREQUENT == appFilter.getFilterType()) {
rebuild(R.id.sort_order_frequent_notification);
} else if (FILTER_APPS_RECENT == appFilter.getFilterType()) {
rebuild(R.id.sort_order_recent_notification);
} else {
rebuild();
}
}
public void setExtraViewController(FileViewHolderController extraViewController) {
mExtraViewController = extraViewController;
// Start to query extra view's stats on background, and once done post result to main
// thread.
ThreadUtils.postOnBackgroundThread(() -> {
mExtraViewController.queryStats();
ThreadUtils.postOnMainThread(() -> {
onExtraViewCompleted();
});
});
}
public void resume(int sort) {
if (DEBUG) Log.i(TAG, "Resume! mResumed=" + mResumed);
if (!mResumed) {
mResumed = true;
mSession.onResume();
mLastSortMode = sort;
if (mExtraInfoBridge != null) {
mExtraInfoBridge.resume();
}
rebuild();
} else {
rebuild(sort);
}
}
public void pause() {
if (mResumed) {
mResumed = false;
mSession.onPause();
if (mExtraInfoBridge != null) {
mExtraInfoBridge.pause();
}
}
}
public void onSaveInstanceState(Bundle outState) {
// Record the current scroll position before pausing.
final LinearLayoutManager layoutManager =
(LinearLayoutManager) mManageApplications.mRecyclerView.getLayoutManager();
outState.putInt(STATE_LAST_SCROLL_INDEX, layoutManager.findFirstVisibleItemPosition());
}
public void release() {
mSession.onDestroy();
if (mExtraInfoBridge != null) {
mExtraInfoBridge.release();
}
}
public void rebuild(int sort) {
if (sort == mLastSortMode) {
return;
}
mManageApplications.mSortOrder = sort;
mLastSortMode = sort;
rebuild();
}
@Override
public ApplicationViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) {
view = ApplicationViewHolder.newView(parent, true /* twoTarget */);
} else {
view = ApplicationViewHolder.newView(parent, false /* twoTarget */);
}
return new ApplicationViewHolder(view,
shouldUseStableItemHeight(mManageApplications.mListType));
}
@Override
public int getItemViewType(int position) {
boolean isLastItem = (getItemCount() - 1) == position;
return hasExtraView() && isLastItem
? VIEW_TYPE_EXTRA_VIEW
: VIEW_TYPE_APP;
}
public void rebuild() {
if (!mHasReceivedLoadEntries
|| (mExtraInfoBridge != null && !mHasReceivedBridgeCallback)) {
// Don't rebuild the list until all the app entries are loaded.
return;
}
ApplicationsState.AppFilter filterObj;
Comparator<AppEntry> comparatorObj;
boolean emulated = Environment.isExternalStorageEmulated();
if (emulated) {
mWhichSize = SIZE_TOTAL;
} else {
mWhichSize = SIZE_INTERNAL;
}
filterObj = mAppFilter.getFilter();
if (mCompositeFilter != null) {
filterObj = new CompoundFilter(filterObj, mCompositeFilter);
}
if (!mManageApplications.mShowSystem) {
if (LIST_TYPES_WITH_INSTANT.contains(mManageApplications.mListType)) {
filterObj = new CompoundFilter(filterObj,
ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT);
} else {
filterObj = new CompoundFilter(filterObj,
ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER);
}
}
switch (mLastSortMode) {
case R.id.sort_order_size:
switch (mWhichSize) {
case SIZE_INTERNAL:
comparatorObj = ApplicationsState.INTERNAL_SIZE_COMPARATOR;
break;
case SIZE_EXTERNAL:
comparatorObj = ApplicationsState.EXTERNAL_SIZE_COMPARATOR;
break;
default:
comparatorObj = ApplicationsState.SIZE_COMPARATOR;
break;
}
break;
case R.id.sort_order_recent_notification:
comparatorObj = AppStateNotificationBridge.RECENT_NOTIFICATION_COMPARATOR;
break;
case R.id.sort_order_frequent_notification:
comparatorObj = AppStateNotificationBridge.FREQUENCY_NOTIFICATION_COMPARATOR;
break;
default:
comparatorObj = ApplicationsState.ALPHA_COMPARATOR;
break;
}
filterObj = new CompoundFilter(filterObj, ApplicationsState.FILTER_NOT_HIDE);
AppFilter finalFilterObj = filterObj;
ThreadUtils.postOnBackgroundThread(() -> {
final ArrayList<AppEntry> entries = mSession.rebuild(finalFilterObj,
comparatorObj, false);
if (entries != null) {
ThreadUtils.postOnMainThread(() -> onRebuildComplete(entries));
}
});
}
@VisibleForTesting
static boolean shouldUseStableItemHeight(int listType) {
switch (listType) {
case LIST_TYPE_NOTIFICATION:
// Most entries in notification type has no summary. Don't use stable height
// so height is short for most entries.
return false;
default:
// Other types have non-empty summary, so keep the height as we expect summary
// to fill in.
return true;
}
}
private static boolean packageNameEquals(PackageItemInfo info1, PackageItemInfo info2) {
if (info1 == null || info2 == null) {
return false;
}
if (info1.packageName == null || info2.packageName == null) {
return false;
}
return info1.packageName.equals(info2.packageName);
}
private ArrayList<ApplicationsState.AppEntry> removeDuplicateIgnoringUser(
ArrayList<ApplicationsState.AppEntry> entries) {
int size = entries.size();
// returnList will not have more entries than entries
ArrayList<ApplicationsState.AppEntry> returnEntries = new ArrayList<>(size);
// assume appinfo of same package but different users are grouped together
PackageItemInfo lastInfo = null;
for (int i = 0; i < size; i++) {
AppEntry appEntry = entries.get(i);
PackageItemInfo info = appEntry.info;
if (!packageNameEquals(lastInfo, appEntry.info)) {
returnEntries.add(appEntry);
}
lastInfo = info;
}
returnEntries.trimToSize();
return returnEntries;
}
@Override
public void onRebuildComplete(ArrayList<AppEntry> entries) {
final int filterType = mAppFilter.getFilterType();
if (filterType == FILTER_APPS_POWER_WHITELIST ||
filterType == FILTER_APPS_POWER_WHITELIST_ALL) {
entries = removeDuplicateIgnoringUser(entries);
}
mEntries = entries;
notifyDataSetChanged();
if (getItemCount() == 0) {
mManageApplications.mRecyclerView.setVisibility(View.GONE);
mManageApplications.mEmptyView.setVisibility(View.VISIBLE);
} else {
mManageApplications.mEmptyView.setVisibility(View.GONE);
mManageApplications.mRecyclerView.setVisibility(View.VISIBLE);
}
// Restore the last scroll position if the number of entries added so far is bigger than
// it.
if (mLastIndex != -1 && getItemCount() > mLastIndex) {
mManageApplications.mRecyclerView.getLayoutManager().scrollToPosition(mLastIndex);
mLastIndex = -1;
}
if (mSession.getAllApps().size() != 0
&& mManageApplications.mListContainer.getVisibility() != View.VISIBLE) {
mLoadingViewController.showContent(true /* animate */);
}
if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) {
// No enabled or disabled filters for usage access.
return;
}
mManageApplications.setHasDisabled(mState.haveDisabledApps());
mManageApplications.setHasInstant(mState.haveInstantApps());
}
@VisibleForTesting
void updateLoading() {
final boolean appLoaded = mHasReceivedLoadEntries && mSession.getAllApps().size() != 0;
if (appLoaded) {
mLoadingViewController.showContent(false /* animate */);
} else {
mLoadingViewController.showLoadingViewDelayed();
}
}
@Override
public void onExtraInfoUpdated() {
mHasReceivedBridgeCallback = true;
rebuild();
}
@Override
public void onRunningStateChanged(boolean running) {
mManageApplications.getActivity().setProgressBarIndeterminateVisibility(running);
}
@Override
public void onPackageListChanged() {
rebuild();
}
@Override
public void onPackageIconChanged() {
// We ensure icons are loaded when their item is displayed, so
// don't care about icons loaded in the background.
}
@Override
public void onLoadEntriesCompleted() {
mHasReceivedLoadEntries = true;
// We may have been skipping rebuilds until this came in, trigger one now.
rebuild();
}
@Override
public void onPackageSizeChanged(String packageName) {
if (mEntries == null) {
return;
}
final int size = mEntries.size();
for (int i = 0; i < size; i++) {
final AppEntry entry = mEntries.get(i);
final ApplicationInfo info = entry.info;
if (info == null && !TextUtils.equals(packageName, info.packageName)) {
continue;
}
if (TextUtils.equals(mManageApplications.mCurrentPkgName, info.packageName)) {
// We got the size information for the last app the
// user viewed, and are sorting by size... they may
// have cleared data, so we immediately want to resort
// the list with the new size to reflect it to the user.
rebuild();
return;
} else {
mOnScrollListener.postNotifyItemChange(i);
}
}
}
@Override
public void onLauncherInfoChanged() {
if (!mManageApplications.mShowSystem) {
rebuild();
}
}
@Override
public void onAllSizesComputed() {
if (mLastSortMode == R.id.sort_order_size) {
rebuild();
}
}
public void onExtraViewCompleted() {
if (!hasExtraView()) {
return;
}
// Update last item - this is assumed to be the extra view.
notifyItemChanged(getItemCount() - 1);
}
@Override
public int getItemCount() {
if (mEntries == null) {
return 0;
}
return mEntries.size() + (hasExtraView() ? 1 : 0);
}
public int getApplicationCount() {
return mEntries != null ? mEntries.size() : 0;
}
public AppEntry getAppEntry(int position) {
return mEntries.get(position);
}
@Override
public long getItemId(int position) {
if (position == mEntries.size()) {
return -1;
}
return mEntries.get(position).id;
}
public boolean isEnabled(int position) {
if (getItemViewType(position) == VIEW_TYPE_EXTRA_VIEW
|| mManageApplications.mListType != LIST_TYPE_HIGH_POWER) {
return true;
}
ApplicationsState.AppEntry entry = mEntries.get(position);
return !PowerWhitelistBackend.getInstance(mContext)
.isSysWhitelisted(entry.info.packageName);
}
@Override
public void onBindViewHolder(ApplicationViewHolder holder, int position) {
if (mEntries != null && mExtraViewController != null && position == mEntries.size()) {
// set up view for extra view controller
mExtraViewController.setupView(holder);
} else {
// Bind the data efficiently with the holder
ApplicationsState.AppEntry entry = mEntries.get(position);
synchronized (entry) {
holder.setTitle(entry.label);
mState.ensureIcon(entry);
holder.setIcon(entry.icon);
updateSummary(holder, entry);
updateSwitch(holder, entry);
holder.updateDisableView(entry.info);
}
holder.setEnabled(isEnabled(position));
}
holder.itemView.setOnClickListener(mManageApplications);
}
private void updateSummary(ApplicationViewHolder holder, AppEntry entry) {
switch (mManageApplications.mListType) {
case LIST_TYPE_NOTIFICATION:
if (entry.extraInfo != null) {
holder.setSummary(AppStateNotificationBridge.getSummary(mContext,
(NotificationsSentState) entry.extraInfo,
(mLastSortMode == R.id.sort_order_recent_notification)));
} else {
holder.setSummary(null);
}
break;
case LIST_TYPE_USAGE_ACCESS:
if (entry.extraInfo != null) {
holder.setSummary(
(new UsageState((PermissionState) entry.extraInfo)).isPermissible()
? R.string.app_permission_summary_allowed
: R.string.app_permission_summary_not_allowed);
} else {
holder.setSummary(null);
}
break;
case LIST_TYPE_HIGH_POWER:
holder.setSummary(HighPowerDetail.getSummary(mContext, entry));
break;
case LIST_TYPE_OVERLAY:
holder.setSummary(DrawOverlayDetails.getSummary(mContext, entry));
break;
case LIST_TYPE_WRITE_SETTINGS:
holder.setSummary(WriteSettingsDetails.getSummary(mContext, entry));
break;
case LIST_TYPE_MANAGE_SOURCES:
holder.setSummary(ExternalSourcesDetails.getPreferenceSummary(mContext, entry));
break;
case LIST_TYPE_DIRECTORY_ACCESS:
holder.setSummary(null);
break;
case LIST_TYPE_WIFI_ACCESS:
holder.setSummary(ChangeWifiStateDetails.getSummary(mContext, entry));
break;
default:
holder.updateSizeText(entry, mManageApplications.mInvalidSizeStr, mWhichSize);
break;
}
}
private void updateSwitch(ApplicationViewHolder holder, AppEntry entry) {
switch (mManageApplications.mListType) {
case LIST_TYPE_NOTIFICATION:
holder.updateSwitch(((AppStateNotificationBridge) mExtraInfoBridge)
.getSwitchOnClickListener(entry),
AppStateNotificationBridge.enableSwitch(entry),
AppStateNotificationBridge.checkSwitch(entry));
if (entry.extraInfo != null) {
holder.setSummary(AppStateNotificationBridge.getSummary(mContext,
(NotificationsSentState) entry.extraInfo,
(mLastSortMode == R.id.sort_order_recent_notification)));
} else {
holder.setSummary(null);
}
break;
}
}
private boolean hasExtraView() {
return mExtraViewController != null
&& mExtraViewController.shouldShow();
}
public static class OnScrollListener extends RecyclerView.OnScrollListener {
private int mScrollState = SCROLL_STATE_IDLE;
private boolean mDelayNotifyDataChange;
private ApplicationsAdapter mAdapter;
public OnScrollListener(ApplicationsAdapter adapter) {
mAdapter = adapter;
}
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
mScrollState = newState;
if (mScrollState == SCROLL_STATE_IDLE && mDelayNotifyDataChange) {
mDelayNotifyDataChange = false;
mAdapter.notifyDataSetChanged();
}
}
public void postNotifyItemChange(int index) {
if (mScrollState == SCROLL_STATE_IDLE) {
mAdapter.notifyItemChanged(index);
} else {
mDelayNotifyDataChange = true;
}
}
}
}
private static class SummaryProvider implements SummaryLoader.SummaryProvider {
private final Context mContext;
private final SummaryLoader mLoader;
private SummaryProvider(Context context, SummaryLoader loader) {
mContext = context;
mLoader = loader;
}
@Override
public void setListening(boolean listening) {
if (listening) {
new InstalledAppCounter(mContext, InstalledAppCounter.IGNORE_INSTALL_REASON,
new PackageManagerWrapper(mContext.getPackageManager())) {
@Override
protected void onCountComplete(int num) {
mLoader.setSummary(SummaryProvider.this,
mContext.getString(R.string.apps_summary, num));
}
}.execute();
}
}
}
public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
= new SummaryLoader.SummaryProviderFactory() {
@Override
public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
SummaryLoader summaryLoader) {
return new SummaryProvider(activity, summaryLoader);
}
};
}