AppDashboardFragment.java,通过getPreferenceScreenResId()进入应用界面的布局文件app.xml
/*
* Copyright (C) 2021 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;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.provider.SearchIndexableResource;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.search.SearchIndexable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/** Settings page for apps. */
@SearchIndexable
public class AppDashboardFragment extends DashboardFragment {
private static final String TAG = "AppDashboardFragment";
private AppsPreferenceController mAppsPreferenceController;
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new AppsPreferenceController(context));
return controllers;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.MANAGE_APPLICATIONS;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
public int getHelpResource() {
return R.string.help_url_apps_and_notifications;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.apps;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
use(SpecialAppAccessPreferenceController.class).setSession(getSettingsLifecycle());
mAppsPreferenceController = use(AppsPreferenceController.class);
mAppsPreferenceController.setFragment(this /* fragment */);
getSettingsLifecycle().addObserver(mAppsPreferenceController);
final HibernatedAppsPreferenceController hibernatedAppsPreferenceController =
use(HibernatedAppsPreferenceController.class);
getSettingsLifecycle().addObserver(hibernatedAppsPreferenceController);
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
return buildPreferenceControllers(context);
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
@Override
public List<SearchIndexableResource> getXmlResourcesToIndex(
Context context, boolean enabled) {
final SearchIndexableResource sir = new SearchIndexableResource(context);
sir.xmlResId = R.xml.apps;
return Arrays.asList(sir);
}
@Override
public List<AbstractPreferenceController> createPreferenceControllers(
Context context) {
return buildPreferenceControllers(context);
}
};
}
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="apps_screen"
android:title="@string/apps_dashboard_title">
<Preference
android:key="all_app_infos"
android:title="@string/all_apps"
android:summary="@string/summary_placeholder"
android:order="-999"
android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
settings:keywords="@string/keywords_applications_settings"/>
<PreferenceCategory
android:key="recent_apps_category"
android:title="@string/recent_app_category_title"
android:order="-998"
settings:searchable="false">
<Preference
android:key="see_all_apps"
android:title="@string/default_see_all_apps_title"
android:icon="@drawable/ic_chevron_right_24dp"
android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
android:order="5"
settings:searchable="false">
Preference>
PreferenceCategory>
<PreferenceCategory
android:key="general_category"
android:title="@string/category_name_general"
android:order="-997"
android:visibility="gone"
settings:searchable="false"/>
<Preference
android:key="default_apps"
android:title="@string/app_default_dashboard_title"
android:order="-996"
settings:controller="com.android.settings.applications.DefaultAppsPreferenceController">
<intent android:action="android.settings.MANAGE_DEFAULT_APPS_SETTINGS"/>
Preference>
<Preference
android:key="game_settings"
android:title="@string/game_settings_title"
android:summary="@string/game_settings_summary"
android:order="-995"
settings:controller="com.android.settings.applications.GameSettingsPreferenceController">
Preference>
<PreferenceCategory
android:key="dashboard_tile_placeholder"
android:order="10"/>
<Preference
android:key="hibernated_apps"
android:title="@string/unused_apps"
android:summary="@string/summary_placeholder"
android:order="15"
settings:keywords="app_hibernation_key"
settings:controller="com.android.settings.applications.HibernatedAppsPreferenceController">
<intent android:action="android.intent.action.MANAGE_UNUSED_APPS"/>
Preference>
<Preference
android:key="special_access"
android:fragment="com.android.settings.applications.specialaccess.SpecialAccessSettings"
android:title="@string/special_access"
android:order="20"
settings:controller="com.android.settings.applications.SpecialAppAccessPreferenceController"/>
<Preference
android:key="parallel_app"
android:fragment="com.android.settings.applications.parallelapp.ParallelAppSettings"
android:title="@string/parallel_app"
android:order="25"
settings:controller="com.android.settings.applications.parallelapp.ParallelAppPreferenceController"/>
PreferenceScreen>


