The root cause is that androidx.preference.Preference does not implement equals and hashCode methods, but it is used as Map key (see bug comment2 for more details). Given that Preference.getParent() can find the category, we can simplify the data structure from Map to List. Bug: 388696327 Flag: EXEMPT bugfix Test: Resume/Pause Accessibility screen 100 times Change-Id: Ib70acbf2147048730f8a4e8fd66731f9efdadecf
615 lines
26 KiB
Java
615 lines
26 KiB
Java
/*
|
|
* 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.accessibility;
|
|
|
|
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.DEFAULT;
|
|
|
|
import android.accessibilityservice.AccessibilityServiceInfo;
|
|
import android.accessibilityservice.AccessibilityShortcutInfo;
|
|
import android.app.settings.SettingsEnums;
|
|
import android.content.ComponentName;
|
|
import android.content.Context;
|
|
import android.hardware.input.InputManager;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.os.UserHandle;
|
|
import android.provider.Settings;
|
|
import android.text.TextUtils;
|
|
import android.util.ArrayMap;
|
|
import android.view.accessibility.AccessibilityManager;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.VisibleForTesting;
|
|
import androidx.preference.Preference;
|
|
import androidx.preference.PreferenceCategory;
|
|
|
|
import com.android.internal.accessibility.AccessibilityShortcutController;
|
|
import com.android.internal.accessibility.util.AccessibilityUtils;
|
|
import com.android.internal.content.PackageMonitor;
|
|
import com.android.settings.R;
|
|
import com.android.settings.accessibility.AccessibilityUtil.AccessibilityServiceFragmentType;
|
|
import com.android.settings.dashboard.DashboardFragment;
|
|
import com.android.settings.overlay.FeatureFactory;
|
|
import com.android.settings.search.BaseSearchIndexProvider;
|
|
import com.android.settingslib.RestrictedPreference;
|
|
import com.android.settingslib.core.AbstractPreferenceController;
|
|
import com.android.settingslib.search.SearchIndexable;
|
|
import com.android.settingslib.search.SearchIndexableRaw;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
/** Activity with the accessibility settings. */
|
|
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
|
|
public class AccessibilitySettings extends DashboardFragment implements
|
|
InputManager.InputDeviceListener {
|
|
|
|
private static final String TAG = "AccessibilitySettings";
|
|
|
|
// Preference categories
|
|
private static final String CATEGORY_SCREEN_READER = "screen_reader_category";
|
|
private static final String CATEGORY_CAPTIONS = "captions_category";
|
|
private static final String CATEGORY_AUDIO = "audio_category";
|
|
private static final String CATEGORY_SPEECH = "speech_category";
|
|
private static final String CATEGORY_DISPLAY = "display_category";
|
|
@VisibleForTesting
|
|
static final String CATEGORY_DOWNLOADED_SERVICES = "user_installed_services_category";
|
|
@VisibleForTesting
|
|
static final String CATEGORY_INTERACTION_CONTROL = "interaction_control_category";
|
|
|
|
private static final String[] CATEGORIES = new String[]{
|
|
CATEGORY_SCREEN_READER, CATEGORY_CAPTIONS, CATEGORY_AUDIO, CATEGORY_DISPLAY,
|
|
CATEGORY_SPEECH, CATEGORY_INTERACTION_CONTROL, CATEGORY_DOWNLOADED_SERVICES
|
|
};
|
|
|
|
// Extras passed to sub-fragments.
|
|
static final String EXTRA_PREFERENCE_KEY = "preference_key";
|
|
static final String EXTRA_CHECKED = "checked";
|
|
static final String EXTRA_TITLE = "title";
|
|
static final String EXTRA_RESOLVE_INFO = "resolve_info";
|
|
static final String EXTRA_SUMMARY = "summary";
|
|
static final String EXTRA_INTRO = "intro";
|
|
static final String EXTRA_SETTINGS_TITLE = "settings_title";
|
|
static final String EXTRA_COMPONENT_NAME = "component_name";
|
|
static final String EXTRA_SETTINGS_COMPONENT_NAME = "settings_component_name";
|
|
static final String EXTRA_TILE_SERVICE_COMPONENT_NAME = "tile_service_component_name";
|
|
static final String EXTRA_LAUNCHED_FROM_SUW = "from_suw";
|
|
static final String EXTRA_ANIMATED_IMAGE_RES = "animated_image_res";
|
|
static final String EXTRA_HTML_DESCRIPTION = "html_description";
|
|
static final String EXTRA_TIME_FOR_LOGGING = "start_time_to_log_a11y_tool";
|
|
static final String EXTRA_METRICS_CATEGORY = "metrics_category";
|
|
|
|
// Timeout before we update the services if packages are added/removed
|
|
// since the AccessibilityManagerService has to do that processing first
|
|
// to generate the AccessibilityServiceInfo we need for proper
|
|
// presentation.
|
|
private static final long DELAY_UPDATE_SERVICES_MILLIS = 1000;
|
|
|
|
private final Handler mHandler = new Handler();
|
|
|
|
private final Runnable mUpdateRunnable = new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (getActivity() != null) {
|
|
onContentChanged();
|
|
}
|
|
}
|
|
};
|
|
|
|
private final PackageMonitor mSettingsPackageMonitor = new PackageMonitor() {
|
|
@Override
|
|
public void onPackageAdded(String packageName, int uid) {
|
|
sendUpdate();
|
|
}
|
|
|
|
@Override
|
|
public void onPackageModified(@NonNull String packageName) {
|
|
sendUpdate();
|
|
}
|
|
|
|
@Override
|
|
public void onPackageAppeared(String packageName, int reason) {
|
|
sendUpdate();
|
|
}
|
|
|
|
@Override
|
|
public void onPackageDisappeared(String packageName, int reason) {
|
|
sendUpdate();
|
|
}
|
|
|
|
@Override
|
|
public void onPackageRemoved(String packageName, int uid) {
|
|
sendUpdate();
|
|
}
|
|
|
|
private void sendUpdate() {
|
|
mHandler.postDelayed(mUpdateRunnable, DELAY_UPDATE_SERVICES_MILLIS);
|
|
}
|
|
};
|
|
|
|
@VisibleForTesting
|
|
AccessibilitySettingsContentObserver mSettingsContentObserver;
|
|
|
|
private final Map<String, PreferenceCategory> mCategoryToPrefCategoryMap =
|
|
new ArrayMap<>();
|
|
private final List<Preference> mServicePreferences = new ArrayList<>();
|
|
private final Map<ComponentName, PreferenceCategory> mPreBundledServiceComponentToCategoryMap =
|
|
new ArrayMap<>();
|
|
|
|
private boolean mNeedPreferencesUpdate = false;
|
|
private boolean mIsForeground = true;
|
|
|
|
public AccessibilitySettings() {
|
|
mSettingsContentObserver = new AccessibilitySettingsContentObserver(mHandler);
|
|
}
|
|
|
|
private void initializeSettingsContentObserver() {
|
|
// Observe changes to anything that the shortcut can toggle, so we can reflect updates
|
|
final Collection<AccessibilityShortcutController.FrameworkFeatureInfo> features =
|
|
AccessibilityShortcutController
|
|
.getFrameworkShortcutFeaturesMap().values();
|
|
final List<String> shortcutFeatureKeys = new ArrayList<>(features.size());
|
|
for (AccessibilityShortcutController.FrameworkFeatureInfo feature : features) {
|
|
final String key = feature.getSettingKey();
|
|
if (key != null) {
|
|
shortcutFeatureKeys.add(key);
|
|
}
|
|
}
|
|
|
|
// Observe changes from accessibility selection menu
|
|
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
|
|
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
|
|
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS);
|
|
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_STICKY_KEYS);
|
|
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SLOW_KEYS);
|
|
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS);
|
|
mSettingsContentObserver.registerKeysToObserverCallback(shortcutFeatureKeys,
|
|
key -> onContentChanged());
|
|
}
|
|
|
|
@Override
|
|
public int getMetricsCategory() {
|
|
return SettingsEnums.ACCESSIBILITY;
|
|
}
|
|
|
|
@Override
|
|
public int getHelpResource() {
|
|
return R.string.help_uri_accessibility;
|
|
}
|
|
|
|
@Override
|
|
public void onAttach(Context context) {
|
|
super.onAttach(context);
|
|
use(AccessibilityHearingAidPreferenceController.class)
|
|
.setFragmentManager(getFragmentManager());
|
|
}
|
|
|
|
@Override
|
|
public void onCreate(Bundle icicle) {
|
|
super.onCreate(icicle);
|
|
initializeSettingsContentObserver();
|
|
initializeAllPreferences();
|
|
updateAllPreferences();
|
|
mNeedPreferencesUpdate = false;
|
|
registerContentMonitors();
|
|
registerInputDeviceListener();
|
|
}
|
|
|
|
@Override
|
|
public void onStart() {
|
|
super.onStart();
|
|
mIsForeground = true;
|
|
}
|
|
|
|
@Override
|
|
public void onResume() {
|
|
super.onResume();
|
|
if (mNeedPreferencesUpdate) {
|
|
updateAllPreferences();
|
|
mNeedPreferencesUpdate = false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onPause() {
|
|
super.onPause();
|
|
mNeedPreferencesUpdate = true;
|
|
}
|
|
|
|
@Override
|
|
public void onStop() {
|
|
mIsForeground = false;
|
|
super.onStop();
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy() {
|
|
unregisterContentMonitors();
|
|
unRegisterInputDeviceListener();
|
|
super.onDestroy();
|
|
}
|
|
|
|
@Override
|
|
protected int getPreferenceScreenResId() {
|
|
return R.xml.accessibility_settings;
|
|
}
|
|
|
|
@Override
|
|
protected String getLogTag() {
|
|
return TAG;
|
|
}
|
|
|
|
/**
|
|
* Returns the summary for the current state of this accessibilityService.
|
|
*
|
|
* @param context A valid context
|
|
* @param info The accessibilityService's info
|
|
* @param serviceEnabled Whether the accessibility service is enabled.
|
|
* @return The service summary
|
|
*/
|
|
public static CharSequence getServiceSummary(Context context, AccessibilityServiceInfo info,
|
|
boolean serviceEnabled) {
|
|
if (serviceEnabled && info.crashed) {
|
|
return context.getText(R.string.accessibility_summary_state_stopped);
|
|
}
|
|
|
|
final CharSequence serviceState;
|
|
final int fragmentType = AccessibilityUtil.getAccessibilityServiceFragmentType(info);
|
|
if (fragmentType == AccessibilityServiceFragmentType.INVISIBLE_TOGGLE) {
|
|
final ComponentName componentName = new ComponentName(
|
|
info.getResolveInfo().serviceInfo.packageName,
|
|
info.getResolveInfo().serviceInfo.name);
|
|
final boolean shortcutEnabled = AccessibilityUtil.getUserShortcutTypesFromSettings(
|
|
context, componentName) != DEFAULT;
|
|
serviceState = shortcutEnabled
|
|
? context.getText(R.string.accessibility_summary_shortcut_enabled)
|
|
: context.getText(R.string.generic_accessibility_feature_shortcut_off);
|
|
} else {
|
|
serviceState = serviceEnabled
|
|
? context.getText(R.string.generic_accessibility_service_on)
|
|
: context.getText(R.string.generic_accessibility_service_off);
|
|
}
|
|
|
|
final CharSequence serviceSummary = info.loadSummary(context.getPackageManager());
|
|
final String stateSummaryCombo = context.getString(
|
|
com.android.settingslib.R.string.preference_summary_default_combination,
|
|
serviceState, serviceSummary);
|
|
|
|
return TextUtils.isEmpty(serviceSummary) ? serviceState : stateSummaryCombo;
|
|
}
|
|
|
|
/**
|
|
* Returns the description for the current state of this accessibilityService.
|
|
*
|
|
* @param context A valid context
|
|
* @param info The accessibilityService's info
|
|
* @param serviceEnabled Whether the accessibility service is enabled.
|
|
* @return The service description
|
|
*/
|
|
public static CharSequence getServiceDescription(Context context, AccessibilityServiceInfo info,
|
|
boolean serviceEnabled) {
|
|
if (serviceEnabled && info.crashed) {
|
|
return context.getText(R.string.accessibility_description_state_stopped);
|
|
}
|
|
|
|
return info.loadDescription(context.getPackageManager());
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void onContentChanged() {
|
|
// If the fragment is visible then update preferences immediately, else set the flag then
|
|
// wait for the fragment to show up to update preferences.
|
|
if (mIsForeground) {
|
|
updateAllPreferences();
|
|
} else {
|
|
mNeedPreferencesUpdate = true;
|
|
}
|
|
}
|
|
|
|
private void initializeAllPreferences() {
|
|
for (int i = 0; i < CATEGORIES.length; i++) {
|
|
PreferenceCategory prefCategory = findPreference(CATEGORIES[i]);
|
|
mCategoryToPrefCategoryMap.put(CATEGORIES[i], prefCategory);
|
|
}
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void updateAllPreferences() {
|
|
updateServicePreferences();
|
|
updatePreferencesState();
|
|
updateSystemPreferences();
|
|
}
|
|
|
|
private void registerContentMonitors() {
|
|
final Context context = getActivity();
|
|
|
|
mSettingsPackageMonitor.register(context, context.getMainLooper(), /* externalStorage= */
|
|
false);
|
|
mSettingsContentObserver.register(getContentResolver());
|
|
}
|
|
|
|
private void registerInputDeviceListener() {
|
|
InputManager mIm = getSystemService(InputManager.class);
|
|
if (mIm == null) {
|
|
return;
|
|
}
|
|
mIm.registerInputDeviceListener(this, null);
|
|
}
|
|
|
|
private void unRegisterInputDeviceListener() {
|
|
InputManager mIm = getSystemService(InputManager.class);
|
|
if (mIm == null) {
|
|
return;
|
|
}
|
|
mIm.unregisterInputDeviceListener(this);
|
|
}
|
|
|
|
private void unregisterContentMonitors() {
|
|
mSettingsPackageMonitor.unregister();
|
|
mSettingsContentObserver.unregister(getContentResolver());
|
|
}
|
|
|
|
protected void updateServicePreferences() {
|
|
final AccessibilityManager a11yManager = AccessibilityManager.getInstance(getPrefContext());
|
|
// Since services category is auto generated we have to do a pass
|
|
// to generate it since services can come and go and then based on
|
|
// the global accessibility state to decided whether it is enabled.
|
|
for (Preference service : mServicePreferences) {
|
|
service.getParent().removePreference(service);
|
|
}
|
|
mServicePreferences.clear();
|
|
|
|
initializePreBundledServicesMapFromArray(CATEGORY_SCREEN_READER,
|
|
R.array.config_preinstalled_screen_reader_services);
|
|
initializePreBundledServicesMapFromArray(CATEGORY_CAPTIONS,
|
|
R.array.config_preinstalled_captions_services);
|
|
initializePreBundledServicesMapFromArray(CATEGORY_AUDIO,
|
|
R.array.config_preinstalled_audio_services);
|
|
initializePreBundledServicesMapFromArray(CATEGORY_DISPLAY,
|
|
R.array.config_preinstalled_display_services);
|
|
initializePreBundledServicesMapFromArray(CATEGORY_SPEECH,
|
|
R.array.config_preinstalled_speech_services);
|
|
initializePreBundledServicesMapFromArray(CATEGORY_INTERACTION_CONTROL,
|
|
R.array.config_preinstalled_interaction_control_services);
|
|
|
|
// ACCESSIBILITY_MENU_IN_SYSTEM is a default pre-bundled interaction control service.
|
|
// If the device opts out of including this service then this is a no-op.
|
|
mPreBundledServiceComponentToCategoryMap.put(
|
|
AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM,
|
|
mCategoryToPrefCategoryMap.get(CATEGORY_INTERACTION_CONTROL));
|
|
|
|
final List<AccessibilityShortcutInfo> installedShortcutList =
|
|
a11yManager.getInstalledAccessibilityShortcutListAsUser(getPrefContext(),
|
|
UserHandle.myUserId());
|
|
final List<AccessibilityServiceInfo> installedServiceList =
|
|
a11yManager.getInstalledAccessibilityServiceList();
|
|
final List<RestrictedPreference> preferenceList = getInstalledAccessibilityPreferences(
|
|
getPrefContext(), installedShortcutList, installedServiceList);
|
|
|
|
removeNonPreinstalledComponents(mPreBundledServiceComponentToCategoryMap,
|
|
installedShortcutList, installedServiceList);
|
|
|
|
final PreferenceCategory downloadedServicesCategory =
|
|
mCategoryToPrefCategoryMap.get(CATEGORY_DOWNLOADED_SERVICES);
|
|
|
|
for (int i = 0, count = preferenceList.size(); i < count; ++i) {
|
|
final RestrictedPreference preference = preferenceList.get(i);
|
|
final ComponentName componentName = preference.getExtras().getParcelable(
|
|
EXTRA_COMPONENT_NAME);
|
|
PreferenceCategory prefCategory = downloadedServicesCategory;
|
|
// Set the appropriate category if the service comes pre-installed.
|
|
if (mPreBundledServiceComponentToCategoryMap.containsKey(componentName)) {
|
|
prefCategory = mPreBundledServiceComponentToCategoryMap.get(componentName);
|
|
}
|
|
prefCategory.addPreference(preference);
|
|
mServicePreferences.add(preference);
|
|
}
|
|
|
|
// Update the order of all the category according to the order defined in xml file.
|
|
updateCategoryOrderFromArray(CATEGORY_SCREEN_READER,
|
|
R.array.config_order_screen_reader_services);
|
|
updateCategoryOrderFromArray(CATEGORY_CAPTIONS,
|
|
R.array.config_order_captions_services);
|
|
updateCategoryOrderFromArray(CATEGORY_AUDIO,
|
|
R.array.config_order_audio_services);
|
|
updateCategoryOrderFromArray(CATEGORY_INTERACTION_CONTROL,
|
|
R.array.config_order_interaction_control_services);
|
|
updateCategoryOrderFromArray(CATEGORY_DISPLAY,
|
|
R.array.config_order_display_services);
|
|
updateCategoryOrderFromArray(CATEGORY_SPEECH,
|
|
R.array.config_order_speech_services);
|
|
|
|
// Need to check each time when updateServicePreferences() called.
|
|
if (downloadedServicesCategory.getPreferenceCount() == 0) {
|
|
getPreferenceScreen().removePreference(downloadedServicesCategory);
|
|
} else {
|
|
getPreferenceScreen().addPreference(downloadedServicesCategory);
|
|
}
|
|
|
|
// Hide category if it is empty.
|
|
updatePreferenceCategoryVisibility(CATEGORY_SCREEN_READER);
|
|
updatePreferenceCategoryVisibility(CATEGORY_SPEECH);
|
|
}
|
|
|
|
/**
|
|
* Gets a list of {@link RestrictedPreference}s for the provided a11y shortcuts and services.
|
|
*
|
|
* <p>{@code modifiableInstalledServiceList} may be modified to remove any entries with
|
|
* matching package name and label as an entry in {@code installedShortcutList}.
|
|
*
|
|
* @param installedShortcutList A list of installed {@link AccessibilityShortcutInfo}s.
|
|
* @param installedServiceList A list of installed {@link AccessibilityServiceInfo}s.
|
|
*/
|
|
private static List<RestrictedPreference> getInstalledAccessibilityPreferences(Context context,
|
|
List<AccessibilityShortcutInfo> installedShortcutList,
|
|
List<AccessibilityServiceInfo> installedServiceList) {
|
|
final RestrictedPreferenceHelper preferenceHelper = new RestrictedPreferenceHelper(context);
|
|
|
|
final List<AccessibilityActivityPreference> activityList =
|
|
preferenceHelper.createAccessibilityActivityPreferenceList(installedShortcutList);
|
|
final List<RestrictedPreference> serviceList =
|
|
preferenceHelper.createAccessibilityServicePreferenceList(installedServiceList);
|
|
|
|
final List<RestrictedPreference> preferenceList = new ArrayList<>();
|
|
preferenceList.addAll(activityList);
|
|
preferenceList.addAll(serviceList);
|
|
|
|
return preferenceList;
|
|
}
|
|
|
|
private static void removeNonPreinstalledComponents(
|
|
Map<ComponentName, PreferenceCategory> componentToCategory,
|
|
List<AccessibilityShortcutInfo> shortcutInfos,
|
|
List<AccessibilityServiceInfo> serviceInfos) {
|
|
for (AccessibilityShortcutInfo info : shortcutInfos) {
|
|
if (!info.getActivityInfo().applicationInfo.isSystemApp()) {
|
|
componentToCategory.remove(info.getComponentName());
|
|
}
|
|
}
|
|
for (AccessibilityServiceInfo info : serviceInfos) {
|
|
if (!info.getResolveInfo().serviceInfo.applicationInfo.isSystemApp()) {
|
|
componentToCategory.remove(info.getComponentName());
|
|
}
|
|
}
|
|
}
|
|
|
|
private void initializePreBundledServicesMapFromArray(String categoryKey, int key) {
|
|
String[] services = getResources().getStringArray(key);
|
|
PreferenceCategory category = mCategoryToPrefCategoryMap.get(categoryKey);
|
|
for (int i = 0; i < services.length; i++) {
|
|
ComponentName component = ComponentName.unflattenFromString(services[i]);
|
|
mPreBundledServiceComponentToCategoryMap.put(component, category);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update the order of preferences in the category by matching their preference
|
|
* key with the string array of preference order which is defined in the xml.
|
|
*
|
|
* @param categoryKey The key of the category need to update the order
|
|
* @param key The key of the string array which defines the order of category
|
|
*/
|
|
private void updateCategoryOrderFromArray(String categoryKey, int key) {
|
|
String[] services = getResources().getStringArray(key);
|
|
PreferenceCategory category = mCategoryToPrefCategoryMap.get(categoryKey);
|
|
int preferenceCount = category.getPreferenceCount();
|
|
int serviceLength = services.length;
|
|
for (int preferenceIndex = 0; preferenceIndex < preferenceCount; preferenceIndex++) {
|
|
for (int serviceIndex = 0; serviceIndex < serviceLength; serviceIndex++) {
|
|
if (category.getPreference(preferenceIndex).getKey()
|
|
.equals(services[serviceIndex])) {
|
|
category.getPreference(preferenceIndex).setOrder(serviceIndex);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the visibility of a category according to its child preference count.
|
|
*
|
|
* @param categoryKey The key of the category which needs to check
|
|
*/
|
|
private void updatePreferenceCategoryVisibility(String categoryKey) {
|
|
final PreferenceCategory category = mCategoryToPrefCategoryMap.get(categoryKey);
|
|
category.setVisible(category.getPreferenceCount() != 0);
|
|
}
|
|
|
|
/**
|
|
* Updates preferences related to system configurations.
|
|
*/
|
|
protected void updateSystemPreferences() {}
|
|
|
|
private void updatePreferencesState() {
|
|
final List<AbstractPreferenceController> controllers = new ArrayList<>();
|
|
getPreferenceControllers().forEach(controllers::addAll);
|
|
controllers.forEach(controller -> controller.updateState(
|
|
findPreference(controller.getPreferenceKey())));
|
|
}
|
|
|
|
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
|
new BaseSearchIndexProvider(R.xml.accessibility_settings) {
|
|
@Override
|
|
public List<SearchIndexableRaw> getRawDataToIndex(Context context,
|
|
boolean enabled) {
|
|
return FeatureFactory.getFeatureFactory()
|
|
.getAccessibilitySearchFeatureProvider().getSearchIndexableRawData(
|
|
context);
|
|
}
|
|
|
|
@Override
|
|
public List<SearchIndexableRaw> getDynamicRawDataToIndex(Context context,
|
|
boolean enabled) {
|
|
List<SearchIndexableRaw> dynamicRawData = super.getDynamicRawDataToIndex(
|
|
context, enabled);
|
|
if (dynamicRawData == null) {
|
|
dynamicRawData = new ArrayList<>();
|
|
}
|
|
if (!Flags.fixA11ySettingsSearch()) {
|
|
return dynamicRawData;
|
|
}
|
|
|
|
AccessibilityManager a11yManager = context.getSystemService(
|
|
AccessibilityManager.class);
|
|
AccessibilitySearchFeatureProvider a11ySearchFeatureProvider =
|
|
FeatureFactory.getFeatureFactory()
|
|
.getAccessibilitySearchFeatureProvider();
|
|
List<RestrictedPreference> installedA11yFeaturesPref =
|
|
AccessibilitySettings.getInstalledAccessibilityPreferences(
|
|
context,
|
|
a11yManager.getInstalledAccessibilityShortcutListAsUser(
|
|
context, UserHandle.myUserId()),
|
|
a11yManager.getInstalledAccessibilityServiceList()
|
|
);
|
|
for (RestrictedPreference pref : installedA11yFeaturesPref) {
|
|
SearchIndexableRaw indexableRaw = new SearchIndexableRaw(context);
|
|
indexableRaw.key = pref.getKey();
|
|
indexableRaw.title = pref.getTitle().toString();
|
|
@NonNull String synonyms = "";
|
|
if (pref instanceof AccessibilityServicePreference) {
|
|
synonyms = a11ySearchFeatureProvider.getSynonymsForComponent(
|
|
context,
|
|
((AccessibilityServicePreference) pref).getComponentName());
|
|
} else if (pref instanceof AccessibilityActivityPreference) {
|
|
synonyms = a11ySearchFeatureProvider.getSynonymsForComponent(
|
|
context,
|
|
((AccessibilityActivityPreference) pref).getComponentName());
|
|
}
|
|
indexableRaw.keywords = synonyms;
|
|
dynamicRawData.add(indexableRaw);
|
|
}
|
|
|
|
return dynamicRawData;
|
|
}
|
|
};
|
|
|
|
@Override
|
|
public void onInputDeviceAdded(int deviceId) {
|
|
}
|
|
|
|
@Override
|
|
public void onInputDeviceRemoved(int deviceId) {
|
|
}
|
|
|
|
@Override
|
|
public void onInputDeviceChanged(int deviceId) {
|
|
mHandler.postDelayed(mUpdateRunnable, DELAY_UPDATE_SERVICES_MILLIS);
|
|
}
|
|
}
|