Android12 设置搜索屏蔽某个App入口
1.前言:
之前在Android12Rom定制需求中做了屏蔽Settings某个App的入口,有网络和互联网、已连接的设备、电池和存储,但是在搜索的时候还是能看到入口,所以感觉体验不是很好,于是根据要求屏蔽这几个3App的搜索功能,这里记录一下修改过程。
2.核心修改点:
- StorageDashboardFragment
- ConnectedDeviceDashboardFragment
- NetworkDashboardFragment
- PowerUsageSummary
- 修改isEligibleForIndexing()方法
- 修改getInjectionIndexableRawData()方法
3.屏蔽存储:
源码路径:package/apps/settings/src/com/android/settings/deviceinfo/storage/StorageDashboardFragment
核心修改:
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.storage_dashboard_fragment) {
@Override
public boolean isPageSearchEnabled(Context context) {
return false; // 禁用搜索
}
};
/*
* 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.deviceinfo;
import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.app.usage.StorageStatsManager;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.provider.SearchIndexableResource;
import android.util.SparseArray;
import android.view.View;
import androidx.annotation.VisibleForTesting;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
import com.android.settings.deviceinfo.storage.AutomaticStorageManagementSwitchPreferenceController;
import com.android.settings.deviceinfo.storage.CachedStorageValuesHelper;
import com.android.settings.deviceinfo.storage.SecondaryUserController;
import com.android.settings.deviceinfo.storage.StorageAsyncLoader;
import com.android.settings.deviceinfo.storage.StorageItemPreferenceController;
import com.android.settings.deviceinfo.storage.UserIconLoader;
import com.android.settings.deviceinfo.storage.VolumeSizesLoader;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.applications.StorageStatsSource;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.deviceinfo.PrivateStorageInfo;
import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
import com.android.settingslib.search.SearchIndexable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@SearchIndexable
public class StorageDashboardFragment extends DashboardFragment
implements
LoaderManager.LoaderCallbacks<SparseArray<StorageAsyncLoader.AppsStorageResult>> {
private static final String TAG = "StorageDashboardFrag";
private static final String SUMMARY_PREF_KEY = "storage_summary";
private static final int STORAGE_JOB_ID = 0;
private static final int ICON_JOB_ID = 1;
private static final int VOLUME_SIZE_JOB_ID = 2;
private VolumeInfo mVolume;
private PrivateStorageInfo mStorageInfo;
private SparseArray<StorageAsyncLoader.AppsStorageResult> mAppsResult;
private CachedStorageValuesHelper mCachedStorageValuesHelper;
private StorageItemPreferenceController mPreferenceController;
private PrivateVolumeOptionMenuController mOptionMenuController;
private List<AbstractPreferenceController> mSecondaryUsers;
private boolean mPersonalOnly;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
// Initialize the storage sizes that we can quickly calc.
final Activity activity = getActivity();
StorageManager sm = activity.getSystemService(StorageManager.class);
mVolume = Utils.maybeInitializeVolume(sm, getArguments());
mPersonalOnly = getArguments().getInt(ProfileSelectFragment.EXTRA_PROFILE)
== ProfileSelectFragment.ProfileType.PERSONAL;
if (mVolume == null) {
activity.finish();
return;
}
initializeOptionsMenu(activity);
if (mPersonalOnly) {
final Preference summary = getPreferenceScreen().findPreference(SUMMARY_PREF_KEY);
if (summary != null) {
summary.setVisible(false);
}
}
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
use(AutomaticStorageManagementSwitchPreferenceController.class).setFragmentManager(
getFragmentManager());
}
@VisibleForTesting
void initializeOptionsMenu(Activity activity) {
mOptionMenuController = new PrivateVolumeOptionMenuController(
activity, mVolume, activity.getPackageManager());
getSettingsLifecycle().addObserver(mOptionMenuController);
setHasOptionsMenu(true);
activity.invalidateOptionsMenu();
}
@Override
public void onViewCreated(View v, Bundle savedInstanceState) {
super.onViewCreated(v, savedInstanceState);
initializeCacheProvider();
maybeSetLoading(isQuotaSupported());
final Activity activity = getActivity();
EntityHeaderController.newInstance(activity, this /*fragment*/,
null /* header view */)
.setRecyclerView(getListView(), getSettingsLifecycle())
.styleActionBar(activity);
}
@Override
public void onResume() {
super.onResume();
getLoaderManager().restartLoader(STORAGE_JOB_ID, Bundle.EMPTY, this);
getLoaderManager()
.restartLoader(VOLUME_SIZE_JOB_ID, Bundle.EMPTY, new VolumeSizeCallbacks());
getLoaderManager().restartLoader(ICON_JOB_ID, Bundle.EMPTY, new IconLoaderCallbacks());
}
@Override
public int getHelpResource() {
return R.string.help_url_storage_dashboard;
}
private void onReceivedSizes() {
boolean stopLoading = false;
if (mStorageInfo != null) {
long privateUsedBytes = mStorageInfo.totalBytes - mStorageInfo.freeBytes;
mPreferenceController.setVolume(mVolume);
mPreferenceController.setUsedSize(privateUsedBytes);
mPreferenceController.setTotalSize(mStorageInfo.totalBytes);
for (int i = 0, size = mSecondaryUsers.size(); i < size; i++) {
AbstractPreferenceController controller = mSecondaryUsers.get(i);
if (controller instanceof SecondaryUserController) {
SecondaryUserController userController = (SecondaryUserController) controller;
userController.setTotalSize(mStorageInfo.totalBytes);
}
}
stopLoading = true;
}
if (mAppsResult != null) {
mPreferenceController.onLoadFinished(mAppsResult, UserHandle.myUserId());
updateSecondaryUserControllers(mSecondaryUsers, mAppsResult);
stopLoading = true;
}
// setLoading always causes a flicker, so let's avoid doing it.
if (stopLoading) {
if (getView().findViewById(R.id.loading_container).getVisibility() == View.VISIBLE) {
setLoading(false, true);
}
}
}
@Override
public int getMetricsCategory() {
return SettingsEnums.SETTINGS_STORAGE_CATEGORY;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.storage_dashboard_fragment;
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
StorageManager sm = context.getSystemService(StorageManager.class);
mPreferenceController = new StorageItemPreferenceController(context, this,
mVolume, new StorageManagerVolumeProvider(sm));
controllers.add(mPreferenceController);
final UserManager userManager = context.getSystemService(UserManager.class);
mSecondaryUsers = SecondaryUserController.getSecondaryUserControllers(context, userManager);
controllers.addAll(mSecondaryUsers);
return controllers;
}
@VisibleForTesting
protected void setVolume(VolumeInfo info) {
mVolume = info;
}
/**
* Updates the secondary user controller sizes.
*/
private void updateSecondaryUserControllers(List<AbstractPreferenceController> controllers,
SparseArray<StorageAsyncLoader.AppsStorageResult> stats) {
for (int i = 0, size = controllers.size(); i < size; i++) {
AbstractPreferenceController controller = controllers.get(i);
if (controller instanceof StorageAsyncLoader.ResultHandler) {
StorageAsyncLoader.ResultHandler userController =
(StorageAsyncLoader.ResultHandler) controller;
userController.handleResult(stats);
}
}
}
/**
* For Search.
*/
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.storage_dashboard_fragment;
return Arrays.asList(sir);
}
@Override
public List<AbstractPreferenceController> createPreferenceControllers(
Context context) {
final StorageManager sm = context.getSystemService(StorageManager.class);
final UserManager userManager = context.getSystemService(UserManager.class);
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new StorageItemPreferenceController(context, null /* host */,
null /* volume */, new StorageManagerVolumeProvider(sm)));
controllers.addAll(SecondaryUserController.getSecondaryUserControllers(
context, userManager));
return controllers;
}
@Override
public boolean isPageSearchEnabled(Context context) {
return false; // 禁用搜索
}
};
@Override
public Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> onCreateLoader(int id,
Bundle args) {
final Context context = getContext();
return new StorageAsyncLoader(context, context.getSystemService(UserManager.class),
mVolume.fsUuid,
new StorageStatsSource(context),
context.getPackageManager());
}
@Override
public void onLoadFinished(Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> loader,
SparseArray<StorageAsyncLoader.AppsStorageResult> data) {
mAppsResult = data;
maybeCacheFreshValues();
onReceivedSizes();
}
@Override
public void onLoaderReset(Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> loader) {
}
@VisibleForTesting
public void setCachedStorageValuesHelper(CachedStorageValuesHelper helper) {
mCachedStorageValuesHelper = helper;
}
@VisibleForTesting
public PrivateStorageInfo getPrivateStorageInfo() {
return mStorageInfo;
}
@VisibleForTesting
public void setPrivateStorageInfo(PrivateStorageInfo info) {
mStorageInfo = info;
}
@VisibleForTesting
public SparseArray<StorageAsyncLoader.AppsStorageResult> getAppsStorageResult() {
return mAppsResult;
}
@VisibleForTesting
public void setAppsStorageResult(SparseArray<StorageAsyncLoader.AppsStorageResult> info) {
mAppsResult = info;
}
@VisibleForTesting
public void initializeCachedValues() {
PrivateStorageInfo info = mCachedStorageValuesHelper.getCachedPrivateStorageInfo();
SparseArray<StorageAsyncLoader.AppsStorageResult> loaderResult =
mCachedStorageValuesHelper.getCachedAppsStorageResult();
if (info == null || loaderResult == null) {
return;
}
mStorageInfo = info;
mAppsResult = loaderResult;
}
@VisibleForTesting
public void maybeSetLoading(boolean isQuotaSupported) {
// If we have fast stats, we load until both have loaded.
// If we have slow stats, we load when we get the total volume sizes.
if ((isQuotaSupported && (mStorageInfo == null || mAppsResult == null)) ||
(!isQuotaSupported && mStorageInfo == null)) {
setLoading(true /* loading */, false /* animate */);
}
}
private void initializeCacheProvider() {
mCachedStorageValuesHelper =
new CachedStorageValuesHelper(getContext(), UserHandle.myUserId());
initializeCachedValues();
onReceivedSizes();
}
private void maybeCacheFreshValues() {
if (mStorageInfo != null && mAppsResult != null) {
mCachedStorageValuesHelper.cacheResult(
mStorageInfo, mAppsResult.get(UserHandle.myUserId()));
}
}
private boolean isQuotaSupported() {
final StorageStatsManager stats = getActivity().getSystemService(StorageStatsManager.class);
return stats.isQuotaSupported(mVolume.fsUuid);
}
/**
* IconLoaderCallbacks exists because StorageDashboardFragment already implements
* LoaderCallbacks for a different type.
*/
public final class IconLoaderCallbacks
implements LoaderManager.LoaderCallbacks<SparseArray<Drawable>> {
@Override
public Loader<SparseArray<Drawable>> onCreateLoader(int id, Bundle args) {
return new UserIconLoader(
getContext(),
() -> UserIconLoader.loadUserIconsWithContext(getContext()));
}
@Override
public void onLoadFinished(
Loader<SparseArray<Drawable>> loader, SparseArray<Drawable> data) {
mSecondaryUsers
.stream()
.filter(controller -> controller instanceof UserIconLoader.UserIconHandler)
.forEach(
controller ->
((UserIconLoader.UserIconHandler) controller)
.handleUserIcons(data));
}
@Override
public void onLoaderReset(Loader<SparseArray<Drawable>> loader) {
}
}
public final class VolumeSizeCallbacks
implements LoaderManager.LoaderCallbacks<PrivateStorageInfo> {
@Override
public Loader<PrivateStorageInfo> onCreateLoader(int id, Bundle args) {
Context context = getContext();
StorageManager sm = context.getSystemService(StorageManager.class);
StorageManagerVolumeProvider smvp = new StorageManagerVolumeProvider(sm);
final StorageStatsManager stats = context.getSystemService(StorageStatsManager.class);
return new VolumeSizesLoader(context, smvp, stats, mVolume);
}
@Override
public void onLoaderReset(Loader<PrivateStorageInfo> loader) {
}
@Override
public void onLoadFinished(
Loader<PrivateStorageInfo> loader, PrivateStorageInfo privateStorageInfo) {
if (privateStorageInfo == null) {
getActivity().finish();
return;
}
mStorageInfo = privateStorageInfo;
maybeCacheFreshValues();
onReceivedSizes();
}
}
}
4.屏蔽已连接的设备:
源码路径:package/apps/settings/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment
关键修改如下:
/**
* For Search.
*/
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.connected_devices){
@Override
public boolean isPageSearchEnabled(Context context) {
return false; // 禁用搜索
}
};
/*
* 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.connecteddevice;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.net.Uri;
import android.provider.DeviceConfig;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.core.SettingsUIDeviceConfig;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.slices.SlicePreferenceController;
import com.android.settingslib.search.SearchIndexable;
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class ConnectedDeviceDashboardFragment extends DashboardFragment {
private static final String TAG = "ConnectedDeviceFrag";
@VisibleForTesting
static final String KEY_CONNECTED_DEVICES = "connected_device_list";
@VisibleForTesting
static final String KEY_AVAILABLE_DEVICES = "available_device_list";
@Override
public int getMetricsCategory() {
return SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected boolean isParalleledControllers() {
return true;
}
@Override
public int getHelpResource() {
return R.string.help_url_connected_devices;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.connected_devices;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
final boolean nearbyEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_NEAR_BY_SUGGESTION_ENABLED, true);
use(AvailableMediaDeviceGroupController.class).init(this);
use(ConnectedDeviceGroupController.class).init(this);
use(PreviouslyConnectedDevicePreferenceController.class).init(this);
use(SlicePreferenceController.class).setSliceUri(nearbyEnabled
? Uri.parse(getString(R.string.config_nearby_devices_slice_uri))
: null);
}
/**
* For Search.
*/
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.connected_devices){
@Override
public boolean isPageSearchEnabled(Context context) {
return false; // 禁用搜索
}
};
}
5.屏蔽网络和互联网:
源码路径:package/apps/settings/src/com/android/settings/network/NetworkDashboardFragment
关键修改方法如下:
在getNonIndexableKeysFromProvider()方法添加过滤
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.network_and_internet) {
@Override
public List<AbstractPreferenceController> createPreferenceControllers(Context
context) {
return buildPreferenceControllers(context, null /* lifecycle */,
null /* metricsFeatureProvider */, null /* fragment */,
null /* mobilePlanHost */);
}
@Override
public boolean isPageSearchEnabled(Context context) {
return false; // 禁用搜索
}
};
/*
* 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.network;
import static com.android.settings.network.MobilePlanPreferenceController.MANAGE_MOBILE_PLAN_DIALOG_ID;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.network.MobilePlanPreferenceController.MobilePlanPreferenceHost;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.wifi.WifiMasterSwitchPreferenceController;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.search.SearchIndexable;
import java.util.ArrayList;
import java.util.List;
@SearchIndexable
public class NetworkDashboardFragment extends DashboardFragment implements
MobilePlanPreferenceHost {
private static final String TAG = "NetworkDashboardFrag";
@Override
public int getMetricsCategory() {
return SettingsEnums.SETTINGS_NETWORK_CATEGORY;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.network_and_internet;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
use(MultiNetworkHeaderController.class).init(getSettingsLifecycle());
use(AirplaneModePreferenceController.class).setFragment(this);
getSettingsLifecycle().addObserver(use(AllInOneTetherPreferenceController.class));
}
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
super.onCreatePreferences(savedInstanceState, rootKey);
use(AllInOneTetherPreferenceController.class).initEnabler(getSettingsLifecycle());
}
@Override
public int getHelpResource() {
return R.string.help_url_network_dashboard;
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
return buildPreferenceControllers(context, getSettingsLifecycle(), mMetricsFeatureProvider,
this /* fragment */, this /* mobilePlanHost */);
}
@Override
protected boolean isParalleledControllers() {
return true;
}
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
Lifecycle lifecycle, MetricsFeatureProvider metricsFeatureProvider, Fragment fragment,
MobilePlanPreferenceHost mobilePlanHost) {
final MobilePlanPreferenceController mobilePlanPreferenceController =
new MobilePlanPreferenceController(context, mobilePlanHost);
final WifiMasterSwitchPreferenceController wifiPreferenceController =
new WifiMasterSwitchPreferenceController(context, metricsFeatureProvider);
final VpnPreferenceController vpnPreferenceController =
new VpnPreferenceController(context);
final PrivateDnsPreferenceController privateDnsPreferenceController =
new PrivateDnsPreferenceController(context);
if (lifecycle != null) {
lifecycle.addObserver(mobilePlanPreferenceController);
lifecycle.addObserver(wifiPreferenceController);
lifecycle.addObserver(vpnPreferenceController);
lifecycle.addObserver(privateDnsPreferenceController);
}
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new MobileNetworkSummaryController(context, lifecycle));
controllers.add(new TetherPreferenceController(context, lifecycle));
controllers.add(vpnPreferenceController);
controllers.add(new ProxyPreferenceController(context));
controllers.add(mobilePlanPreferenceController);
controllers.add(wifiPreferenceController);
controllers.add(privateDnsPreferenceController);
return controllers;
}
@Override
public void showMobilePlanMessageDialog() {
showDialog(MANAGE_MOBILE_PLAN_DIALOG_ID);
}
@Override
public Dialog onCreateDialog(int dialogId) {
Log.d(TAG, "onCreateDialog: dialogId=" + dialogId);
switch (dialogId) {
case MANAGE_MOBILE_PLAN_DIALOG_ID:
final MobilePlanPreferenceController controller =
use(MobilePlanPreferenceController.class);
return new AlertDialog.Builder(getActivity())
.setMessage(controller.getMobilePlanDialogMessage())
.setCancelable(false)
.setPositiveButton(com.android.internal.R.string.ok,
(dialog, id) -> controller.setMobilePlanDialogMessage(null))
.create();
}
return super.onCreateDialog(dialogId);
}
@Override
public int getDialogMetricsCategory(int dialogId) {
if (MANAGE_MOBILE_PLAN_DIALOG_ID == dialogId) {
return SettingsEnums.DIALOG_MANAGE_MOBILE_PLAN;
}
return 0;
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.network_and_internet) {
@Override
public List<AbstractPreferenceController> createPreferenceControllers(Context
context) {
return buildPreferenceControllers(context, null /* lifecycle */,
null /* metricsFeatureProvider */, null /* fragment */,
null /* mobilePlanHost */);
}
@Override
public boolean isPageSearchEnabled(Context context) {
return false; // 禁用搜索
}
};
}
6.屏蔽电池:
源码路径:package/apps/settings/src/com/android/settings/fuelgauge/PowerUsageSummary
关键修改方法:
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.power_usage_summary){
@Override
public boolean isPageSearchEnabled(Context context) {
return false; // 禁用搜索
}
};
/*
* Copyright (C) 2009 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.fuelgauge;
import static com.android.settings.fuelgauge.BatteryBroadcastReceiver.BatteryUpdateType;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings.Global;
import android.text.format.Formatter;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnLongClickListener;
import android.widget.TextView;
import androidx.annotation.VisibleForTesting;
import androidx.loader.app.LoaderManager;
import androidx.loader.app.LoaderManager.LoaderCallbacks;
import androidx.loader.content.Loader;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.fuelgauge.batterytip.BatteryTipLoader;
import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.fuelgauge.EstimateKt;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.utils.PowerUtil;
import com.android.settingslib.utils.StringUtil;
import com.android.settingslib.widget.LayoutPreference;
import java.util.List;
/**
* Displays a list of apps and subsystems that consume power, ordered by how much power was consumed
* since the last time it was unplugged.
*/
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class PowerUsageSummary extends PowerUsageBase implements OnLongClickListener,
BatteryTipPreferenceController.BatteryTipListener {
static final String TAG = "PowerUsageSummary";
private static final String KEY_BATTERY_HEADER = "battery_header";
private static final String KEY_SCREEN_USAGE = "screen_usage";
private static final String KEY_TIME_SINCE_LAST_FULL_CHARGE = "last_full_charge";
@VisibleForTesting
static final int BATTERY_INFO_LOADER = 1;
@VisibleForTesting
static final int BATTERY_TIP_LOADER = 2;
@VisibleForTesting
static final int MENU_ADVANCED_BATTERY = Menu.FIRST + 1;
public static final int DEBUG_INFO_LOADER = 3;
@VisibleForTesting
PowerGaugePreference mScreenUsagePref;
@VisibleForTesting
PowerGaugePreference mLastFullChargePref;
@VisibleForTesting
PowerUsageFeatureProvider mPowerFeatureProvider;
@VisibleForTesting
BatteryUtils mBatteryUtils;
@VisibleForTesting
LayoutPreference mBatteryLayoutPref;
@VisibleForTesting
BatteryInfo mBatteryInfo;
@VisibleForTesting
BatteryHeaderPreferenceController mBatteryHeaderPreferenceController;
@VisibleForTesting
boolean mNeedUpdateBatteryTip;
@VisibleForTesting
BatteryTipPreferenceController mBatteryTipPreferenceController;
@VisibleForTesting
final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange, Uri uri) {
restartBatteryInfoLoader();
}
};
@VisibleForTesting
LoaderManager.LoaderCallbacks<BatteryInfo> mBatteryInfoLoaderCallbacks =
new LoaderManager.LoaderCallbacks<BatteryInfo>() {
@Override
public Loader<BatteryInfo> onCreateLoader(int i, Bundle bundle) {
return new BatteryInfoLoader(getContext(), mStatsHelper);
}
@Override
public void onLoadFinished(Loader<BatteryInfo> loader, BatteryInfo batteryInfo) {
mBatteryHeaderPreferenceController.updateHeaderPreference(batteryInfo);
mBatteryInfo = batteryInfo;
updateLastFullChargePreference();
}
@Override
public void onLoaderReset(Loader<BatteryInfo> loader) {
// do nothing
}
};
LoaderManager.LoaderCallbacks<List<BatteryInfo>> mBatteryInfoDebugLoaderCallbacks =
new LoaderCallbacks<List<BatteryInfo>>() {
@Override
public Loader<List<BatteryInfo>> onCreateLoader(int i, Bundle bundle) {
return new DebugEstimatesLoader(getContext(), mStatsHelper);
}
@Override
public void onLoadFinished(Loader<List<BatteryInfo>> loader,
List<BatteryInfo> batteryInfos) {
updateViews(batteryInfos);
}
@Override
public void onLoaderReset(Loader<List<BatteryInfo>> loader) {
}
};
protected void updateViews(List<BatteryInfo> batteryInfos) {
final BatteryMeterView batteryView = mBatteryLayoutPref
.findViewById(R.id.battery_header_icon);
final TextView percentRemaining =
mBatteryLayoutPref.findViewById(R.id.battery_percent);
final TextView summary1 = mBatteryLayoutPref.findViewById(R.id.summary1);
BatteryInfo oldInfo = batteryInfos.get(0);
BatteryInfo newInfo = batteryInfos.get(1);
percentRemaining.setText(Utils.formatPercentage(oldInfo.batteryLevel));
// set the text to the old estimate (copied from battery info). Note that this
// can sometimes say 0 time remaining because battery stats requires the phone
// be unplugged for a period of time before being willing ot make an estimate.
final String OldEstimateString = mPowerFeatureProvider.getOldEstimateDebugString(
Formatter.formatShortElapsedTime(getContext(),
PowerUtil.convertUsToMs(oldInfo.remainingTimeUs)));
final String NewEstimateString = mPowerFeatureProvider.getEnhancedEstimateDebugString(
Formatter.formatShortElapsedTime(getContext(),
PowerUtil.convertUsToMs(newInfo.remainingTimeUs)));
summary1.setText(OldEstimateString + "\n" + NewEstimateString);
batteryView.setBatteryLevel(oldInfo.batteryLevel);
batteryView.setCharging(!oldInfo.discharging);
}
private LoaderManager.LoaderCallbacks<List<BatteryTip>> mBatteryTipsCallbacks =
new LoaderManager.LoaderCallbacks<List<BatteryTip>>() {
@Override
public Loader<List<BatteryTip>> onCreateLoader(int id, Bundle args) {
return new BatteryTipLoader(getContext(), mStatsHelper);
}
@Override
public void onLoadFinished(Loader<List<BatteryTip>> loader,
List<BatteryTip> data) {
mBatteryTipPreferenceController.updateBatteryTips(data);
}
@Override
public void onLoaderReset(Loader<List<BatteryTip>> loader) {
}
};
@Override
public void onAttach(Context context) {
super.onAttach(context);
final SettingsActivity activity = (SettingsActivity) getActivity();
mBatteryHeaderPreferenceController = use(BatteryHeaderPreferenceController.class);
mBatteryHeaderPreferenceController.setActivity(activity);
mBatteryHeaderPreferenceController.setFragment(this);
mBatteryHeaderPreferenceController.setLifecycle(getSettingsLifecycle());
mBatteryTipPreferenceController = use(BatteryTipPreferenceController.class);
mBatteryTipPreferenceController.setActivity(activity);
mBatteryTipPreferenceController.setFragment(this);
mBatteryTipPreferenceController.setBatteryTipListener(this::onBatteryTipHandled);
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setAnimationAllowed(true);
initFeatureProvider();
mBatteryLayoutPref = (LayoutPreference) findPreference(KEY_BATTERY_HEADER);
mScreenUsagePref = (PowerGaugePreference) findPreference(KEY_SCREEN_USAGE);
mLastFullChargePref = (PowerGaugePreference) findPreference(
KEY_TIME_SINCE_LAST_FULL_CHARGE);
mBatteryUtils = BatteryUtils.getInstance(getContext());
if (Utils.isBatteryPresent(getContext())) {
restartBatteryInfoLoader();
}
mBatteryTipPreferenceController.restoreInstanceState(icicle);
updateBatteryTipFlag(icicle);
}
@Override
public void onResume() {
super.onResume();
getContentResolver().registerContentObserver(
Global.getUriFor(Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME),
false,
mSettingsObserver);
}
@Override
public void onPause() {
getContentResolver().unregisterContentObserver(mSettingsObserver);
super.onPause();
}
@Override
public int getMetricsCategory() {
return SettingsEnums.FUELGAUGE_POWER_USAGE_SUMMARY_V2;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.power_usage_summary;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.add(Menu.NONE, MENU_ADVANCED_BATTERY, Menu.NONE, R.string.advanced_battery_title);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public int getHelpResource() {
return R.string.help_url_battery;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_ADVANCED_BATTERY:
new SubSettingLauncher(getContext())
.setDestination(PowerUsageAdvanced.class.getName())
.setSourceMetricsCategory(getMetricsCategory())
.setTitleRes(R.string.advanced_battery_title)
.launch();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
protected void refreshUi(@BatteryUpdateType int refreshType) {
final Context context = getContext();
if (context == null) {
return;
}
// Skip refreshing UI if battery is not present.
if (!mIsBatteryPresent) {
return;
}
// Skip BatteryTipLoader if device is rotated or only battery level change
if (mNeedUpdateBatteryTip
&& refreshType != BatteryUpdateType.BATTERY_LEVEL) {
restartBatteryTipLoader();
} else {
mNeedUpdateBatteryTip = true;
}
// reload BatteryInfo and updateUI
restartBatteryInfoLoader();
updateLastFullChargePreference();
mScreenUsagePref.setSubtitle(StringUtil.formatElapsedTime(getContext(),
mBatteryUtils.calculateScreenUsageTime(mStatsHelper), false));
}
@VisibleForTesting
void restartBatteryTipLoader() {
getLoaderManager().restartLoader(BATTERY_TIP_LOADER, Bundle.EMPTY, mBatteryTipsCallbacks);
}
@VisibleForTesting
void setBatteryLayoutPreference(LayoutPreference layoutPreference) {
mBatteryLayoutPref = layoutPreference;
}
@VisibleForTesting
void updateLastFullChargePreference() {
if (mBatteryInfo != null && mBatteryInfo.averageTimeToDischarge
!= EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN) {
mLastFullChargePref.setTitle(R.string.battery_full_charge_last);
mLastFullChargePref.setSubtitle(
StringUtil.formatElapsedTime(getContext(), mBatteryInfo.averageTimeToDischarge,
false /* withSeconds */));
} else {
final long lastFullChargeTime = mBatteryUtils.calculateLastFullChargeTime(mStatsHelper,
System.currentTimeMillis());
mLastFullChargePref.setTitle(R.string.battery_last_full_charge);
mLastFullChargePref.setSubtitle(
StringUtil.formatRelativeTime(getContext(), lastFullChargeTime,
false /* withSeconds */));
}
}
@VisibleForTesting
void showBothEstimates() {
final Context context = getContext();
if (context == null
|| !mPowerFeatureProvider.isEnhancedBatteryPredictionEnabled(context)) {
return;
}
getLoaderManager().restartLoader(DEBUG_INFO_LOADER, Bundle.EMPTY,
mBatteryInfoDebugLoaderCallbacks);
}
@VisibleForTesting
void initFeatureProvider() {
final Context context = getContext();
mPowerFeatureProvider = FeatureFactory.getFactory(context)
.getPowerUsageFeatureProvider(context);
}
@VisibleForTesting
void restartBatteryInfoLoader() {
if (getContext() == null) {
return;
}
// Skip restartBatteryInfoLoader if battery is not present.
if (!mIsBatteryPresent) {
return;
}
getLoaderManager().restartLoader(BATTERY_INFO_LOADER, Bundle.EMPTY,
mBatteryInfoLoaderCallbacks);
if (mPowerFeatureProvider.isEstimateDebugEnabled()) {
// Set long click action for summary to show debug info
View header = mBatteryLayoutPref.findViewById(R.id.summary1);
header.setOnLongClickListener(this);
}
}
@VisibleForTesting
void updateBatteryTipFlag(Bundle icicle) {
mNeedUpdateBatteryTip = icicle == null || mBatteryTipPreferenceController.needUpdate();
}
@Override
public boolean onLongClick(View view) {
showBothEstimates();
view.setOnLongClickListener(null);
return true;
}
@Override
protected void restartBatteryStatsLoader(@BatteryUpdateType int refreshType) {
super.restartBatteryStatsLoader(refreshType);
// Update battery header if battery is present.
if (mIsBatteryPresent) {
mBatteryHeaderPreferenceController.quickUpdateHeaderPreference();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mBatteryTipPreferenceController.saveInstanceState(outState);
}
@Override
public void onBatteryTipHandled(BatteryTip batteryTip) {
restartBatteryTipLoader();
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.power_usage_summary){
@Override
public boolean isPageSearchEnabled(Context context) {
return false; // 禁用搜索
}
};
}
7.屏蔽功能入口:
源码路径:package/apps/settings/src/com/android/settings/DashboardFragmentRegistry
核心修改方法:注销以上几个App的入口:
PARENT_TO_CATEGORY_KEY_MAP = new ArrayMap<>();
PARENT_TO_CATEGORY_KEY_MAP.put(TopLevelSettings.class.getName(),
CategoryKey.CATEGORY_HOMEPAGE);
/* PARENT_TO_CATEGORY_KEY_MAP.put(
NetworkDashboardFragment.class.getName(), CategoryKey.CATEGORY_NETWORK);
PARENT_TO_CATEGORY_KEY_MAP.put(ConnectedDeviceDashboardFragment.class.getName(),
CategoryKey.CATEGORY_CONNECT);
PARENT_TO_CATEGORY_KEY_MAP.put(AdvancedConnectedDeviceDashboardFragment.class.getName(),
CategoryKey.CATEGORY_DEVICE);*/
PARENT_TO_CATEGORY_KEY_MAP.put(AppAndNotificationDashboardFragment.class.getName(),
CategoryKey.CATEGORY_APPS);
/* PARENT_TO_CATEGORY_KEY_MAP.put(PowerUsageSummary.class.getName(),
CategoryKey.CATEGORY_BATTERY);*/
PARENT_TO_CATEGORY_KEY_MAP.put(DisplaySettings.class.getName(),
CategoryKey.CATEGORY_DISPLAY);
PARENT_TO_CATEGORY_KEY_MAP.put(SoundSettings.class.getName(),
CategoryKey.CATEGORY_SOUND);
/* PARENT_TO_CATEGORY_KEY_MAP.put(StorageDashboardFragment.class.getName(),
CategoryKey.CATEGORY_STORAGE);*/
/*
* 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.dashboard;
import android.util.ArrayMap;
import com.android.settings.DisplaySettings;
import com.android.settings.LegalSettings;
import com.android.settings.accounts.AccountDashboardFragment;
import com.android.settings.accounts.AccountDetailDashboardFragment;
import com.android.settings.applications.AppAndNotificationDashboardFragment;
import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment;
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
import com.android.settings.development.DevelopmentSettingsDashboardFragment;
import com.android.settings.deviceinfo.StorageDashboardFragment;
import com.android.settings.deviceinfo.aboutphone.MyDeviceInfoFragment;
import com.android.settings.display.NightDisplaySettings;
import com.android.settings.enterprise.EnterprisePrivacySettings;
import com.android.settings.fuelgauge.PowerUsageSummary;
import com.android.settings.fuelgauge.SmartBatterySettings;
import com.android.settings.fuelgauge.batterysaver.BatterySaverSettings;
import com.android.settings.gestures.GestureSettings;
import com.android.settings.homepage.TopLevelSettings;
import com.android.settings.language.LanguageAndInputSettings;
import com.android.settings.network.NetworkDashboardFragment;
import com.android.settings.notification.ConfigureNotificationSettings;
import com.android.settings.notification.SoundSettings;
import com.android.settings.notification.zen.ZenModeSettings;
import com.android.settings.privacy.PrivacyDashboardFragment;
import com.android.settings.security.LockscreenDashboardFragment;
import com.android.settings.security.SecuritySettings;
import com.android.settings.system.SystemDashboardFragment;
import com.android.settingslib.drawer.CategoryKey;
import java.util.Map;
/**
* A registry to keep track of which page hosts which category.
*/
public class DashboardFragmentRegistry {
/**
* Map from parent fragment to category key. The parent fragment hosts child with
* category_key.
*/
public static final Map<String, String> PARENT_TO_CATEGORY_KEY_MAP;
/**
* Map from category_key to parent. This is a helper to look up which fragment hosts the
* category_key.
*/
public static final Map<String, String> CATEGORY_KEY_TO_PARENT_MAP;
static {
PARENT_TO_CATEGORY_KEY_MAP = new ArrayMap<>();
PARENT_TO_CATEGORY_KEY_MAP.put(TopLevelSettings.class.getName(),
CategoryKey.CATEGORY_HOMEPAGE);
PARENT_TO_CATEGORY_KEY_MAP.put(
NetworkDashboardFragment.class.getName(), CategoryKey.CATEGORY_NETWORK);
PARENT_TO_CATEGORY_KEY_MAP.put(ConnectedDeviceDashboardFragment.class.getName(),
CategoryKey.CATEGORY_CONNECT);
PARENT_TO_CATEGORY_KEY_MAP.put(AdvancedConnectedDeviceDashboardFragment.class.getName(),
CategoryKey.CATEGORY_DEVICE);
PARENT_TO_CATEGORY_KEY_MAP.put(AppAndNotificationDashboardFragment.class.getName(),
CategoryKey.CATEGORY_APPS);
PARENT_TO_CATEGORY_KEY_MAP.put(PowerUsageSummary.class.getName(),
CategoryKey.CATEGORY_BATTERY);
PARENT_TO_CATEGORY_KEY_MAP.put(DisplaySettings.class.getName(),
CategoryKey.CATEGORY_DISPLAY);
PARENT_TO_CATEGORY_KEY_MAP.put(SoundSettings.class.getName(),
CategoryKey.CATEGORY_SOUND);
PARENT_TO_CATEGORY_KEY_MAP.put(StorageDashboardFragment.class.getName(),
CategoryKey.CATEGORY_STORAGE);
PARENT_TO_CATEGORY_KEY_MAP.put(SecuritySettings.class.getName(),
CategoryKey.CATEGORY_SECURITY);
PARENT_TO_CATEGORY_KEY_MAP.put(AccountDetailDashboardFragment.class.getName(),
CategoryKey.CATEGORY_ACCOUNT_DETAIL);
PARENT_TO_CATEGORY_KEY_MAP.put(AccountDashboardFragment.class.getName(),
CategoryKey.CATEGORY_ACCOUNT);
PARENT_TO_CATEGORY_KEY_MAP.put(
SystemDashboardFragment.class.getName(), CategoryKey.CATEGORY_SYSTEM);
PARENT_TO_CATEGORY_KEY_MAP.put(LanguageAndInputSettings.class.getName(),
CategoryKey.CATEGORY_SYSTEM_LANGUAGE);
PARENT_TO_CATEGORY_KEY_MAP.put(DevelopmentSettingsDashboardFragment.class.getName(),
CategoryKey.CATEGORY_SYSTEM_DEVELOPMENT);
PARENT_TO_CATEGORY_KEY_MAP.put(ConfigureNotificationSettings.class.getName(),
CategoryKey.CATEGORY_NOTIFICATIONS);
PARENT_TO_CATEGORY_KEY_MAP.put(LockscreenDashboardFragment.class.getName(),
CategoryKey.CATEGORY_SECURITY_LOCKSCREEN);
PARENT_TO_CATEGORY_KEY_MAP.put(ZenModeSettings.class.getName(),
CategoryKey.CATEGORY_DO_NOT_DISTURB);
PARENT_TO_CATEGORY_KEY_MAP.put(GestureSettings.class.getName(),
CategoryKey.CATEGORY_GESTURES);
PARENT_TO_CATEGORY_KEY_MAP.put(NightDisplaySettings.class.getName(),
CategoryKey.CATEGORY_NIGHT_DISPLAY);
PARENT_TO_CATEGORY_KEY_MAP.put(PrivacyDashboardFragment.class.getName(),
CategoryKey.CATEGORY_PRIVACY);
PARENT_TO_CATEGORY_KEY_MAP.put(EnterprisePrivacySettings.class.getName(),
CategoryKey.CATEGORY_ENTERPRISE_PRIVACY);
PARENT_TO_CATEGORY_KEY_MAP.put(LegalSettings.class.getName(),
CategoryKey.CATEGORY_ABOUT_LEGAL);
PARENT_TO_CATEGORY_KEY_MAP.put(MyDeviceInfoFragment.class.getName(),
CategoryKey.CATEGORY_MY_DEVICE_INFO);
PARENT_TO_CATEGORY_KEY_MAP.put(BatterySaverSettings.class.getName(),
CategoryKey.CATEGORY_BATTERY_SAVER_SETTINGS);
PARENT_TO_CATEGORY_KEY_MAP.put(SmartBatterySettings.class.getName(),
CategoryKey.CATEGORY_SMART_BATTERY_SETTINGS);
CATEGORY_KEY_TO_PARENT_MAP = new ArrayMap<>(PARENT_TO_CATEGORY_KEY_MAP.size());
for (Map.Entry<String, String> parentToKey : PARENT_TO_CATEGORY_KEY_MAP.entrySet()) {
CATEGORY_KEY_TO_PARENT_MAP.put(parentToKey.getValue(), parentToKey.getKey());
}
// For injection index, redirect CATEGORY_ACCOUNT_DETAIL to AccountDashboardFragment.
CATEGORY_KEY_TO_PARENT_MAP.put(CategoryKey.CATEGORY_ACCOUNT_DETAIL,
AccountDashboardFragment.class.getName());
}
}
8.修改搜索Provider:
源码路径:package/apps/settings/src/com/android/settings/SettingsSearchIndexablesProvider
关键修改如下:
在getNonIndexableKeysFromProvider()方法添加如下过滤:
nonIndexableKeys.add(CategoryKey.CATEGORY_NETWORK);
nonIndexableKeys.add(CategoryKey.CATEGORY_CONNECT);
nonIndexableKeys.add(CategoryKey.CATEGORY_DEVICE);
nonIndexableKeys.add(CategoryKey.CATEGORY_BATTERY);
nonIndexableKeys.add(CategoryKey.CATEGORY_STORAGE);
/*
* Copyright (C) 2014 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.search;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_CLASS_NAME;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ENTRIES;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ICON_RESID;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_ACTION;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_CLASS;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEY;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEYWORDS;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SCREEN_TITLE;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_OFF;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_ON;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_TITLE;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_USER_ID;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_CLASS_NAME;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_ICON_RESID;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_ACTION;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RANK;
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RESID;
import static android.provider.SearchIndexablesContract.INDEXABLES_RAW_COLUMNS;
import static android.provider.SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS;
import static android.provider.SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS;
import static android.provider.SearchIndexablesContract.SITE_MAP_COLUMNS;
import static android.provider.SearchIndexablesContract.SLICE_URI_PAIRS_COLUMNS;
import static com.android.settings.dashboard.DashboardFragmentRegistry.CATEGORY_KEY_TO_PARENT_MAP;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.provider.SearchIndexableResource;
import android.provider.SearchIndexablesContract;
import android.provider.SearchIndexablesProvider;
import android.provider.SettingsSlicesContract;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.slice.SliceViewManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.dashboard.DashboardFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.slices.SettingsSliceProvider;
import com.android.settingslib.drawer.ActivityTile;
import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.search.Indexable;
import com.android.settingslib.search.SearchIndexableData;
import com.android.settingslib.search.SearchIndexableRaw;
import com.android.settingslib.drawer.CategoryKey;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class SettingsSearchIndexablesProvider extends SearchIndexablesProvider {
public static final boolean DEBUG = false;
/**
* Flag for a system property which checks if we should crash if there are issues in the
* indexing pipeline.
*/
public static final String SYSPROP_CRASH_ON_ERROR =
"debug.com.android.settings.search.crash_on_error";
private static final String TAG = "SettingsSearchProvider";
private static final Collection<String> INVALID_KEYS;
static {
INVALID_KEYS = new ArraySet<>();
INVALID_KEYS.add(null);
INVALID_KEYS.add("");
}
@Override
public boolean onCreate() {
return true;
}
@Override
public Cursor queryXmlResources(String[] projection) {
final MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS);
final List<SearchIndexableResource> resources =
getSearchIndexableResourcesFromProvider(getContext());
for (SearchIndexableResource val : resources) {
final Object[] ref = new Object[INDEXABLES_XML_RES_COLUMNS.length];
ref[COLUMN_INDEX_XML_RES_RANK] = val.rank;
ref[COLUMN_INDEX_XML_RES_RESID] = val.xmlResId;
ref[COLUMN_INDEX_XML_RES_CLASS_NAME] = val.className;
ref[COLUMN_INDEX_XML_RES_ICON_RESID] = val.iconResId;
ref[COLUMN_INDEX_XML_RES_INTENT_ACTION] = val.intentAction;
ref[COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE] = val.intentTargetPackage;
ref[COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS] = null; // intent target class
cursor.addRow(ref);
}
return cursor;
}
/**
* Gets a Cursor of RawData. We use those data in search indexing time
*/
@Override
public Cursor queryRawData(String[] projection) {
final MatrixCursor cursor = new MatrixCursor(INDEXABLES_RAW_COLUMNS);
final List<SearchIndexableRaw> raws = getSearchIndexableRawFromProvider(getContext());
for (SearchIndexableRaw val : raws) {
cursor.addRow(createIndexableRawColumnObjects(val));
}
return cursor;
}
/**
* Gets a combined list non-indexable keys that come from providers inside of settings.
* The non-indexable keys are used in Settings search at both index and update time to verify
* the validity of results in the database.
*/
@Override
public Cursor queryNonIndexableKeys(String[] projection) {
final MatrixCursor cursor = new MatrixCursor(NON_INDEXABLES_KEYS_COLUMNS);
final List<String> nonIndexableKeys = getNonIndexableKeysFromProvider(getContext());
for (String nik : nonIndexableKeys) {
final Object[] ref = new Object[NON_INDEXABLES_KEYS_COLUMNS.length];
ref[COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE] = nik;
cursor.addRow(ref);
}
return cursor;
}
/**
* Gets a Cursor of dynamic Raw data similar to queryRawData. We use those data in search query
* time
*/
@Nullable
@Override
public Cursor queryDynamicRawData(String[] projection) {
final Context context = getContext();
final List<SearchIndexableRaw> rawList = new ArrayList<>();
rawList.addAll(getDynamicSearchIndexableRawFromProvider(context));
rawList.addAll(getInjectionIndexableRawData(context));
final MatrixCursor cursor = new MatrixCursor(INDEXABLES_RAW_COLUMNS);
for (SearchIndexableRaw raw : rawList) {
cursor.addRow(createIndexableRawColumnObjects(raw));
}
return cursor;
}
@Override
public Cursor querySiteMapPairs() {
final MatrixCursor cursor = new MatrixCursor(SITE_MAP_COLUMNS);
final Context context = getContext();
// Loop through all IA categories and pages and build additional SiteMapPairs
final List<DashboardCategory> categories = FeatureFactory.getFactory(context)
.getDashboardFeatureProvider(context).getAllCategories();
for (DashboardCategory category : categories) {
// Use the category key to look up parent (which page hosts this key)
final String parentClass = CATEGORY_KEY_TO_PARENT_MAP.get(category.key);
if (parentClass == null) {
continue;
}
// Build parent-child class pairs for all children listed under this key.
for (Tile tile : category.getTiles()) {
String childClass = null;
CharSequence childTitle = "";
if (tile.getMetaData() != null) {
childClass = tile.getMetaData().getString(
SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS);
}
if (childClass == null) {
childClass = tile.getComponentName();
childTitle = tile.getTitle(getContext());
}
if (childClass == null) {
continue;
}
cursor.newRow()
.add(SearchIndexablesContract.SiteMapColumns.PARENT_CLASS, parentClass)
.add(SearchIndexablesContract.SiteMapColumns.CHILD_CLASS, childClass)
.add(SearchIndexablesContract.SiteMapColumns.CHILD_TITLE, childTitle);
}
}
// Loop through custom site map registry to build additional SiteMapPairs
for (String childClass : CustomSiteMapRegistry.CUSTOM_SITE_MAP.keySet()) {
final String parentClass = CustomSiteMapRegistry.CUSTOM_SITE_MAP.get(childClass);
cursor.newRow()
.add(SearchIndexablesContract.SiteMapColumns.PARENT_CLASS, parentClass)
.add(SearchIndexablesContract.SiteMapColumns.CHILD_CLASS, childClass);
}
// Done.
return cursor;
}
@Override
public Cursor querySliceUriPairs() {
final SliceViewManager manager = SliceViewManager.getInstance(getContext());
final MatrixCursor cursor = new MatrixCursor(SLICE_URI_PAIRS_COLUMNS);
final String queryUri = getContext().getString(R.string.config_non_public_slice_query_uri);
final Uri baseUri = !TextUtils.isEmpty(queryUri) ? Uri.parse(queryUri)
: new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.build();
final Uri platformBaseUri =
new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSlicesContract.AUTHORITY)
.build();
final Collection<Uri> sliceUris = manager.getSliceDescendants(baseUri);
sliceUris.addAll(manager.getSliceDescendants(platformBaseUri));
for (Uri uri : sliceUris) {
cursor.newRow()
.add(SearchIndexablesContract.SliceUriPairColumns.KEY, uri.getLastPathSegment())
.add(SearchIndexablesContract.SliceUriPairColumns.SLICE_URI, uri);
}
return cursor;
}
private List<String> getNonIndexableKeysFromProvider(Context context) {
final Collection<SearchIndexableData> bundles = FeatureFactory.getFactory(context)
.getSearchFeatureProvider().getSearchIndexableResources().getProviderValues();
final List<String> nonIndexableKeys = new ArrayList<>();
nonIndexableKeys.add(CategoryKey.CATEGORY_NETWORK);
nonIndexableKeys.add(CategoryKey.CATEGORY_CONNECT);
nonIndexableKeys.add(CategoryKey.CATEGORY_DEVICE);
nonIndexableKeys.add(CategoryKey.CATEGORY_BATTERY);
nonIndexableKeys.add(CategoryKey.CATEGORY_STORAGE);
for (SearchIndexableData bundle : bundles) {
final long startTime = System.currentTimeMillis();
Indexable.SearchIndexProvider provider = bundle.getSearchIndexProvider();
List<String> providerNonIndexableKeys;
try {
providerNonIndexableKeys = provider.getNonIndexableKeys(context);
} catch (Exception e) {
// Catch a generic crash. In the absence of the catch, the background thread will
// silently fail anyway, so we aren't losing information by catching the exception.
// We crash when the system property exists so that we can test if crashes need to
// be fixed.
// The gain is that if there is a crash in a specific controller, we don't lose all
// non-indexable keys, but we can still find specific crashes in development.
if (System.getProperty(SYSPROP_CRASH_ON_ERROR) != null) {
throw new RuntimeException(e);
}
Log.e(TAG, "Error trying to get non-indexable keys from: "
+ bundle.getTargetClass().getName(), e);
continue;
}
if (providerNonIndexableKeys == null || providerNonIndexableKeys.isEmpty()) {
if (DEBUG) {
final long totalTime = System.currentTimeMillis() - startTime;
Log.d(TAG, "No indexable, total time " + totalTime);
}
continue;
}
if (providerNonIndexableKeys.removeAll(INVALID_KEYS)) {
Log.v(TAG, provider + " tried to add an empty non-indexable key");
}
if (DEBUG) {
final long totalTime = System.currentTimeMillis() - startTime;
Log.d(TAG, "Non-indexables " + providerNonIndexableKeys.size() + ", total time "
+ totalTime);
}
nonIndexableKeys.addAll(providerNonIndexableKeys);
}
return nonIndexableKeys;
}
private List<SearchIndexableResource> getSearchIndexableResourcesFromProvider(Context context) {
final Collection<SearchIndexableData> bundles = FeatureFactory.getFactory(context)
.getSearchFeatureProvider().getSearchIndexableResources().getProviderValues();
List<SearchIndexableResource> resourceList = new ArrayList<>();
for (SearchIndexableData bundle : bundles) {
Indexable.SearchIndexProvider provider = bundle.getSearchIndexProvider();
final List<SearchIndexableResource> resList =
provider.getXmlResourcesToIndex(context, true);
if (resList == null) {
continue;
}
for (SearchIndexableResource item : resList) {
item.className = TextUtils.isEmpty(item.className)
? bundle.getTargetClass().getName()
: item.className;
}
resourceList.addAll(resList);
}
return resourceList;
}
private List<SearchIndexableRaw> getSearchIndexableRawFromProvider(Context context) {
final Collection<SearchIndexableData> bundles = FeatureFactory.getFactory(context)
.getSearchFeatureProvider().getSearchIndexableResources().getProviderValues();
final List<SearchIndexableRaw> rawList = new ArrayList<>();
for (SearchIndexableData bundle : bundles) {
Indexable.SearchIndexProvider provider = bundle.getSearchIndexProvider();
final List<SearchIndexableRaw> providerRaws = provider.getRawDataToIndex(context,
true /* enabled */);
if (providerRaws == null) {
continue;
}
for (SearchIndexableRaw raw : providerRaws) {
// The classname and intent information comes from the PreIndexData
// This will be more clear when provider conversion is done at PreIndex time.
raw.className = bundle.getTargetClass().getName();
}
rawList.addAll(providerRaws);
}
return rawList;
}
private List<SearchIndexableRaw> getDynamicSearchIndexableRawFromProvider(Context context) {
final Collection<SearchIndexableData> bundles = FeatureFactory.getFactory(context)
.getSearchFeatureProvider().getSearchIndexableResources().getProviderValues();
final List<SearchIndexableRaw> rawList = new ArrayList<>();
for (SearchIndexableData bundle : bundles) {
final Indexable.SearchIndexProvider provider = bundle.getSearchIndexProvider();
final List<SearchIndexableRaw> providerRaws =
provider.getDynamicRawDataToIndex(context, true /* enabled */);
if (providerRaws == null) {
continue;
}
for (SearchIndexableRaw raw : providerRaws) {
// The classname and intent information comes from the PreIndexData
// This will be more clear when provider conversion is done at PreIndex time.
raw.className = bundle.getTargetClass().getName();
}
rawList.addAll(providerRaws);
}
return rawList;
}
private List<SearchIndexableRaw> getInjectionIndexableRawData(Context context) {
final DashboardFeatureProvider dashboardFeatureProvider =
FeatureFactory.getFactory(context).getDashboardFeatureProvider(context);
final List<SearchIndexableRaw> rawList = new ArrayList<>();
final String currentPackageName = context.getPackageName();
for (DashboardCategory category : dashboardFeatureProvider.getAllCategories()) {
// add start 屏蔽某些App
if (category.key.equals(CategoryKey.CATEGORY_NETWORK) ||
category.key.equals(CategoryKey.CATEGORY_CONNECT) ||
category.key.equals(CategoryKey.CATEGORY_DEVICE) ||
category.key.equals(CategoryKey.CATEGORY_BATTERY) ||
category.key.equals(CategoryKey.CATEGORY_STORAGE)) {
continue;
}
//add end
for (Tile tile : category.getTiles()) {
if (!isEligibleForIndexing(currentPackageName, tile)) {
continue;
}
final SearchIndexableRaw raw = new SearchIndexableRaw(context);
final CharSequence title = tile.getTitle(context);
raw.title = TextUtils.isEmpty(title) ? null : title.toString();
if (TextUtils.isEmpty(raw.title)) {
continue;
}
raw.key = dashboardFeatureProvider.getDashboardKeyForTile(tile);
final CharSequence summary = tile.getSummary(context);
raw.summaryOn = TextUtils.isEmpty(summary) ? null : summary.toString();
raw.summaryOff = raw.summaryOn;
raw.className = CATEGORY_KEY_TO_PARENT_MAP.get(tile.getCategory());
rawList.add(raw);
}
}
return rawList;
}
@VisibleForTesting
boolean isEligibleForIndexing(String packageName, Tile tile) {
// 屏蔽特定 Tile
if (tile.getCategory().equals(CategoryKey.CATEGORY_NETWORK) ||
tile.getCategory().equals(CategoryKey.CATEGORY_CONNECT) ||
tile.getCategory().equals(CategoryKey.CATEGORY_DEVICE) ||
tile.getCategory().equals(CategoryKey.CATEGORY_BATTERY) ||
tile.getCategory().equals(CategoryKey.CATEGORY_STORAGE)) {
return false;
}
if (TextUtils.equals(packageName, tile.getPackageName())
&& tile instanceof ActivityTile) {
// Skip Settings injected items because they should be indexed in the sub-pages.
return false;
}
if (TextUtils.equals(tile.getCategory(), CategoryKey.CATEGORY_HOMEPAGE)) {
// Skip homepage injected items since we would like to index their target activity.
return false;
}
return true;
}
private static Object[] createIndexableRawColumnObjects(SearchIndexableRaw raw) {
final Object[] ref = new Object[INDEXABLES_RAW_COLUMNS.length];
ref[COLUMN_INDEX_RAW_TITLE] = raw.title;
ref[COLUMN_INDEX_RAW_SUMMARY_ON] = raw.summaryOn;
ref[COLUMN_INDEX_RAW_SUMMARY_OFF] = raw.summaryOff;
ref[COLUMN_INDEX_RAW_ENTRIES] = raw.entries;
ref[COLUMN_INDEX_RAW_KEYWORDS] = raw.keywords;
ref[COLUMN_INDEX_RAW_SCREEN_TITLE] = raw.screenTitle;
ref[COLUMN_INDEX_RAW_CLASS_NAME] = raw.className;
ref[COLUMN_INDEX_RAW_ICON_RESID] = raw.iconResId;
ref[COLUMN_INDEX_RAW_INTENT_ACTION] = raw.intentAction;
ref[COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE] = raw.intentTargetPackage;
ref[COLUMN_INDEX_RAW_INTENT_TARGET_CLASS] = raw.intentTargetClass;
ref[COLUMN_INDEX_RAW_KEY] = raw.key;
ref[COLUMN_INDEX_RAW_USER_ID] = raw.userId;
return ref;
}
}
9.修改getInjectionIndexableRawData()方法:
private List<SearchIndexableRaw> getInjectionIndexableRawData(Context context) {
final DashboardFeatureProvider dashboardFeatureProvider =
FeatureFactory.getFactory(context).getDashboardFeatureProvider(context);
final List<SearchIndexableRaw> rawList = new ArrayList<>();
final String currentPackageName = context.getPackageName();
for (DashboardCategory category : dashboardFeatureProvider.getAllCategories()) {
// add start 屏蔽某些App
if (category.key.equals(CategoryKey.CATEGORY_NETWORK) ||
category.key.equals(CategoryKey.CATEGORY_CONNECT) ||
category.key.equals(CategoryKey.CATEGORY_DEVICE) ||
category.key.equals(CategoryKey.CATEGORY_BATTERY) ||
category.key.equals(CategoryKey.CATEGORY_STORAGE)) {
continue;
}
//add end
for (Tile tile : category.getTiles()) {
if (!isEligibleForIndexing(currentPackageName, tile)) {
continue;
}
final SearchIndexableRaw raw = new SearchIndexableRaw(context);
final CharSequence title = tile.getTitle(context);
raw.title = TextUtils.isEmpty(title) ? null : title.toString();
if (TextUtils.isEmpty(raw.title)) {
continue;
}
raw.key = dashboardFeatureProvider.getDashboardKeyForTile(tile);
final CharSequence summary = tile.getSummary(context);
raw.summaryOn = TextUtils.isEmpty(summary) ? null : summary.toString();
raw.summaryOff = raw.summaryOn;
raw.className = CATEGORY_KEY_TO_PARENT_MAP.get(tile.getCategory());
rawList.add(raw);
}
}
return rawList;
}
10.修改isEligibleForIndexing方法:
@VisibleForTesting
boolean isEligibleForIndexing(String packageName, Tile tile) {
// 屏蔽特定 Tile
if (tile.getCategory().equals(CategoryKey.CATEGORY_NETWORK) ||
tile.getCategory().equals(CategoryKey.CATEGORY_CONNECT) ||
tile.getCategory().equals(CategoryKey.CATEGORY_DEVICE) ||
tile.getCategory().equals(CategoryKey.CATEGORY_BATTERY) ||
tile.getCategory().equals(CategoryKey.CATEGORY_STORAGE)) {
return false;
}
if (TextUtils.equals(packageName, tile.getPackageName())
&& tile instanceof ActivityTile) {
// Skip Settings injected items because they should be indexed in the sub-pages.
return false;
}
if (TextUtils.equals(tile.getCategory(), CategoryKey.CATEGORY_HOMEPAGE)) {
// Skip homepage injected items since we would like to index their target activity.
return false;
}
return true;
}
11.实现效果如下:
12.总结:
从上面的截图可以看到不管是搜索关键字还是具体的App,都没有出现网络和互联网、已连接的设备、存储、电池四个App的入口,基本上这几个App不会在Setting是搜索中出现,至此上面的问题得到解决。
- 找到问题需要多多查看源码,然后针对性进行修改, 打包镜像进行验证
- 在Setting中搜索前面几个App,基本上都不会出现
- 修改几个核心类和功能,然后进行调试验证
- 这些要屏蔽的App可以根据需求来实现,但是如果关键字搜索出来 太多App入口,没必要全都屏蔽,这样修改的话后期如果不需要屏蔽改动太多,所以建议修改几个主要的App搜索入口即可.