ParallelAppSettings.java
/*
* Copyright (C) 2016 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.parallelapp;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.widget.Switch;
import android.util.Log;
import androidx.preference.PreferenceGroup;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.SettingsActivity;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppFilter;
import java.util.List;
public class ParallelAppSettings extends DashboardFragment {
private static final String TAG = "ParallelAppSettings";
private static final String KEY = "app_list";
private AppFilter mFilter;
private int mCloneUserId = -1;
private IActivityManager mAms;
private UserManager mUm;
PreferenceGroup mAppList;
@Override
public void onAttach(Context context) {
super.onAttach(context);
mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
mFilter = ApplicationsState.FILTER_ALL_ENABLED;
use(ManageParallelAppsPreferenceController.class).setSession(getSettingsLifecycle());
use(ManageParallelAppsPreferenceController.class).setParentFragment(this);
use(ManageParallelAppsPreferenceController.class).setFilter(mFilter);
mAms = ActivityManager.getService();
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
// 获取布局文件中PreferenceCategory中key
mAppList = (PreferenceGroup) findPreference(KEY);
android.util.Log.d("xin.wang", " Content of mAppList : " + mAppList.getPreferenceCount());
// 视图反射进入ManageParallelAppsPreferenceController,设置setPreferenceGroup
use(ManageParallelAppsPreferenceController.class).setPreferenceGroup(mAppList);
}
@Override
public void onResume() {
super.onResume();
use(ManageParallelAppsPreferenceController.class).rebuild();
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.parallel_app_settings;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.PAGE_UNKNOWN;
}
}
user()方法,是父类DashboardFragment的方法
protected <T extends AbstractPreferenceController> T use(Class<T> clazz) {
List<AbstractPreferenceController> controllerList = mPreferenceControllers.get(clazz);
if (controllerList != null) {
if (controllerList.size() > 1) {
Log.w(TAG, "Multiple controllers of Class " + clazz.getSimpleName()
+ " found, returning first one.");
}
return (T) controllerList.get(0);
}
return null;
}
进入布局文件parallel_app_settings.xml
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="parallel_app_settings"
android:title="@string/parallel_app"
settings:controller="com.android.settings.applications.parallelapp.ManageParallelAppsPreferenceController"
settings:searchable="false">
<com.android.settingslib.widget.TopIntroPreference
android:key="support_app"
android:title="@string/parallel_app_support"/>
<PreferenceCategory
android:key="app_list"
android:order="10"
settings:searchable="false">
PreferenceCategory>
PreferenceScreen>

ManageParallelAppsPreferenceController/*
* Copyright (C) 2017 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.parallelapp;
import android.app.ActivityManager;
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.PreferenceGroup;
import com.android.settings.applications.AppStateBaseBridge;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.AppFilter;
import com.android.settingslib.core.lifecycle.Lifecycle;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
public class ManageParallelAppsPreferenceController extends BasePreferenceController implements
ApplicationsState.Callbacks, Preference.OnPreferenceChangeListener {
private static final String TAG = "ManageParallelAppsPreferenceController";
private final ApplicationsState mApplicationsState;
private final PackageInstaller mPackageInstaller;
private final PackageManager mPm;
private final UserManager mUm;
private ApplicationsState.Session mSession;
private AppFilter mFilter;
private Context mContext;
private ParallelAppSettings mParentFragment;
private HashSet<String> mSupportAppClone;
PreferenceGroup mAppList;
public ManageParallelAppsPreferenceController(Context context, String key) {
super(context, key);
mContext = context;
mApplicationsState = ApplicationsState.getInstance(
(Application) context.getApplicationContext());
mPm = mContext.getPackageManager();
mPackageInstaller = mPm.getPackageInstaller();
mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
//get app could cloning by -> com.tencent.mm=>微信,com.sina.weibo=>微博,com.tencent.mobilqq=>QQ,com.ss.android.ugc.aweme=>抖音
// 读取支持手机分身的应用包名
String[] supportApps = context.getResources().getStringArray(com.unisoc.internal.R.array.support_app_clone);
// 将数组转化为列表
mSupportAppClone = new HashSet<String>(Arrays.asList(supportApps));
}
@Override
public int getAvailabilityStatus() {
ActivityManager activityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
boolean isAdmin = mUm.isAdminUser();
boolean isLowRam = activityManager.isLowRamDevice();
boolean isSupportAppClone = mContext.getResources().getBoolean(R.bool.config_support_appclone);
return isAdmin && !isLowRam && isSupportAppClone ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
@Override
public void onRebuildComplete(ArrayList<AppEntry> apps) {
if (apps == null) {
return;
}
final Set<String> appsKeySet = new TreeSet<>();
final int N = apps.size();
android.util.Log.d("xin.wang", "apps.size -> " + N);
for (int i = 0; i < N; i++) {
final AppEntry entry = apps.get(i);
android.util.Log.d("xin.wang", "AppEntry entry --> " + entry.toString());
boolean isSupportClone = mSupportAppClone.contains(entry.info.packageName);
android.util.Log.d("xin.wang", "get Should Clone for clone --> " + mSupportAppClone);
android.util.Log.d("xin.wang", "get app packageName : -> " + entry.info.packageName + "<- isSupportClone ->" + isSupportClone );
int userId = UserHandle.getUserId(entry.info.uid);
boolean isAdminUser = mUm.isUserAdmin(userId);
if (mSupportAppClone.contains(entry.info.packageName) && isAdminUser) {
if (!shouldAddPreference(entry)) {
continue;
}
final String prefkey = ParallelAppPreference.generateKey(entry);
appsKeySet.add(prefkey);
android.util.Log.d("xin.wang", " get appsKeySet -> " + appsKeySet + "<- prefkey ->" + prefkey);
// 获取布局中key
ParallelAppPreference preference =
(ParallelAppPreference) mAppList.findPreference(prefkey);
// 当布局为空的时候,实例化一个布局
if (preference == null) {
preference = new ParallelAppPreference(mContext, entry,
mApplicationsState, mUm, mPm, mPackageInstaller);
preference.setOnPreferenceChangeListener(this);
mAppList.addPreference(preference);
} else {
preference.updateState();
}
preference.setOrder(i);
}
}
removeUselessPrefs(appsKeySet);
}
@Override
public void onRunningStateChanged(boolean running) {}
@Override
public void onPackageListChanged() {
rebuild();
}
@Override
public void onPackageIconChanged() {}
@Override
public void onPackageSizeChanged(String packageName) {}
@Override
public void onAllSizesComputed() {}
@Override
public void onLauncherInfoChanged() {}
@Override
public void onLoadEntriesCompleted() {
rebuild();
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (Utils.isMonkeyRunning()) {
return true;
}
if (preference instanceof ParallelAppPreference) {
ParallelAppPreference parAppPreference = (ParallelAppPreference)preference;
if ((Boolean)newValue) {
parAppPreference.openParApp();
} else {
parAppPreference.createCloseOptionsDialog(mParentFragment.getActivity());
}
}
return true;
}
public void setFilter(AppFilter filter) {
mFilter = filter;
}
public void setSession(Lifecycle lifecycle) {
mSession = mApplicationsState.newSession(this, lifecycle);
}
public void setParentFragment(ParallelAppSettings parentFragment) {
mParentFragment = parentFragment;
}
public void setPreferenceGroup(PreferenceGroup appList) {
mAppList = appList;
android.util.Log.d("xin.wang", "setPreferenceGroup: -> appList -> " + appList.getPreferenceCount());
}
private boolean shouldAddPreference(AppEntry app) {
return app != null && UserHandle.isApp(app.info.uid);
}
private void removeUselessPrefs(final Set<String> appsKeySet) {
final int prefCount = mAppList.getPreferenceCount();
android.util.Log.d("xin.wang", "<- prefCount -> " + prefCount);
String prefKey;
if (prefCount > 0) {
for (int i = prefCount - 1; i >= 0; i--) {
Preference pref = mAppList.getPreference(i);
prefKey = pref.getKey();
boolean isEmpty = !appsKeySet.isEmpty();
boolean contains = appsKeySet.contains(prefKey);
android.util.Log.d("xin.wang", "<- isEmpty -> " + isEmpty + "<- contains -> " + contains + "<- appsKeySet -> " + appsKeySet + "<- preKey ->" + prefKey);
if (!appsKeySet.isEmpty() && appsKeySet.contains(prefKey)) {
continue;
}
mAppList.removePreference(pref);
}
}
}
public void rebuild() {
final ArrayList<AppEntry> apps = mSession.rebuild(mFilter,
ApplicationsState.ALPHA_COMPARATOR);
boolean flag = apps != null;
if (apps != null) {
android.util.Log.d("xin.wang", "rebuild: <-- apps -> " + apps.size());
onRebuildComplete(apps);
}
}
}
ParallelAppPreference.java点击swicth开启分身的时候创建一个Toast来提醒

关闭分身弹出dialog,判断是否进行关闭

/*
* Copyright (C) 2017 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.parallelapp;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.DialogPreference;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.widget.AppSwitchPreference;
import java.util.List;
public class ParallelAppPreference extends AppSwitchPreference {
private final ApplicationsState mApplicationsState;
private final AppEntry mEntry;
private final PackageInstaller mPackageInstaller;
private final PackageManager mPm;
private final String TAG = "ParallelAppPreference";
private final UserManager mUm;
private Activity mActivity;
private Context mContext;
public ParallelAppPreference (final Context context, AppEntry entry,
ApplicationsState applicationsState, UserManager um, PackageManager pm, PackageInstaller pi) {
super(context);
mContext = context;
setWidgetLayoutResource(R.layout.restricted_switch_widget);
mEntry = entry;
mEntry.ensureLabel(context);
mApplicationsState = applicationsState;
mPm = pm;
mPackageInstaller = pi;
mUm = um;
setKey(generateKey(mEntry));
if (mEntry.icon == null) {
mApplicationsState.ensureIcon(mEntry);
}
setIcon(mEntry.icon);
setTitle(mEntry.label);
updateState();
}
static String generateKey(final AppEntry entry) {
android.util.Log.d("xin.wang", "generateKey: -> AppEntry -> " + entry.info.packageName + " | " + entry.info.uid);
return entry.info.packageName + "|" + entry.info.uid;
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
if (mEntry.icon == null) {
holder.itemView.post(new Runnable() {
@Override
public void run() {
mApplicationsState.ensureIcon(mEntry);
setIcon(mEntry.icon);
}
});
}
final View widgetFrame = holder.findViewById(android.R.id.widget_frame);
widgetFrame.setVisibility(View.VISIBLE);
super.onBindViewHolder(holder);
holder.findViewById(R.id.restricted_icon).setVisibility(View.GONE);
holder.findViewById(android.R.id.switch_widget).setVisibility(View.VISIBLE);
}
// 更新状态
public void updateState() {
boolean isChecked = false;
boolean hasCloneUser = hasCloneUser();
try {
if (hasCloneUser) {
ApplicationInfo info = mPm.getApplicationInfoAsUser(
mEntry.info.packageName, PackageManager.GET_META_DATA, getCloneUser());
isChecked = (info.flags & ApplicationInfo.FLAG_INSTALLED) != 0 ? true : false;
android.util.Log.d("xin.wang", "updateState -> 1 -> : " + isChecked);
}
android.util.Log.d("xin.wang", "updateState -> 2 -> : " + isChecked);
setChecked(isChecked);
} catch (NameNotFoundException e) {
Log.e(TAG,"can not found Application "+mEntry.label);
}
}
//关闭应用分身的时候弹出的dialog -> parallel_app_close -> 当关闭时分身应用的所有数据将会被清除。您确定要继续吗?
public void createCloseOptionsDialog(Activity activity) {
mActivity = activity;
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setMessage(mContext.getResources().getString(R.string.parallel_app_close));
builder.setPositiveButton(android.R.string.ok, mDialogClickListener);
builder.setNegativeButton(android.R.string.cancel, mDialogClickListener);
AlertDialog dialog = builder.create();
dialog.setCancelable(false);
dialog.show();
}
// 开始手机分身
public void openParApp() {
if (!hasCloneUser()) {
createAndStartCloneUser();
}
try {
//应用双开的方法 -> installExistingPackageAsUser(String packageName,int userId);
int status = mPm.installExistingPackageAsUser(mEntry.info.packageName, getCloneUser());
android.util.Log.d("xin.wang", "openParApp: -> status -> " + status + "<- mEntry.info.packageName -> " + mEntry.info.packageName);
if (status == PackageManager.INSTALL_SUCCEEDED) { //PackageManager.INSTALL_SUCCEEDED -> 返回安装成功的状态码 1
android.util.Log.d("xin.wang", "openParApp: -> status -> " + status);
createToast();
} else {
Log.d(TAG, "open Parallel App failed status:"+status);
}
} catch (PackageManager.NameNotFoundException e) {
Log.d(TAG, "open Parallel App failed ,"+e);
}
}
// 创建开启分身的Toast -> 分身的 %1$s 已经创建
private void createToast() {
String content = String.format(mContext.getResources().getString(R.string.parallel_app_open), mEntry.label);
Toast.makeText(mContext, content, Toast.LENGTH_LONG).show();
}
// 获取克隆的用户
private int getCloneUser() {
int cloneUserId = UserHandle.myUserId();
int userCount = 0;
List<UserInfo> users = mUm.getUsers(); //UserManager.getUser() -> 获取所有的用户信息
for(UserInfo user : users){
userCount++;
android.util.Log.d("xin.wang", "getCloneUser: users -> " + userCount);
if (user.userType != null && user.userType.equals(UserManager.USER_TYPE_PROFILE_CLONE)) {
cloneUserId = user.id;
}
}
return cloneUserId;
}
//判断是否克隆
private boolean hasCloneUser() {
boolean hasCloneUser = false;
List<UserInfo> users = mUm.getUsers(); // UserManager.getUsers() -> 获取所有的用户信息
android.util.Log.d("xin.wang", "hasCloneUser: -> user -> " + users + "<- user.size() -> " + users.size());
for(UserInfo user : users){
android.util.Log.d("xin.wang", "hasCloneUser: -> user.userType -> " + user.userType);
if (user.userType != null && user.userType.equals(UserManager.USER_TYPE_PROFILE_CLONE)) {
hasCloneUser = true;
break;
}
}
return hasCloneUser;
}
//创建第二个user用户
private void createAndStartCloneUser() {
String userName = mContext.getResources().getString(R.string.clone_user);
UserInfo userInfo = mUm.createProfileForUser(userName, UserManager.USER_TYPE_PROFILE_CLONE,
UserInfo.FLAG_PROFILE, UserHandle.USER_SYSTEM);
IActivityManager ams = ActivityManager.getService();
try {
ams.startUserInBackground(userInfo.getUserHandle().getIdentifier());
} catch(RemoteException e) {
Log.d(TAG, "create and start clone user failed");
e.printStackTrace();
}
}
//关闭手机分身的时候弹出的dialog
private DialogInterface.OnClickListener mDialogClickListener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
if (arg1 == DialogInterface.BUTTON_NEGATIVE) { //DialogInterface.BUTTON_NEGATIVE -> 取消按钮,点击dialog的取消按钮
setChecked(true);
} else {
//deletePackageAsUser(String packageName,IPackageDeleteObserver observer,int Flags,int userId);
mPm.deletePackageAsUser(mEntry.info.packageName, null, 0, getCloneUser());
android.util.Log.d("xin.wang", "DialogInterface.OnClickListener -> mEntry.info.packageName -> " + mEntry.info.packageName);
}
}
};
}

通过该界面的布局文件app.xml中的key-> recent_apps_category
AppsPreferenceController.java类中,该类计算当前应用的数量,并且显示当前使用的应用数量通过分析,可知,该方法中的loadAllAppsCount()加载所有app的数量,该方法实例化InstalledAppCounter类,并重写onCountComplete()方法
/*
* Copyright (C) 2021 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;
import android.app.Application;
import android.app.usage.UsageStats;
import android.content.Context;
import android.icu.text.RelativeDateTimeFormatter;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.Utils;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.utils.StringUtil;
import com.android.settingslib.widget.AppPreference;
import java.util.List;
import java.util.Map;
/**
* This controller displays up to four recently used apps.
* If there is no recently used app, we only show up an "App Info" preference.
*/
public class AppsPreferenceController extends BasePreferenceController implements
LifecycleObserver {
public static final int SHOW_RECENT_APP_COUNT = 4;
@VisibleForTesting
static final String KEY_RECENT_APPS_CATEGORY = "recent_apps_category";
@VisibleForTesting
static final String KEY_GENERAL_CATEGORY = "general_category";
@VisibleForTesting
static final String KEY_ALL_APP_INFO = "all_app_infos";
@VisibleForTesting
static final String KEY_SEE_ALL = "see_all_apps";
private final ApplicationsState mApplicationsState;
private final int mUserId;
@VisibleForTesting
List<UsageStats> mRecentApps;
@VisibleForTesting
PreferenceCategory mRecentAppsCategory;
@VisibleForTesting
PreferenceCategory mGeneralCategory;
@VisibleForTesting
Preference mAllAppsInfoPref;
@VisibleForTesting
Preference mSeeAllPref;
private Fragment mHost;
private boolean mInitialLaunch = false;
public AppsPreferenceController(Context context) {
super(context, KEY_RECENT_APPS_CATEGORY);
mApplicationsState = ApplicationsState.getInstance(
(Application) mContext.getApplicationContext());
mUserId = UserHandle.myUserId();
}
public void setFragment(Fragment fragment) {
mHost = fragment;
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE_UNSEARCHABLE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
initPreferences(screen);
refreshUi();
mInitialLaunch = true;
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
if (!mInitialLaunch) {
refreshUi();
}
}
/**
* Called when the apps page pauses.
*/
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void onPause() {
mInitialLaunch = false;
}
@VisibleForTesting
void refreshUi() {
loadAllAppsCount();
mRecentApps = loadRecentApps();
android.util.Log.d("xin.wang", "mRecentApps -> : " + mRecentApps.size() + "<- loadAllAppsCount -> " + loadRecentApps());
if (!mRecentApps.isEmpty()) {
displayRecentApps();
mAllAppsInfoPref.setVisible(false);
mRecentAppsCategory.setVisible(true);
mGeneralCategory.setVisible(true);
mSeeAllPref.setVisible(true);
} else {
mAllAppsInfoPref.setVisible(true);
mRecentAppsCategory.setVisible(false);
mGeneralCategory.setVisible(false);
mSeeAllPref.setVisible(false);
}
}
// 计算应用的数量
@VisibleForTesting
void loadAllAppsCount() {
// Show total number of installed apps as See all's summary.
new InstalledAppCounter(mContext, InstalledAppCounter.IGNORE_INSTALL_REASON,
mContext.getPackageManager()) {
@Override
protected void onCountComplete(int num) {
if (!mRecentApps.isEmpty()) {
mSeeAllPref.setTitle(
mContext.getResources().getQuantityString(R.plurals.see_all_apps_title,
num, num));
android.util.Log.d("xin.wang", "onCountComplete: -> AppsCount -> " + num);
} else {
mAllAppsInfoPref.setSummary(mContext.getString(R.string.apps_summary, num));
}
}
}.execute();
}
@VisibleForTesting
List<UsageStats> loadRecentApps() {
final RecentAppStatsMixin recentAppStatsMixin = new RecentAppStatsMixin(mContext,
SHOW_RECENT_APP_COUNT);
recentAppStatsMixin.loadDisplayableRecentApps(SHOW_RECENT_APP_COUNT);
return recentAppStatsMixin.mRecentApps;
}
private void initPreferences(PreferenceScreen screen) {
mRecentAppsCategory = screen.findPreference(KEY_RECENT_APPS_CATEGORY);
mGeneralCategory = screen.findPreference(KEY_GENERAL_CATEGORY);
mAllAppsInfoPref = screen.findPreference(KEY_ALL_APP_INFO);
mSeeAllPref = screen.findPreference(KEY_SEE_ALL);
mRecentAppsCategory.setVisible(false);
mGeneralCategory.setVisible(false);
mAllAppsInfoPref.setVisible(false);
mSeeAllPref.setVisible(false);
}
private void displayRecentApps() {
if (mRecentAppsCategory != null) {
final Map<String, Preference> existedAppPreferences = new ArrayMap<>();
final int prefCount = mRecentAppsCategory.getPreferenceCount();
android.util.Log.d("xin.wang", "<- prefCount -> : " + prefCount);
for (int i = 0; i < prefCount; i++) {
final Preference pref = mRecentAppsCategory.getPreference(i);
final String key = pref.getKey();
// android.util.Log.d("xin.wang", "for each key -> : " + key);
if (!TextUtils.equals(key, KEY_SEE_ALL)) {
existedAppPreferences.put(key, pref);
}
android.util.Log.d("xin.wang", "<- Map -> " + existedAppPreferences);
}
int showAppsCount = 0;
android.util.Log.d("xin.wang", "List mRecentApps -> : " + mRecentApps);
for (UsageStats stat : mRecentApps) {
final String pkgName = stat.getPackageName();
final ApplicationsState.AppEntry appEntry =
mApplicationsState.getEntry(pkgName, mUserId);
if (appEntry == null) {
continue;
}
boolean rebindPref = true;
Preference pref = existedAppPreferences.remove(pkgName);
if (pref == null) {
pref = new AppPreference(mContext);
rebindPref = false;
}
android.util.Log.d("xin.wang", "pkgName -> : " + pkgName + "<- appEntry.label -> : " + appEntry.label + "<- pref -> " + pref);
pref.setKey(pkgName);
pref.setTitle(appEntry.label);
pref.setIcon(Utils.getBadgedIcon(mContext, appEntry.info));
pref.setSummary(StringUtil.formatRelativeTime(mContext,
System.currentTimeMillis() - stat.getLastTimeUsed(), false,
RelativeDateTimeFormatter.Style.SHORT));
pref.setOrder(showAppsCount++);
pref.setOnPreferenceClickListener(preference -> {
AppInfoBase.startAppInfoFragment(AppInfoDashboardFragment.class,
R.string.application_info_label, pkgName, appEntry.info.uid,
mHost, 1001 /*RequestCode*/, getMetricsCategory());
return true;
});
if (!rebindPref) {
mRecentAppsCategory.addPreference(pref);
}
}
// Remove unused preferences from pref category.
for (Preference unusedPref : existedAppPreferences.values()) {
mRecentAppsCategory.removePreference(unusedPref);
}
}
}
}
AppCounter.java中doInBackground方法进行计算总数量,该计算方法,通过遍历用户和应用双重遍历进行计算,所以算出来的数量double,所以在doInBackground(Void... params)中,只获取第一个用户,在进行遍历应用
/*
* Copyright (C) 2016 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;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.AsyncTask;
import android.os.UserHandle;
import android.os.UserManager;
import java.util.List;
public abstract class AppCounter extends AsyncTask<Void, Void, Integer> {
protected final PackageManager mPm;
protected final UserManager mUm;
public AppCounter(Context context, PackageManager packageManager) {
mPm = packageManager;
mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
}
@Override
protected Integer doInBackground(Void... params) {
//int count = 0;
//int testNum = 0;
//int testNum2 = 0;
//android.util.Log.d("xin.wang", "doInBackground:UserInfo-> " + testNum);
UserInfo user = mUm.getProfiles(UserHandle.myUserId()).get(0);
final List<ApplicationInfo> list =
mPm.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
| PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS
| (user.isAdmin() ? PackageManager.MATCH_ANY_USER : 0),
user.id);
for (ApplicationInfo info : list) {
//testNum2 ++;
//android.util.Log.d("xin.wang", "doInBackground: ApplicationInfo -> " + testNum2);
if (includeInCount(info)) {
count++;
//android.util.Log.d("xin.wang", "doInBackground: includeInCount -> " + count);
}
}
/*for (UserInfo user : mUm.getProfiles(UserHandle.myUserId())) {
testNum ++ ;
android.util.Log.d("xin.wang", "doInBackground:UserInfo-> " + testNum);
final List list =
mPm.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
| PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS
| (user.isAdmin() ? PackageManager.MATCH_ANY_USER : 0),
user.id);
for (ApplicationInfo info : list) {
testNum2 ++;
android.util.Log.d("xin.wang", "doInBackground: ApplicationInfo -> " + testNum2);
if (includeInCount(info)) {
count++;
android.util.Log.d("xin.wang", "doInBackground: includeInCount -> " + count);
}
}
}*/
return count;
}
@Override
protected void onPostExecute(Integer count) {
onCountComplete(count);
}
void executeInForeground() {
onPostExecute(doInBackground());
}
protected abstract void onCountComplete(int num);
protected abstract boolean includeInCount(ApplicationInfo info);
}

该界面的方法ManageApplications.java类来显示应用图标和名字
该类有两个内部类适配器
FilterSpinnerAdapter-> SettingsSpinnerAdapter的适配器

ApplicationsAdapter-> RecyclerView的适配器

createHeader()在该方法中创建头部SpinnerHeader,关闭头部的时候只显示一个FILTER_APPS_PERSONAL的应用
@VisibleForTesting
void createHeader() {
final Activity activity = getActivity();
final FrameLayout pinnedHeader = mRootView.findViewById(R.id.pinned_header);
mSpinnerHeader = activity.getLayoutInflater()
.inflate(R.layout.manage_apps_filter_spinner, pinnedHeader, false);
mFilterSpinner = mSpinnerHeader.findViewById(R.id.filter_spinner);
mFilterAdapter = new FilterSpinnerAdapter(this);
mFilterSpinner.setAdapter(mFilterAdapter);
mFilterSpinner.setOnItemSelectedListener(this);
mFilterSpinner.setVisibility(View.GONE);
pinnedHeader.addView(mSpinnerHeader, 0);
final AppFilterRegistry appFilterRegistry = AppFilterRegistry.getInstance();
final int filterType = appFilterRegistry.getDefaultFilterType(mListType);
android.util.Log.d("xin.wang", "createHeader:-> filterType -> " + mListType + "<- filterType -> " + filterType); //filterType = 4 -> FILTER_APPS_ALL
android.util.Log.d("xin.wang", "createHeader:->mFilterOptions.size()-> " + mFilterAdapter.getCount());
//mFilterAdapter.enableFilter(filterType); //filterType = 4 -> FILTER_APPS_ALL
if (mListType == LIST_TYPE_MAIN) {
if (UserManager.get(getActivity()).getUserProfiles().size() > 1 && !mIsWorkOnly
&& !mIsPersonalOnly) {
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.enableFilter(FILTER_APPS_BLOCKED);
mFilterAdapter.enableFilter(FILTER_APPS_ALL);
}
if (mListType == LIST_TYPE_HIGH_POWER) {
mFilterAdapter.enableFilter(FILTER_APPS_POWER_ALLOWLIST_ALL);
}
setCompositeFilter();
}
/*
* 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_BLOCKED;
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_ALLOWLIST;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_POWER_ALLOWLIST_ALL;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_RECENT;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_WORK;
import static com.android.settings.search.actionbar.SearchMenuController.MENU_SEARCH;
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.settings.SettingsEnums;
import android.app.usage.IUsageStatsManager;
import android.compat.annotation.ChangeId;
import android.compat.annotation.LoggingOnly;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.preference.PreferenceFrameLayout;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.IconDrawableFactory;
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.Filter;
import android.widget.FrameLayout;
import android.widget.SearchView;
import android.widget.Spinner;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.compat.IPlatformCompat;
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.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.Utils;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.applications.AppStateAlarmsAndRemindersBridge;
import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
import com.android.settings.applications.AppStateBaseBridge;
import com.android.settings.applications.AppStateInstallAppsBridge;
import com.android.settings.applications.AppStateManageExternalStorageBridge;
import com.android.settings.applications.AppStateMediaManagementAppsBridge;
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.UsageAccessDetails;
import com.android.settings.applications.appinfo.AlarmsAndRemindersDetails;
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.ManageExternalStorageDetails;
import com.android.settings.applications.appinfo.MediaManagementAppsDetails;
import com.android.settings.applications.appinfo.WriteSettingsDetails;
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
import com.android.settings.fuelgauge.HighPowerDetail;
import com.android.settings.notification.ConfigureNotificationSettings;
import com.android.settings.notification.NotificationBackend;
import com.android.settings.notification.app.AppNotificationSettings;
import com.android.settings.widget.LoadingViewController;
import com.android.settings.wifi.AppStateChangeWifiStateBridge;
import com.android.settings.wifi.ChangeWifiStateDetails;
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.AppUtils;
import com.android.settingslib.fuelgauge.PowerAllowlistBackend;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter;
import com.google.android.material.appbar.AppBarLayout;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
/**
* 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, SearchView.OnQueryTextListener,
MenuItem.OnActionExpandListener {
static final String TAG = "ManageApplications";
static final boolean DEBUG = Build.IS_DEBUGGABLE;
// 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_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";
private static final String EXTRA_FILTER_TYPE = "filterType";
@VisibleForTesting
static final String EXTRA_EXPAND_SEARCH_VIEW = "expand_search_view";
// 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_LEGACY = 1; // Show apps even if they can be categorized.
/**
* Intents with action {@code android.settings.MANAGE_APP_OVERLAY_PERMISSION}
* and data URI scheme {@code package} don't go to the app-specific screen for managing the
* permission anymore. Instead, they redirect to this screen for managing all the apps that have
* requested such permission.
*/
@ChangeId
@LoggingOnly
private static final long CHANGE_RESTRICT_SAW_INTENT = 135920175L;
// 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 SearchView mSearchView;
// 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_WIFI_ACCESS = 10;
public static final int LIST_MANAGE_EXTERNAL_STORAGE = 11;
public static final int LIST_TYPE_ALARMS_AND_REMINDERS = 12;
public static final int LIST_TYPE_MEDIA_MANAGEMENT_APPS = 13;
// List types that should show instant apps.
public static final Set LIST_TYPES_WITH_INSTANT = new ArraySet<>(Arrays.asList(
LIST_TYPE_MAIN,
LIST_TYPE_STORAGE));
@VisibleForTesting
View mSpinnerHeader;
@VisibleForTesting
FilterSpinnerAdapter mFilterAdapter;
@VisibleForTesting
RecyclerView mRecyclerView;
// Whether or not search view is expanded.
@VisibleForTesting
boolean mExpandSearch;
private View mRootView;
private Spinner mFilterSpinner;
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 boolean mIsPersonalOnly;
private View mEmptyView;
private int mFilterType;
private AppBarLayout mAppBarLayout;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
final Activity activity = getActivity();
mUserManager = activity.getSystemService(UserManager.class);
mApplicationsState = ApplicationsState.getInstance(activity.getApplication());
Intent intent = activity.getIntent();
Bundle args = getArguments();
int screenTitle = intent.getIntExtra(
SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, R.string.all_apps);
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;
reportIfRestrictedSawIntent(intent);
} 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(Settings.ChangeWifiStateActivity.class.getName())) {
mListType = LIST_TYPE_WIFI_ACCESS;
screenTitle = R.string.change_wifi_state_title;
} else if (className.equals(Settings.ManageExternalStorageActivity.class.getName())) {
mListType = LIST_MANAGE_EXTERNAL_STORAGE;
screenTitle = R.string.manage_external_storage_title;
} else if (className.equals(Settings.MediaManagementAppsActivity.class.getName())) {
mListType = LIST_TYPE_MEDIA_MANAGEMENT_APPS;
screenTitle = R.string.media_management_apps_title;
} else if (className.equals(Settings.AlarmsAndRemindersActivity.class.getName())) {
mListType = LIST_TYPE_ALARMS_AND_REMINDERS;
screenTitle = R.string.alarms_and_reminders_title;
} else if (className.equals(Settings.NotificationAppListActivity.class.getName())) {
mListType = LIST_TYPE_NOTIFICATION;
mUsageStatsManager = IUsageStatsManager.Stub.asInterface(
ServiceManager.getService(Context.USAGE_STATS_SERVICE));
mNotificationBackend = new NotificationBackend();
mSortOrder = R.id.sort_order_recent_notification;
screenTitle = R.string.app_notifications_title;
} else {
if (screenTitle == -1) {
screenTitle = R.string.all_apps;
}
mListType = LIST_TYPE_MAIN;
}
final AppFilterRegistry appFilterRegistry = AppFilterRegistry.getInstance();
mFilter = appFilterRegistry.get(appFilterRegistry.getDefaultFilterType(mListType));
mIsPersonalOnly = args != null ? args.getInt(ProfileSelectFragment.EXTRA_PROFILE)
== ProfileSelectFragment.ProfileType.PERSONAL : false;
mIsWorkOnly = args != null ? args.getInt(ProfileSelectFragment.EXTRA_PROFILE)
== ProfileSelectFragment.ProfileType.WORK : false;
mWorkUserId = args != null ? args.getInt(EXTRA_WORK_ID) : UserHandle.myUserId();
if (mIsWorkOnly && mWorkUserId == UserHandle.myUserId()) {
mWorkUserId = Utils.getManagedProfileId(mUserManager, UserHandle.myUserId());
}
mExpandSearch = activity.getIntent().getBooleanExtra(EXTRA_EXPAND_SEARCH_VIEW, false);
if (savedInstanceState != null) {
mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder);
mShowSystem = savedInstanceState.getBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
mFilterType =
savedInstanceState.getInt(EXTRA_FILTER_TYPE, AppFilterRegistry.FILTER_APPS_ALL);
mExpandSearch = savedInstanceState.getBoolean(EXTRA_EXPAND_SEARCH_VIEW);
}
mInvalidSizeStr = activity.getText(R.string.invalid_size_value);
mResetAppsHelper = new ResetAppsHelper(activity);
if (screenTitle > 0) {
activity.setTitle(screenTitle);
}
}
private void reportIfRestrictedSawIntent(Intent intent) {
try {
Uri data = intent.getData();
if (data == null || !TextUtils.equals("package", data.getScheme())) {
// Not a restricted intent
return;
}
IBinder activityToken = getActivity().getActivityToken();
int callingUid = ActivityManager.getService().getLaunchedFromUid(activityToken);
if (callingUid == -1) {
Log.w(TAG, "Error obtaining calling uid");
return;
}
IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
if (platformCompat == null) {
Log.w(TAG, "Error obtaining IPlatformCompat service");
return;
}
platformCompat.reportChangeByUid(CHANGE_RESTRICT_SAW_INTENT, callingUid);
} catch (RemoteException e) {
Log.w(TAG, "Error reporting SAW intent restriction", e);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (mListType == LIST_TYPE_OVERLAY && !Utils.isSystemAlertWindowEnabled(getContext())) {
mRootView = inflater.inflate(R.layout.manage_applications_apps_unsupported, null);
setHasOptionsMenu(false);
return mRootView;
}
mRootView = inflater.inflate(R.layout.manage_applications_apps, null);
mLoadingContainer = mRootView.findViewById(R.id.loading_container);
mEmptyView = mRootView.findViewById(android.R.id.empty);
mRecyclerView = mRootView.findViewById(R.id.apps_list);
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);
}
mRecyclerView.setItemAnimator(null);
mRecyclerView.setLayoutManager(new LinearLayoutManager(
getContext(), RecyclerView.VERTICAL, false /* reverseLayout */));
mRecyclerView.setAdapter(mApplications);
android.util.Log.d("xin.wang", "onCreateView:mRecyclerView.setAdapter-> " + Thread.currentThread().getName());
// 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);
mAppBarLayout = getActivity().findViewById(R.id.app_bar);
disableToolBarScrollableBehavior();
return mRootView;
}
@VisibleForTesting
void createHeader() {
final Activity activity = getActivity();
final FrameLayout pinnedHeader = mRootView.findViewById(R.id.pinned_header);
mSpinnerHeader = activity.getLayoutInflater()
.inflate(R.layout.manage_apps_filter_spinner, pinnedHeader, false);
mFilterSpinner = mSpinnerHeader.findViewById(R.id.filter_spinner);
mFilterAdapter = new FilterSpinnerAdapter(this);
mFilterSpinner.setAdapter(mFilterAdapter);
mFilterSpinner.setOnItemSelectedListener(this);
mFilterSpinner.setVisibility(View.GONE);
pinnedHeader.addView(mSpinnerHeader, 0);
final AppFilterRegistry appFilterRegistry = AppFilterRegistry.getInstance();
final int filterType = appFilterRegistry.getDefaultFilterType(mListType);
android.util.Log.d("xin.wang", "createHeader:-> filterType -> " + mListType + "<- filterType -> " + filterType); //filterType = 4 -> FILTER_APPS_ALL
android.util.Log.d("xin.wang", "createHeader:->mFilterOptions.size()-> " + mFilterAdapter.getCount());
//mFilterAdapter.enableFilter(filterType); //filterType = 4 -> FILTER_APPS_ALL
if (mListType == LIST_TYPE_MAIN) {
if (UserManager.get(getActivity()).getUserProfiles().size() > 1 && !mIsWorkOnly
&& !mIsPersonalOnly) {
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.enableFilter(FILTER_APPS_BLOCKED);
mFilterAdapter.enableFilter(FILTER_APPS_ALL);
}
if (mListType == LIST_TYPE_HIGH_POWER) {
mFilterAdapter.enableFilter(FILTER_APPS_POWER_ALLOWLIST_ALL);
}
setCompositeFilter();
}
@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_DEFAULT) {
filter = new CompoundFilter(ApplicationsState.FILTER_APPS_EXCEPT_GAMES, filter);
}
return filter;
}
if (listType == LIST_TYPE_GAMES) {
return new CompoundFilter(ApplicationsState.FILTER_GAMES, filter);
}
return null;
}
@Override
public int getMetricsCategory() {
switch (mListType) {
case LIST_TYPE_MAIN:
return SettingsEnums.MANAGE_APPLICATIONS;
case LIST_TYPE_NOTIFICATION:
return SettingsEnums.MANAGE_APPLICATIONS_NOTIFICATIONS;
case LIST_TYPE_STORAGE:
return SettingsEnums.APPLICATIONS_STORAGE_APPS;
case LIST_TYPE_GAMES:
return SettingsEnums.APPLICATIONS_STORAGE_GAMES;
case LIST_TYPE_USAGE_ACCESS:
return SettingsEnums.USAGE_ACCESS;
case LIST_TYPE_HIGH_POWER:
return SettingsEnums.APPLICATIONS_HIGH_POWER_APPS;
case LIST_TYPE_OVERLAY:
return SettingsEnums.SYSTEM_ALERT_WINDOW_APPS;
case LIST_TYPE_WRITE_SETTINGS:
return SettingsEnums.SYSTEM_ALERT_WINDOW_APPS;
case LIST_TYPE_MANAGE_SOURCES:
return SettingsEnums.MANAGE_EXTERNAL_SOURCES;
case LIST_TYPE_WIFI_ACCESS:
return SettingsEnums.CONFIGURE_WIFI;
case LIST_MANAGE_EXTERNAL_STORAGE:
return SettingsEnums.MANAGE_EXTERNAL_STORAGE;
case LIST_TYPE_ALARMS_AND_REMINDERS:
return SettingsEnums.ALARMS_AND_REMINDERS;
case LIST_TYPE_MEDIA_MANAGEMENT_APPS:
return SettingsEnums.MEDIA_MANAGEMENT_APPS;
default:
return SettingsEnums.PAGE_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.putInt(EXTRA_FILTER_TYPE, mFilter.getFilterType());
outState.putBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
//add bug 1702049:NullPointerException on a null object reference
if (mApplications != null) {
outState.putBoolean(EXTRA_HAS_ENTRIES, mApplications.mHasReceivedLoadEntries);
outState.putBoolean(EXTRA_HAS_BRIDGE, mApplications.mHasReceivedBridgeCallback);
if (mSearchView != null) {
outState.putBoolean(EXTRA_EXPAND_SEARCH_VIEW, !mSearchView.isIconified());
}
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));
}
}
}
private void setCompositeFilter() {
AppFilter compositeFilter = getCompositeFilter(mListType, mStorageType, mVolumeUuid);
if (compositeFilter == null) {
compositeFilter = mFilter.getFilter();
}
if (mIsWorkOnly) {
compositeFilter = new CompoundFilter(compositeFilter, ApplicationsState.FILTER_WORK);
}
if (mIsPersonalOnly) {
compositeFilter = new CompoundFilter(compositeFilter,
ApplicationsState.FILTER_PERSONAL);
}
mApplications.setCompositeFilter(compositeFilter);
}
// 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_WIFI_ACCESS:
startAppInfoFragment(ChangeWifiStateDetails.class,
R.string.change_wifi_state_title);
break;
case LIST_MANAGE_EXTERNAL_STORAGE:
startAppInfoFragment(ManageExternalStorageDetails.class,
R.string.manage_external_storage_title);
break;
case LIST_TYPE_ALARMS_AND_REMINDERS:
startAppInfoFragment(AlarmsAndRemindersDetails.class,
R.string.alarms_and_reminders_label);
break;
case LIST_TYPE_MEDIA_MANAGEMENT_APPS:
startAppInfoFragment(MediaManagementAppsDetails.class,
R.string.media_management_apps_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;
}
mOptionsMenu = menu;
inflater.inflate(R.menu.manage_apps, menu);
final MenuItem searchMenuItem = menu.findItem(R.id.search_app_list_menu);
if (searchMenuItem != null) {
searchMenuItem.setOnActionExpandListener(this);
mSearchView = (SearchView) searchMenuItem.getActionView();
mSearchView.setQueryHint(getText(R.string.search_settings));
mSearchView.setOnQueryTextListener(this);
if (mExpandSearch) {
searchMenuItem.expandActionView();
}
}
updateOptionsMenu();
}
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
// To prevent a large space on tool bar.
mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/);
// To prevent user can expand the collapsing tool bar view.
ViewCompat.setNestedScrollingEnabled(mRecyclerView, false);
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
// We keep the collapsed status after user cancel the search function.
mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/);
ViewCompat.setNestedScrollingEnabled(mRecyclerView, true);
return true;
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
updateOptionsMenu();
}
@Override
public void onDestroyOptionsMenu() {
mOptionsMenu = null;
}
@StringRes
int getHelpResource() {
switch (mListType) {
case LIST_TYPE_NOTIFICATION:
return R.string.help_uri_notifications;
case LIST_TYPE_USAGE_ACCESS:
return R.string.help_url_usage_access;
case LIST_TYPE_STORAGE:
return R.string.help_uri_apps_storage;
case LIST_TYPE_HIGH_POWER:
return R.string.help_uri_apps_high_power;
case LIST_TYPE_OVERLAY:
return R.string.help_uri_apps_overlay;
case LIST_TYPE_WRITE_SETTINGS:
return R.string.help_uri_apps_write_settings;
case LIST_TYPE_MANAGE_SOURCES:
return R.string.help_uri_apps_manage_sources;
case LIST_TYPE_GAMES:
return R.string.help_uri_apps_overlay;
case LIST_TYPE_WIFI_ACCESS:
return R.string.help_uri_apps_wifi_access;
case LIST_MANAGE_EXTERNAL_STORAGE:
return R.string.help_uri_manage_external_storage;
case LIST_TYPE_ALARMS_AND_REMINDERS:
return R.string.help_uri_alarms_and_reminders;
case LIST_TYPE_MEDIA_MANAGEMENT_APPS:
return R.string.help_uri_media_management_apps;
default:
case LIST_TYPE_MAIN:
return R.string.help_uri_apps;
}
}
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 && wheatherShowSystemOption());
mOptionsMenu.findItem(R.id.hide_system).setVisible(mShowSystem
&& mListType != LIST_TYPE_HIGH_POWER && wheatherShowSystemOption());
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);
final MenuItem searchItem = mOptionsMenu.findItem(MENU_SEARCH);
if (searchItem != null) {
searchItem.setVisible(false);
}
}
//hide the optionsmenu of hide-system and show-system in the work interface
private boolean wheatherShowSystemOption() {
boolean hasManageProfile = false;
List users = mUserManager.getUsers();
android.util.Log.d("xin.wang", "wheatherShowSystemOption: " + users + "<- users.size->" + users.size());
for(UserInfo user : users){
if (user.isManagedProfile()) {
hasManageProfile = true;
break;
}
}
if (mIsWorkOnly && !mIsPersonalOnly) {
if (!hasManageProfile) {
return false;
}
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int menuId = item.getItemId();
int i = item.getItemId();
if (i == R.id.sort_order_alpha || i == R.id.sort_order_size) {
if (mApplications != null) {
mApplications.rebuild(menuId);
}
} else if (i == R.id.show_system || i == R.id.hide_system) {
mShowSystem = !mShowSystem;
mApplications.rebuild();
} else if (i == R.id.reset_app_preferences) {
mResetAppsHelper.buildResetDialog();
return true;
} else if (i == R.id.advanced) {
if (mListType == LIST_TYPE_NOTIFICATION) {
new SubSettingLauncher(getContext())
.setDestination(ConfigureNotificationSettings.class.getName())
.setTitleRes(R.string.configure_notification_settings)
.setSourceMetricsCategory(getMetricsCategory())
.setResultListener(this, ADVANCED_SETTINGS)
.launch();
} else {
Intent intent = new Intent(
android.provider.Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS);
startActivityForResult(intent, ADVANCED_SETTINGS);
}
return true;
} else {// 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();
// We disable the scrolling ability in onMenuItemActionCollapse, we should recover it
// if user selects any app item.
ViewCompat.setNestedScrollingEnabled(mRecyclerView, true);
}
}
@Override
public void onItemSelected(AdapterView> parent, View view, int position, long id) {
mFilter = mFilterAdapter.getFilter(position);
setCompositeFilter();
mApplications.setFilter(mFilter);
if (DEBUG) {
Log.d(TAG, "Selecting filter " + getContext().getText(mFilter.getTitle()));
}
}
@Override
public void onNothingSelected(AdapterView> parent) {
}
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
mApplications.filterSearch(newText);
return false;
}
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);
}
}
private void disableToolBarScrollableBehavior() {
final CoordinatorLayout.LayoutParams params =
(CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams();
final AppBarLayout.Behavior behavior = new AppBarLayout.Behavior();
behavior.setDragCallback(
new AppBarLayout.Behavior.DragCallback() {
@Override
public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
return false;
}
});
params.setBehavior(behavior);
}
static class FilterSpinnerAdapter extends SettingsSpinnerAdapter {
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 mFilterOptions = new ArrayList<>();
public FilterSpinnerAdapter(ManageApplications manageApplications) {
super(manageApplications.getContext());
mContext = manageApplications.getContext();
mManageApplications = manageApplications;
}
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 " + mContext.getText(filter.getTitle()));
}
mFilterOptions.add(filter);
Collections.sort(mFilterOptions);
updateFilterView(mFilterOptions.size() > 1);
notifyDataSetChanged();
if (mFilterOptions.size() == 1) {
if (DEBUG) {
Log.d(TAG, "Auto selecting filter " + filter + " " + mContext.getText(
filter.getTitle()));
}
mManageApplications.mFilterSpinner.setSelection(0);
mManageApplications.onItemSelected(null, null, 0, 0);
}
if (mFilterOptions.size() > 1) {
final AppFilterItem previousFilter = AppFilterRegistry.getInstance().get(
mManageApplications.mFilterType);
final int index = mFilterOptions.indexOf(previousFilter);
if (index != -1) {
android.util.Log.d("xin.wang", "enableFilter: previousFilter -> " + previousFilter + "<- index -> " + index);
mManageApplications.mFilterSpinner.setSelection(index);
mManageApplications.onItemSelected(null, null, index, 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 + " " + mContext.getText(
filter.getTitle()));
}
Collections.sort(mFilterOptions);
updateFilterView(mFilterOptions.size() > 1);
notifyDataSetChanged();
if (mManageApplications.mFilter == filter) {
if (mFilterOptions.size() > 0) {
if (DEBUG) {
Log.d(TAG, "Auto selecting filter " + mFilterOptions.get(0)
+ mContext.getText(mFilterOptions.get(0).getTitle()));
}
android.util.Log.d("xin.wang", "disableFilter: Auto selecting filter -> " + mContext.getText(mFilterOptions.get(0).getTitle()));
mManageApplications.mFilterSpinner.setSelection(0);
mManageApplications.onItemSelected(null, null, 0, 0);
}
}
}
@Override
public int getCount() {
android.util.Log.d("xin.wang", "getCount: mFilterOptions.size()->" + mFilterOptions.size());
return mFilterOptions.size();
}
@Override
public CharSequence getItem(int position) {
return mContext.getText(mFilterOptions.get(position).getTitle());
}
@VisibleForTesting
void updateFilterView(boolean hasFilter) {
// If we need to add a floating filter in this screen, we should have an extra top
// padding for putting floating filter view. Otherwise, the content of list will be
// overlapped by floating filter.
if (hasFilter) {
mManageApplications.mSpinnerHeader.setVisibility(View.VISIBLE);
} else {
mManageApplications.mSpinnerHeader.setVisibility(View.GONE);
}
}
}
static class ApplicationsAdapter extends RecyclerView.Adapter
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 final IconDrawableFactory mIconDrawableFactory;
private AppFilterItem mAppFilter;
private ArrayList mEntries;
private ArrayList mOriginalEntries;
private boolean mResumed;
private int mLastSortMode = -1;
private int mWhichSize = SIZE_TOTAL;
private AppFilter mCompositeFilter;
private boolean mHasReceivedLoadEntries;
private boolean mHasReceivedBridgeCallback;
private SearchFilter mSearchFilter;
private PowerAllowlistBackend mBackend;
private HashSet mSupportAppClone;
// 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.mRecyclerView,
mManageApplications.mEmptyView
);
mContext = manageApplications.getActivity();
mIconDrawableFactory = IconDrawableFactory.newInstance(mContext);
mAppFilter = appFilter;
mBackend = PowerAllowlistBackend.getInstance(mContext);
String[] supportApps = mContext.getResources().getStringArray(com.unisoc.internal.R.array.support_app_clone);
mSupportAppClone = new HashSet(Arrays.asList(supportApps));
android.util.Log.d("xin.wang", "ApplicationsAdapter -> mSupportAppClone -> : " + mSupportAppClone);
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) {
mBackend.refreshList();
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_WIFI_ACCESS) {
mExtraInfoBridge = new AppStateChangeWifiStateBridge(mContext, mState, this);
} else if (mManageApplications.mListType == LIST_MANAGE_EXTERNAL_STORAGE) {
mExtraInfoBridge = new AppStateManageExternalStorageBridge(mContext, mState, this);
} else if (mManageApplications.mListType == LIST_TYPE_ALARMS_AND_REMINDERS) {
mExtraInfoBridge = new AppStateAlarmsAndRemindersBridge(mContext, mState, this);
} else if (mManageApplications.mListType == LIST_TYPE_MEDIA_MANAGEMENT_APPS) {
mExtraInfoBridge = new AppStateMediaManagementAppsBridge(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 (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) {
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 if (FILTER_APPS_BLOCKED == appFilter.getFilterType()) {
rebuild(R.id.sort_order_alpha);
} else {
rebuild(R.id.sort_order_alpha);
}
} else {
rebuild();
}
}
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) {
final 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);
}
@Override
public int getItemViewType(int position) {
return VIEW_TYPE_APP;
}
public void rebuild() {
if (!mHasReceivedLoadEntries
|| (mExtraInfoBridge != null && !mHasReceivedBridgeCallback)) {
// Don't rebuild the list until all the app entries are loaded.
if (DEBUG) {
Log.d(TAG, "Not rebuilding until all the app entries loaded."
+ " !mHasReceivedLoadEntries=" + !mHasReceivedLoadEntries
+ " !mExtraInfoBridgeNull=" + (mExtraInfoBridge != null)
+ " !mHasReceivedBridgeCallback=" + !mHasReceivedBridgeCallback);
}
return;
}
ApplicationsState.AppFilter filterObj;
Comparator 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);
}
}
if (mLastSortMode == 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;
}
} else if (mLastSortMode == R.id.sort_order_recent_notification) {
comparatorObj = AppStateNotificationBridge.RECENT_NOTIFICATION_COMPARATOR;
} else if (mLastSortMode == R.id.sort_order_frequent_notification) {
comparatorObj = AppStateNotificationBridge.FREQUENCY_NOTIFICATION_COMPARATOR;
} else {
comparatorObj = ApplicationsState.ALPHA_COMPARATOR;
}
final AppFilter finalFilterObj = new CompoundFilter(filterObj,
ApplicationsState.FILTER_NOT_HIDE);
ThreadUtils.postOnBackgroundThread(() -> {
mSession.rebuild(finalFilterObj, comparatorObj, false);
});
}
@VisibleForTesting
void filterSearch(String query) {
if (mSearchFilter == null) {
mSearchFilter = new SearchFilter();
}
// If we haven't load apps list completely, don't filter anything.
if (mOriginalEntries == null) {
Log.w(TAG, "Apps haven't loaded completely yet, so nothing can be filtered");
return;
}
mSearchFilter.filter(query);
}
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 removeDuplicateIgnoringUser(
ArrayList entries) {
int size = entries.size();
// returnList will not have more entries than entries
ArrayList 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 entries) {
//android.util.Log.d("xin.wang", "onRebuildComplete:-> entries-> " + entries);
android.util.Log.d("xin.wang", "哈哈哈onRebuildComplete:-> entries-> " + entries.size());
if (DEBUG) {
Log.d(TAG, "onRebuildComplete size=" + entries.size());
}
final int filterType = mAppFilter.getFilterType();
if (filterType == FILTER_APPS_POWER_ALLOWLIST
|| filterType == FILTER_APPS_POWER_ALLOWLIST_ALL) {
entries = removeDuplicateIgnoringUser(entries);
android.util.Log.d("xin.wang", "onRebuildComplete: entries -> " + entries.size());
}
//only show the cloned third-party apps in the interface of work
if (mManageApplications.mIsWorkOnly && !mManageApplications.mIsPersonalOnly) {
ArrayList cloneEntries = new ArrayList();
int cloneNum = 0;
for (ApplicationsState.AppEntry entry : entries) {
cloneNum ++;
android.util.Log.d("xin.wang", "哈哈哈onRebuildComplete: cloneNum -> " + cloneNum);
int userId = UserHandle.getUserId(entry.info.uid);
UserInfo userInfo = mManageApplications.mUserManager.getUserInfo(userId);
boolean isManagedProfile = userInfo.isManagedProfile();
if (isManagedProfile) {
cloneEntries.add(entry);
android.util.Log.d("xin.wang", "isManagedProfile: -> cloneEntries " + cloneEntries + "<- cloneEntries.size() -> " + cloneEntries.size());
continue;
}
boolean isCloneProfile = userInfo.isCloneProfile();
boolean contains = mSupportAppClone.contains(entry.info.packageName);
android.util.Log.d("xin.wang", "onRebuildComplete: isCloneProfile -> " + isCloneProfile + "<- contains -> " + contains);
if (mSupportAppClone.contains(entry.info.packageName) && isCloneProfile) {
android.util.Log.d("xin.wang", "onRebuildComplete:mSupportAppClone-> " + mSupportAppClone + "<- entry.info.packageName->" + entry.info.packageName);
cloneEntries.add(entry);
android.util.Log.d("xin.wang", "mSupportAppClone.contains(entry.info.packageName): -> cloneEntries " + cloneEntries + "<- cloneEntries.size() -> " + cloneEntries.size());
}
}
mEntries = cloneEntries;
android.util.Log.d("xin.wang", "onRebuildComplete:cloneEntries -> " + cloneEntries + "<- cloneEntries.size() -> " + cloneEntries.size());
mOriginalEntries = cloneEntries;
} else {
mEntries = entries;
android.util.Log.d("xin.wang", "哈哈哈onRebuildComplete: mEntries -> " + mEntries.size());
mOriginalEntries = entries;
}
notifyDataSetChanged();
if (getItemCount() == 0) {
mLoadingViewController.showEmpty(false /* animate */);
} else {
mLoadingViewController.showContent(false /* animate */);
if (mManageApplications.mSearchView != null
&& mManageApplications.mSearchView.isVisibleToUser()) {
final CharSequence query = mManageApplications.mSearchView.getQuery();
if (!TextUtils.isEmpty(query)) {
filterSearch(query.toString());
}
}
}
// 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 (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);
android.util.Log.d("xin.wang", "onPackageSizeChanged: -> entry -> " + entry.info.packageName + "<- size -> " + size + "<- mEntries -> " + mEntries);
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();
}
}
@Override
public int getItemCount() {
if (mEntries == null) {
return 0;
}
android.util.Log.d("xin.wang", "getItemCount: " + mEntries.size());
return mEntries.size();
}
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 !mBackend.isSysAllowlisted(entry.info.packageName)
&& !mBackend.isDefaultActiveApp(entry.info.packageName);
}
@Override
public void onBindViewHolder(ApplicationViewHolder holder, int position) {
// Bind the data efficiently with the holder
final ApplicationsState.AppEntry entry = mEntries.get(position);
android.util.Log.d("xin.wang", "onBindViewHolder: ApplicationsState.AppEntry -> " + entry + "<- entry -> ");
synchronized (entry) {
mState.ensureLabelDescription(entry);
holder.setTitle(entry.label, entry.labelDescription);
android.util.Log.d("xin.wang", "onBindViewHolder -> entry.label -> : " + entry.label + "<- entry.labelDescription -> " + entry.labelDescription);
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
&& entry.extraInfo instanceof NotificationsSentState) {
holder.setSummary(AppStateNotificationBridge.getSummary(mContext,
(NotificationsSentState) entry.extraInfo, mLastSortMode));
} else {
holder.setSummary(null);
}
break;
case LIST_TYPE_USAGE_ACCESS:
if (entry.extraInfo != null
&& entry.extraInfo instanceof PermissionState) {
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_WIFI_ACCESS:
holder.setSummary(ChangeWifiStateDetails.getSummary(mContext, entry));
break;
case LIST_MANAGE_EXTERNAL_STORAGE:
holder.setSummary(ManageExternalStorageDetails.getSummary(mContext, entry));
break;
case LIST_TYPE_ALARMS_AND_REMINDERS:
holder.setSummary(AlarmsAndRemindersDetails.getSummary(mContext, entry));
break;
case LIST_TYPE_MEDIA_MANAGEMENT_APPS:
holder.setSummary(MediaManagementAppsDetails.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)
.getSwitchOnCheckedListener(entry),
AppStateNotificationBridge.enableSwitch(entry),
AppStateNotificationBridge.checkSwitch(entry));
if (entry.extraInfo != null
&& entry.extraInfo instanceof NotificationsSentState) {
holder.setSummary(AppStateNotificationBridge.getSummary(mContext,
(NotificationsSentState) entry.extraInfo, mLastSortMode));
} else {
holder.setSummary(null);
}
break;
}
}
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;
}
}
}
/**
* An array filter that constrains the content of the array adapter with a substring.
* Item that does not contains the specified substring will be removed from the list.
*/
private class SearchFilter extends Filter {
@WorkerThread
@Override
protected FilterResults performFiltering(CharSequence query) {
final ArrayList matchedEntries;
if (TextUtils.isEmpty(query)) {
matchedEntries = mOriginalEntries;
} else {
matchedEntries = new ArrayList<>();
for (ApplicationsState.AppEntry entry : mOriginalEntries) {
if (entry.label.toLowerCase().contains(query.toString().toLowerCase())) {
matchedEntries.add(entry);
}
}
}
final FilterResults results = new FilterResults();
results.values = matchedEntries;
results.count = matchedEntries.size();
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
mEntries = (ArrayList) results.values;
notifyDataSetChanged();
}
}
}
}
/*
* 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_BLOCKED;
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_ALLOWLIST;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_POWER_ALLOWLIST_ALL;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_RECENT;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_WORK;
import static com.android.settings.search.actionbar.SearchMenuController.MENU_SEARCH;
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.settings.SettingsEnums;
import android.app.usage.IUsageStatsManager;
import android.compat.annotation.ChangeId;
import android.compat.annotation.LoggingOnly;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.preference.PreferenceFrameLayout;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.IconDrawableFactory;
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.Filter;
import android.widget.FrameLayout;
import android.widget.SearchView;
import android.widget.Spinner;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.compat.IPlatformCompat;
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.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.Utils;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.applications.AppStateAlarmsAndRemindersBridge;
import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
import com.android.settings.applications.AppStateBaseBridge;
import com.android.settings.applications.AppStateInstallAppsBridge;
import com.android.settings.applications.AppStateManageExternalStorageBridge;
import com.android.settings.applications.AppStateMediaManagementAppsBridge;
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.UsageAccessDetails;
import com.android.settings.applications.appinfo.AlarmsAndRemindersDetails;
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.ManageExternalStorageDetails;
import com.android.settings.applications.appinfo.MediaManagementAppsDetails;
import com.android.settings.applications.appinfo.WriteSettingsDetails;
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
import com.android.settings.fuelgauge.HighPowerDetail;
import com.android.settings.notification.ConfigureNotificationSettings;
import com.android.settings.notification.NotificationBackend;
import com.android.settings.notification.app.AppNotificationSettings;
import com.android.settings.widget.LoadingViewController;
import com.android.settings.wifi.AppStateChangeWifiStateBridge;
import com.android.settings.wifi.ChangeWifiStateDetails;
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.AppUtils;
import com.android.settingslib.fuelgauge.PowerAllowlistBackend;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter;
import com.google.android.material.appbar.AppBarLayout;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
/**
* 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, SearchView.OnQueryTextListener,
MenuItem.OnActionExpandListener {
static final String TAG = "ManageApplications";
static final boolean DEBUG = Build.IS_DEBUGGABLE;
// 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_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";
private static final String EXTRA_FILTER_TYPE = "filterType";
@VisibleForTesting
static final String EXTRA_EXPAND_SEARCH_VIEW = "expand_search_view";
// 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_LEGACY = 1; // Show apps even if they can be categorized.
/**
* Intents with action {@code android.settings.MANAGE_APP_OVERLAY_PERMISSION}
* and data URI scheme {@code package} don't go to the app-specific screen for managing the
* permission anymore. Instead, they redirect to this screen for managing all the apps that have
* requested such permission.
*/
@ChangeId
@LoggingOnly
private static final long CHANGE_RESTRICT_SAW_INTENT = 135920175L;
// 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 SearchView mSearchView;
// 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_WIFI_ACCESS = 10;
public static final int LIST_MANAGE_EXTERNAL_STORAGE = 11;
public static final int LIST_TYPE_ALARMS_AND_REMINDERS = 12;
public static final int LIST_TYPE_MEDIA_MANAGEMENT_APPS = 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));
@VisibleForTesting
View mSpinnerHeader;
@VisibleForTesting
FilterSpinnerAdapter mFilterAdapter;
@VisibleForTesting
RecyclerView mRecyclerView;
// Whether or not search view is expanded.
@VisibleForTesting
boolean mExpandSearch;
private View mRootView;
private Spinner mFilterSpinner;
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 boolean mIsPersonalOnly;
private View mEmptyView;
private int mFilterType;
private AppBarLayout mAppBarLayout;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
final Activity activity = getActivity();
mUserManager = activity.getSystemService(UserManager.class);
mApplicationsState = ApplicationsState.getInstance(activity.getApplication());
Intent intent = activity.getIntent();
Bundle args = getArguments();
int screenTitle = intent.getIntExtra(
SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, R.string.all_apps);
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;
reportIfRestrictedSawIntent(intent);
} 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(Settings.ChangeWifiStateActivity.class.getName())) {
mListType = LIST_TYPE_WIFI_ACCESS;
screenTitle = R.string.change_wifi_state_title;
} else if (className.equals(Settings.ManageExternalStorageActivity.class.getName())) {
mListType = LIST_MANAGE_EXTERNAL_STORAGE;
screenTitle = R.string.manage_external_storage_title;
} else if (className.equals(Settings.MediaManagementAppsActivity.class.getName())) {
mListType = LIST_TYPE_MEDIA_MANAGEMENT_APPS;
screenTitle = R.string.media_management_apps_title;
} else if (className.equals(Settings.AlarmsAndRemindersActivity.class.getName())) {
mListType = LIST_TYPE_ALARMS_AND_REMINDERS;
screenTitle = R.string.alarms_and_reminders_title;
} else if (className.equals(Settings.NotificationAppListActivity.class.getName())) {
mListType = LIST_TYPE_NOTIFICATION;
mUsageStatsManager = IUsageStatsManager.Stub.asInterface(
ServiceManager.getService(Context.USAGE_STATS_SERVICE));
mNotificationBackend = new NotificationBackend();
mSortOrder = R.id.sort_order_recent_notification;
screenTitle = R.string.app_notifications_title;
} else {
if (screenTitle == -1) {
screenTitle = R.string.all_apps;
}
mListType = LIST_TYPE_MAIN;
}
final AppFilterRegistry appFilterRegistry = AppFilterRegistry.getInstance();
mFilter = appFilterRegistry.get(appFilterRegistry.getDefaultFilterType(mListType));
mIsPersonalOnly = args != null ? args.getInt(ProfileSelectFragment.EXTRA_PROFILE)
== ProfileSelectFragment.ProfileType.PERSONAL : false;
mIsWorkOnly = args != null ? args.getInt(ProfileSelectFragment.EXTRA_PROFILE)
== ProfileSelectFragment.ProfileType.WORK : false;
mWorkUserId = args != null ? args.getInt(EXTRA_WORK_ID) : UserHandle.myUserId();
if (mIsWorkOnly && mWorkUserId == UserHandle.myUserId()) {
mWorkUserId = Utils.getManagedProfileId(mUserManager, UserHandle.myUserId());
}
mExpandSearch = activity.getIntent().getBooleanExtra(EXTRA_EXPAND_SEARCH_VIEW, false);
if (savedInstanceState != null) {
mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder);
mShowSystem = savedInstanceState.getBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
mFilterType =
savedInstanceState.getInt(EXTRA_FILTER_TYPE, AppFilterRegistry.FILTER_APPS_ALL);
mExpandSearch = savedInstanceState.getBoolean(EXTRA_EXPAND_SEARCH_VIEW);
}
mInvalidSizeStr = activity.getText(R.string.invalid_size_value);
mResetAppsHelper = new ResetAppsHelper(activity);
if (screenTitle > 0) {
activity.setTitle(screenTitle);
}
}
private void reportIfRestrictedSawIntent(Intent intent) {
try {
Uri data = intent.getData();
if (data == null || !TextUtils.equals("package", data.getScheme())) {
// Not a restricted intent
return;
}
IBinder activityToken = getActivity().getActivityToken();
int callingUid = ActivityManager.getService().getLaunchedFromUid(activityToken);
if (callingUid == -1) {
Log.w(TAG, "Error obtaining calling uid");
return;
}
IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
if (platformCompat == null) {
Log.w(TAG, "Error obtaining IPlatformCompat service");
return;
}
platformCompat.reportChangeByUid(CHANGE_RESTRICT_SAW_INTENT, callingUid);
} catch (RemoteException e) {
Log.w(TAG, "Error reporting SAW intent restriction", e);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (mListType == LIST_TYPE_OVERLAY && !Utils.isSystemAlertWindowEnabled(getContext())) {
mRootView = inflater.inflate(R.layout.manage_applications_apps_unsupported, null);
setHasOptionsMenu(false);
return mRootView;
}
mRootView = inflater.inflate(R.layout.manage_applications_apps, null);
mLoadingContainer = mRootView.findViewById(R.id.loading_container);
mEmptyView = mRootView.findViewById(android.R.id.empty);
mRecyclerView = mRootView.findViewById(R.id.apps_list);
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);
}
mRecyclerView.setItemAnimator(null);
mRecyclerView.setLayoutManager(new LinearLayoutManager(
getContext(), RecyclerView.VERTICAL, false /* reverseLayout */));
mRecyclerView.setAdapter(mApplications);
android.util.Log.d("xin.wang", "onCreateView:mRecyclerView.setAdapter-> " + Thread.currentThread().getName());
// 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);
mAppBarLayout = getActivity().findViewById(R.id.app_bar);
disableToolBarScrollableBehavior();
return mRootView;
}
@VisibleForTesting
void createHeader() {
final Activity activity = getActivity();
final FrameLayout pinnedHeader = mRootView.findViewById(R.id.pinned_header);
mSpinnerHeader = activity.getLayoutInflater()
.inflate(R.layout.manage_apps_filter_spinner, pinnedHeader, false);
mFilterSpinner = mSpinnerHeader.findViewById(R.id.filter_spinner);
mFilterAdapter = new FilterSpinnerAdapter(this);
mFilterSpinner.setAdapter(mFilterAdapter);
mFilterSpinner.setOnItemSelectedListener(this);
mFilterSpinner.setVisibility(View.GONE);
pinnedHeader.addView(mSpinnerHeader, 0);
final AppFilterRegistry appFilterRegistry = AppFilterRegistry.getInstance();
final int filterType = appFilterRegistry.getDefaultFilterType(mListType);
android.util.Log.d("xin.wang", "createHeader:-> filterType -> " + mListType + "<- filterType -> " + filterType); //filterType = 4 -> FILTER_APPS_ALL
android.util.Log.d("xin.wang", "createHeader:->mFilterOptions.size()-> " + mFilterAdapter.getCount());
//mFilterAdapter.enableFilter(filterType); //filterType = 4 -> FILTER_APPS_ALL
if (mListType == LIST_TYPE_MAIN) {
if (UserManager.get(getActivity()).getUserProfiles().size() > 1 && !mIsWorkOnly
&& !mIsPersonalOnly) {
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.enableFilter(FILTER_APPS_BLOCKED);
mFilterAdapter.enableFilter(FILTER_APPS_ALL);
}
if (mListType == LIST_TYPE_HIGH_POWER) {
mFilterAdapter.enableFilter(FILTER_APPS_POWER_ALLOWLIST_ALL);
}
setCompositeFilter();
}
@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_DEFAULT) {
filter = new CompoundFilter(ApplicationsState.FILTER_APPS_EXCEPT_GAMES, filter);
}
return filter;
}
if (listType == LIST_TYPE_GAMES) {
return new CompoundFilter(ApplicationsState.FILTER_GAMES, filter);
}
return null;
}
@Override
public int getMetricsCategory() {
switch (mListType) {
case LIST_TYPE_MAIN:
return SettingsEnums.MANAGE_APPLICATIONS;
case LIST_TYPE_NOTIFICATION:
return SettingsEnums.MANAGE_APPLICATIONS_NOTIFICATIONS;
case LIST_TYPE_STORAGE:
return SettingsEnums.APPLICATIONS_STORAGE_APPS;
case LIST_TYPE_GAMES:
return SettingsEnums.APPLICATIONS_STORAGE_GAMES;
case LIST_TYPE_USAGE_ACCESS:
return SettingsEnums.USAGE_ACCESS;
case LIST_TYPE_HIGH_POWER:
return SettingsEnums.APPLICATIONS_HIGH_POWER_APPS;
case LIST_TYPE_OVERLAY:
return SettingsEnums.SYSTEM_ALERT_WINDOW_APPS;
case LIST_TYPE_WRITE_SETTINGS:
return SettingsEnums.SYSTEM_ALERT_WINDOW_APPS;
case LIST_TYPE_MANAGE_SOURCES:
return SettingsEnums.MANAGE_EXTERNAL_SOURCES;
case LIST_TYPE_WIFI_ACCESS:
return SettingsEnums.CONFIGURE_WIFI;
case LIST_MANAGE_EXTERNAL_STORAGE:
return SettingsEnums.MANAGE_EXTERNAL_STORAGE;
case LIST_TYPE_ALARMS_AND_REMINDERS:
return SettingsEnums.ALARMS_AND_REMINDERS;
case LIST_TYPE_MEDIA_MANAGEMENT_APPS:
return SettingsEnums.MEDIA_MANAGEMENT_APPS;
default:
return SettingsEnums.PAGE_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.putInt(EXTRA_FILTER_TYPE, mFilter.getFilterType());
outState.putBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
//add bug 1702049:NullPointerException on a null object reference
if (mApplications != null) {
outState.putBoolean(EXTRA_HAS_ENTRIES, mApplications.mHasReceivedLoadEntries);
outState.putBoolean(EXTRA_HAS_BRIDGE, mApplications.mHasReceivedBridgeCallback);
if (mSearchView != null) {
outState.putBoolean(EXTRA_EXPAND_SEARCH_VIEW, !mSearchView.isIconified());
}
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));
}
}
}
private void setCompositeFilter() {
AppFilter compositeFilter = getCompositeFilter(mListType, mStorageType, mVolumeUuid);
if (compositeFilter == null) {
compositeFilter = mFilter.getFilter();
}
if (mIsWorkOnly) {
compositeFilter = new CompoundFilter(compositeFilter, ApplicationsState.FILTER_WORK);
}
if (mIsPersonalOnly) {
compositeFilter = new CompoundFilter(compositeFilter,
ApplicationsState.FILTER_PERSONAL);
}
mApplications.setCompositeFilter(compositeFilter);
}
// 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_WIFI_ACCESS:
startAppInfoFragment(ChangeWifiStateDetails.class,
R.string.change_wifi_state_title);
break;
case LIST_MANAGE_EXTERNAL_STORAGE:
startAppInfoFragment(ManageExternalStorageDetails.class,
R.string.manage_external_storage_title);
break;
case LIST_TYPE_ALARMS_AND_REMINDERS:
startAppInfoFragment(AlarmsAndRemindersDetails.class,
R.string.alarms_and_reminders_label);
break;
case LIST_TYPE_MEDIA_MANAGEMENT_APPS:
startAppInfoFragment(MediaManagementAppsDetails.class,
R.string.media_management_apps_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;
}
mOptionsMenu = menu;
inflater.inflate(R.menu.manage_apps, menu);
final MenuItem searchMenuItem = menu.findItem(R.id.search_app_list_menu);
if (searchMenuItem != null) {
searchMenuItem.setOnActionExpandListener(this);
mSearchView = (SearchView) searchMenuItem.getActionView();
mSearchView.setQueryHint(getText(R.string.search_settings));
mSearchView.setOnQueryTextListener(this);
if (mExpandSearch) {
searchMenuItem.expandActionView();
}
}
updateOptionsMenu();
}
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
// To prevent a large space on tool bar.
mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/);
// To prevent user can expand the collapsing tool bar view.
ViewCompat.setNestedScrollingEnabled(mRecyclerView, false);
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
// We keep the collapsed status after user cancel the search function.
mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/);
ViewCompat.setNestedScrollingEnabled(mRecyclerView, true);
return true;
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
updateOptionsMenu();
}
@Override
public void onDestroyOptionsMenu() {
mOptionsMenu = null;
}
@StringRes
int getHelpResource() {
switch (mListType) {
case LIST_TYPE_NOTIFICATION:
return R.string.help_uri_notifications;
case LIST_TYPE_USAGE_ACCESS:
return R.string.help_url_usage_access;
case LIST_TYPE_STORAGE:
return R.string.help_uri_apps_storage;
case LIST_TYPE_HIGH_POWER:
return R.string.help_uri_apps_high_power;
case LIST_TYPE_OVERLAY:
return R.string.help_uri_apps_overlay;
case LIST_TYPE_WRITE_SETTINGS:
return R.string.help_uri_apps_write_settings;
case LIST_TYPE_MANAGE_SOURCES:
return R.string.help_uri_apps_manage_sources;
case LIST_TYPE_GAMES:
return R.string.help_uri_apps_overlay;
case LIST_TYPE_WIFI_ACCESS:
return R.string.help_uri_apps_wifi_access;
case LIST_MANAGE_EXTERNAL_STORAGE:
return R.string.help_uri_manage_external_storage;
case LIST_TYPE_ALARMS_AND_REMINDERS:
return R.string.help_uri_alarms_and_reminders;
case LIST_TYPE_MEDIA_MANAGEMENT_APPS:
return R.string.help_uri_media_management_apps;
default:
case LIST_TYPE_MAIN:
return R.string.help_uri_apps;
}
}
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 && wheatherShowSystemOption());
mOptionsMenu.findItem(R.id.hide_system).setVisible(mShowSystem
&& mListType != LIST_TYPE_HIGH_POWER && wheatherShowSystemOption());
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);
final MenuItem searchItem = mOptionsMenu.findItem(MENU_SEARCH);
if (searchItem != null) {
searchItem.setVisible(false);
}
}
//hide the optionsmenu of hide-system and show-system in the work interface
private boolean wheatherShowSystemOption() {
boolean hasManageProfile = false;
List<UserInfo> users = mUserManager.getUsers();
android.util.Log.d("xin.wang", "wheatherShowSystemOption: " + users + "<- users.size->" + users.size());
for(UserInfo user : users){
if (user.isManagedProfile()) {
hasManageProfile = true;
break;
}
}
if (mIsWorkOnly && !mIsPersonalOnly) {
if (!hasManageProfile) {
return false;
}
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int menuId = item.getItemId();
int i = item.getItemId();
if (i == R.id.sort_order_alpha || i == R.id.sort_order_size) {
if (mApplications != null) {
mApplications.rebuild(menuId);
}
} else if (i == R.id.show_system || i == R.id.hide_system) {
mShowSystem = !mShowSystem;
mApplications.rebuild();
} else if (i == R.id.reset_app_preferences) {
mResetAppsHelper.buildResetDialog();
return true;
} else if (i == R.id.advanced) {
if (mListType == LIST_TYPE_NOTIFICATION) {
new SubSettingLauncher(getContext())
.setDestination(ConfigureNotificationSettings.class.getName())
.setTitleRes(R.string.configure_notification_settings)
.setSourceMetricsCategory(getMetricsCategory())
.setResultListener(this, ADVANCED_SETTINGS)
.launch();
} else {
Intent intent = new Intent(
android.provider.Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS);
startActivityForResult(intent, ADVANCED_SETTINGS);
}
return true;
} else {// 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();
// We disable the scrolling ability in onMenuItemActionCollapse, we should recover it
// if user selects any app item.
ViewCompat.setNestedScrollingEnabled(mRecyclerView, true);
}
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
mFilter = mFilterAdapter.getFilter(position);
setCompositeFilter();
mApplications.setFilter(mFilter);
if (DEBUG) {
Log.d(TAG, "Selecting filter " + getContext().getText(mFilter.getTitle()));
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
mApplications.filterSearch(newText);
return false;
}
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);
}
}
private void disableToolBarScrollableBehavior() {
final CoordinatorLayout.LayoutParams params =
(CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams();
final AppBarLayout.Behavior behavior = new AppBarLayout.Behavior();
behavior.setDragCallback(
new AppBarLayout.Behavior.DragCallback() {
@Override
public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
return false;
}
});
params.setBehavior(behavior);
}
static class FilterSpinnerAdapter extends SettingsSpinnerAdapter<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());
mContext = manageApplications.getContext();
mManageApplications = manageApplications;
}
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 " + mContext.getText(filter.getTitle()));
}
mFilterOptions.add(filter);
Collections.sort(mFilterOptions);
updateFilterView(mFilterOptions.size() > 1);
notifyDataSetChanged();
if (mFilterOptions.size() == 1) {
if (DEBUG) {
Log.d(TAG, "Auto selecting filter " + filter + " " + mContext.getText(
filter.getTitle()));
}
mManageApplications.mFilterSpinner.setSelection(0);
mManageApplications.onItemSelected(null, null, 0, 0);
}
if (mFilterOptions.size() > 1) {
final AppFilterItem previousFilter = AppFilterRegistry.getInstance().get(
mManageApplications.mFilterType);
final int index = mFilterOptions.indexOf(previousFilter);
if (index != -1) {
android.util.Log.d("xin.wang", "enableFilter: previousFilter -> " + previousFilter + "<- index -> " + index);
mManageApplications.mFilterSpinner.setSelection(index);
mManageApplications.onItemSelected(null, null, index, 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 + " " + mContext.getText(
filter.getTitle()));
}
Collections.sort(mFilterOptions);
updateFilterView(mFilterOptions.size() > 1);
notifyDataSetChanged();
if (mManageApplications.mFilter == filter) {
if (mFilterOptions.size() > 0) {
if (DEBUG) {
Log.d(TAG, "Auto selecting filter " + mFilterOptions.get(0)
+ mContext.getText(mFilterOptions.get(0).getTitle()));
}
android.util.Log.d("xin.wang", "disableFilter: Auto selecting filter -> " + mContext.getText(mFilterOptions.get(0).getTitle()));
mManageApplications.mFilterSpinner.setSelection(0);
mManageApplications.onItemSelected(null, null, 0, 0);
}
}
}
@Override
public int getCount() {
android.util.Log.d("xin.wang", "getCount: mFilterOptions.size()->" + mFilterOptions.size());
return mFilterOptions.size();
}
@Override
public CharSequence getItem(int position) {
return mContext.getText(mFilterOptions.get(position).getTitle());
}
@VisibleForTesting
void updateFilterView(boolean hasFilter) {
// If we need to add a floating filter in this screen, we should have an extra top
// padding for putting floating filter view. Otherwise, the content of list will be
// overlapped by floating filter.
if (hasFilter) {
mManageApplications.mSpinnerHeader.setVisibility(View.VISIBLE);
} else {
mManageApplications.mSpinnerHeader.setVisibility(View.GONE);
}
}
}
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 final IconDrawableFactory mIconDrawableFactory;
private AppFilterItem mAppFilter;
private ArrayList<ApplicationsState.AppEntry> mEntries;
private ArrayList<ApplicationsState.AppEntry> mOriginalEntries;
private boolean mResumed;
private int mLastSortMode = -1;
private int mWhichSize = SIZE_TOTAL;
private AppFilter mCompositeFilter;
private boolean mHasReceivedLoadEntries;
private boolean mHasReceivedBridgeCallback;
private SearchFilter mSearchFilter;
private PowerAllowlistBackend mBackend;
private HashSet<String> mSupportAppClone;
// 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.mRecyclerView,
mManageApplications.mEmptyView
);
mContext = manageApplications.getActivity();
mIconDrawableFactory = IconDrawableFactory.newInstance(mContext);
mAppFilter = appFilter;
mBackend = PowerAllowlistBackend.getInstance(mContext);
String[] supportApps = mContext.getResources().getStringArray(com.unisoc.internal.R.array.support_app_clone);
mSupportAppClone = new HashSet<String>(Arrays.asList(supportApps));
android.util.Log.d("xin.wang", "ApplicationsAdapter -> mSupportAppClone -> : " + mSupportAppClone);
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) {
mBackend.refreshList();
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_WIFI_ACCESS) {
mExtraInfoBridge = new AppStateChangeWifiStateBridge(mContext, mState, this);
} else if (mManageApplications.mListType == LIST_MANAGE_EXTERNAL_STORAGE) {
mExtraInfoBridge = new AppStateManageExternalStorageBridge(mContext, mState, this);
} else if (mManageApplications.mListType == LIST_TYPE_ALARMS_AND_REMINDERS) {
mExtraInfoBridge = new AppStateAlarmsAndRemindersBridge(mContext, mState, this);
} else if (mManageApplications.mListType == LIST_TYPE_MEDIA_MANAGEMENT_APPS) {
mExtraInfoBridge = new AppStateMediaManagementAppsBridge(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 (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) {
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 if (FILTER_APPS_BLOCKED == appFilter.getFilterType()) {
rebuild(R.id.sort_order_alpha);
} else {
rebuild(R.id.sort_order_alpha);
}
} else {
rebuild();
}
}
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) {
final 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);
}
@Override
public int getItemViewType(int position) {
return VIEW_TYPE_APP;
}
public void rebuild() {
if (!mHasReceivedLoadEntries
|| (mExtraInfoBridge != null && !mHasReceivedBridgeCallback)) {
// Don't rebuild the list until all the app entries are loaded.
if (DEBUG) {
Log.d(TAG, "Not rebuilding until all the app entries loaded."
+ " !mHasReceivedLoadEntries=" + !mHasReceivedLoadEntries
+ " !mExtraInfoBridgeNull=" + (mExtraInfoBridge != null)
+ " !mHasReceivedBridgeCallback=" + !mHasReceivedBridgeCallback);
}
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);
}
}
if (mLastSortMode == 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;
}
} else if (mLastSortMode == R.id.sort_order_recent_notification) {
comparatorObj = AppStateNotificationBridge.RECENT_NOTIFICATION_COMPARATOR;
} else if (mLastSortMode == R.id.sort_order_frequent_notification) {
comparatorObj = AppStateNotificationBridge.FREQUENCY_NOTIFICATION_COMPARATOR;
} else {
comparatorObj = ApplicationsState.ALPHA_COMPARATOR;
}
final AppFilter finalFilterObj = new CompoundFilter(filterObj,
ApplicationsState.FILTER_NOT_HIDE);
ThreadUtils.postOnBackgroundThread(() -> {
mSession.rebuild(finalFilterObj, comparatorObj, false);
});
}
@VisibleForTesting
void filterSearch(String query) {
if (mSearchFilter == null) {
mSearchFilter = new SearchFilter();
}
// If we haven't load apps list completely, don't filter anything.
if (mOriginalEntries == null) {
Log.w(TAG, "Apps haven't loaded completely yet, so nothing can be filtered");
return;
}
mSearchFilter.filter(query);
}
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) {
//android.util.Log.d("xin.wang", "onRebuildComplete:-> entries-> " + entries);
android.util.Log.d("xin.wang", "哈哈哈onRebuildComplete:-> entries-> " + entries.size());
if (DEBUG) {
Log.d(TAG, "onRebuildComplete size=" + entries.size());
}
final int filterType = mAppFilter.getFilterType();
if (filterType == FILTER_APPS_POWER_ALLOWLIST
|| filterType == FILTER_APPS_POWER_ALLOWLIST_ALL) {
entries = removeDuplicateIgnoringUser(entries);
android.util.Log.d("xin.wang", "onRebuildComplete: entries -> " + entries.size());
}
//only show the cloned third-party apps in the interface of work
if (mManageApplications.mIsWorkOnly && !mManageApplications.mIsPersonalOnly) {
ArrayList<ApplicationsState.AppEntry> cloneEntries = new ArrayList();
int cloneNum = 0;
for (ApplicationsState.AppEntry entry : entries) {
cloneNum ++;
android.util.Log.d("xin.wang", "哈哈哈onRebuildComplete: cloneNum -> " + cloneNum);
int userId = UserHandle.getUserId(entry.info.uid);
UserInfo userInfo = mManageApplications.mUserManager.getUserInfo(userId);
boolean isManagedProfile = userInfo.isManagedProfile();
if (isManagedProfile) {
cloneEntries.add(entry);
android.util.Log.d("xin.wang", "isManagedProfile: -> cloneEntries " + cloneEntries + "<- cloneEntries.size() -> " + cloneEntries.size());
continue;
}
boolean isCloneProfile = userInfo.isCloneProfile();
boolean contains = mSupportAppClone.contains(entry.info.packageName);
android.util.Log.d("xin.wang", "onRebuildComplete: isCloneProfile -> " + isCloneProfile + "<- contains -> " + contains);
if (mSupportAppClone.contains(entry.info.packageName) && isCloneProfile) {
android.util.Log.d("xin.wang", "onRebuildComplete:mSupportAppClone-> " + mSupportAppClone + "<- entry.info.packageName->" + entry.info.packageName);
cloneEntries.add(entry);
android.util.Log.d("xin.wang", "mSupportAppClone.contains(entry.info.packageName): -> cloneEntries " + cloneEntries + "<- cloneEntries.size() -> " + cloneEntries.size());
}
}
mEntries = cloneEntries;
android.util.Log.d("xin.wang", "onRebuildComplete:cloneEntries -> " + cloneEntries + "<- cloneEntries.size() -> " + cloneEntries.size());
mOriginalEntries = cloneEntries;
} else {
mEntries = entries;
android.util.Log.d("xin.wang", "哈哈哈onRebuildComplete: mEntries -> " + mEntries.size());
mOriginalEntries = entries;
}
notifyDataSetChanged();
if (getItemCount() == 0) {
mLoadingViewController.showEmpty(false /* animate */);
} else {
mLoadingViewController.showContent(false /* animate */);
if (mManageApplications.mSearchView != null
&& mManageApplications.mSearchView.isVisibleToUser()) {
final CharSequence query = mManageApplications.mSearchView.getQuery();
if (!TextUtils.isEmpty(query)) {
filterSearch(query.toString());
}
}
}
// 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 (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);
android.util.Log.d("xin.wang", "onPackageSizeChanged: -> entry -> " + entry.info.packageName + "<- size -> " + size + "<- mEntries -> " + mEntries);
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();
}
}
@Override
public int getItemCount() {
if (mEntries == null) {
return 0;
}
android.util.Log.d("xin.wang", "getItemCount: " + mEntries.size());
return mEntries.size();
}
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 !mBackend.isSysAllowlisted(entry.info.packageName)
&& !mBackend.isDefaultActiveApp(entry.info.packageName);
}
@Override
public void onBindViewHolder(ApplicationViewHolder holder, int position) {
// Bind the data efficiently with the holder
final ApplicationsState.AppEntry entry = mEntries.get(position);
android.util.Log.d("xin.wang", "onBindViewHolder: ApplicationsState.AppEntry -> " + entry + "<- entry -> ");
synchronized (entry) {
mState.ensureLabelDescription(entry);
holder.setTitle(entry.label, entry.labelDescription);
android.util.Log.d("xin.wang", "onBindViewHolder -> entry.label -> : " + entry.label + "<- entry.labelDescription -> " + entry.labelDescription);
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
&& entry.extraInfo instanceof NotificationsSentState) {
holder.setSummary(AppStateNotificationBridge.getSummary(mContext,
(NotificationsSentState) entry.extraInfo, mLastSortMode));
} else {
holder.setSummary(null);
}
break;
case LIST_TYPE_USAGE_ACCESS:
if (entry.extraInfo != null
&& entry.extraInfo instanceof PermissionState) {
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_WIFI_ACCESS:
holder.setSummary(ChangeWifiStateDetails.getSummary(mContext, entry));
break;
case LIST_MANAGE_EXTERNAL_STORAGE:
holder.setSummary(ManageExternalStorageDetails.getSummary(mContext, entry));
break;
case LIST_TYPE_ALARMS_AND_REMINDERS:
holder.setSummary(AlarmsAndRemindersDetails.getSummary(mContext, entry));
break;
case LIST_TYPE_MEDIA_MANAGEMENT_APPS:
holder.setSummary(MediaManagementAppsDetails.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)
.getSwitchOnCheckedListener(entry),
AppStateNotificationBridge.enableSwitch(entry),
AppStateNotificationBridge.checkSwitch(entry));
if (entry.extraInfo != null
&& entry.extraInfo instanceof NotificationsSentState) {
holder.setSummary(AppStateNotificationBridge.getSummary(mContext,
(NotificationsSentState) entry.extraInfo, mLastSortMode));
} else {
holder.setSummary(null);
}
break;
}
}
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;
}
}
}
/**
* An array filter that constrains the content of the array adapter with a substring.
* Item that does not contains the specified substring will be removed from the list.
*/
private class SearchFilter extends Filter {
@WorkerThread
@Override
protected FilterResults performFiltering(CharSequence query) {
final ArrayList<ApplicationsState.AppEntry> matchedEntries;
if (TextUtils.isEmpty(query)) {
matchedEntries = mOriginalEntries;
} else {
matchedEntries = new ArrayList<>();
for (ApplicationsState.AppEntry entry : mOriginalEntries) {
if (entry.label.toLowerCase().contains(query.toString().toLowerCase())) {
matchedEntries.add(entry);
}
}
}
final FilterResults results = new FilterResults();
results.values = matchedEntries;
results.count = matchedEntries.size();
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
mEntries = (ArrayList<ApplicationsState.AppEntry>) results.values;
notifyDataSetChanged();
}
}
}
}

个人区域存放所有应用
工作区域显示双开的应用
ProfileSelectManageApplications/*
* Copyright (C) 2019 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.dashboard.profileselector;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import com.android.settings.applications.manageapplications.ManageApplications;
/**
* Application Setting page for personal/managed profile.
*/
public class ProfileSelectManageApplications extends ProfileSelectFragment {
@Override
public Fragment[] getFragments() {
final Bundle workOnly = getArguments() != null ? getArguments().deepCopy() : new Bundle();
workOnly.putInt(EXTRA_PROFILE, ProfileSelectFragment.ProfileType.WORK);
final Fragment workFragment = new ManageApplications();
workFragment.setArguments(workOnly);
final Bundle personalOnly = getArguments() != null ? getArguments() : new Bundle();
personalOnly.putInt(EXTRA_PROFILE, ProfileSelectFragment.ProfileType.PERSONAL);
final Fragment personalFragment = new ManageApplications();
personalFragment.setArguments(personalOnly);
return new Fragment[]{
personalFragment, //0
workFragment
};
}
}