Add A11y Slices
Add AccessibilityPreferenceController, which wraps all a11y settings since they are share common infrastructure for enabling, current value, and availability. We add an overlay for OEMs to declare their bundled a11y services. This is the only list of services that will be possible to enabled via Settings slices. Accessibility Slices are built by getting a list of valid services, and indexing the service names as a key in the Slices DB. When they are built at runtime, they use the generic A11yPrefController to get the status and enable/disable the service. Bug: 67997836 Bug: 67997672 Test: robotests Change-Id: I66f905bf1c55eecb937945c4675c12bcbc96d698
This commit is contained in:
@@ -148,7 +148,7 @@ public class SettingsSliceProvider extends SliceProvider {
|
||||
void loadSlice(Uri uri) {
|
||||
long startBuildTime = System.currentTimeMillis();
|
||||
|
||||
SliceData sliceData = mSlicesDatabaseAccessor.getSliceDataFromUri(uri);
|
||||
final SliceData sliceData = mSlicesDatabaseAccessor.getSliceDataFromUri(uri);
|
||||
mSliceDataCache.put(uri, sliceData);
|
||||
getContext().getContentResolver().notifyChange(uri, null /* content observer */);
|
||||
|
||||
|
||||
@@ -113,13 +113,13 @@ public class SliceBuilderUtils {
|
||||
* - key
|
||||
* <p>
|
||||
* Examples of valid paths are:
|
||||
* - intent/wifi
|
||||
* - intent/bluetooth
|
||||
* - action/wifi
|
||||
* - action/accessibility/servicename
|
||||
* - /intent/wifi
|
||||
* - /intent/bluetooth
|
||||
* - /action/wifi
|
||||
* - /action/accessibility/servicename
|
||||
*
|
||||
* @param uri of the Slice. Follows pattern outlined in {@link SettingsSliceProvider}.
|
||||
* @return Pair whose first element {@code true} if the path is prepended with "action", and
|
||||
* @return Pair whose first element {@code true} if the path is prepended with "intent", and
|
||||
* second is a key.
|
||||
*/
|
||||
public static Pair<Boolean, String> getPathData(Uri uri) {
|
||||
@@ -133,10 +133,10 @@ public class SliceBuilderUtils {
|
||||
throw new IllegalArgumentException("Uri (" + uri + ") has incomplete path: " + path);
|
||||
}
|
||||
|
||||
final boolean isInline = TextUtils.equals(SettingsSlicesContract.PATH_SETTING_ACTION,
|
||||
final boolean isIntent = TextUtils.equals(SettingsSlicesContract.PATH_SETTING_INTENT,
|
||||
split[1]);
|
||||
|
||||
return new Pair<>(isInline, split[2]);
|
||||
return new Pair<>(isIntent, split[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,8 +215,8 @@ public class SliceBuilderUtils {
|
||||
static Intent getContentIntent(Context context, SliceData sliceData) {
|
||||
final Uri contentUri = new Uri.Builder().appendPath(sliceData.getKey()).build();
|
||||
final Intent intent = DatabaseIndexingUtils.buildSearchResultPageIntent(context,
|
||||
sliceData.getFragmentClassName(), sliceData.getKey(), sliceData.getScreenTitle(),
|
||||
0 /* TODO */);
|
||||
sliceData.getFragmentClassName(), sliceData.getKey(),
|
||||
sliceData.getScreenTitle().toString(), 0 /* TODO */);
|
||||
intent.setClassName(context.getPackageName(), SubSettings.class.getName());
|
||||
intent.setData(contentUri);
|
||||
return intent;
|
||||
|
||||
@@ -57,7 +57,7 @@ public class SliceData {
|
||||
|
||||
private final String mSummary;
|
||||
|
||||
private final String mScreenTitle;
|
||||
private final CharSequence mScreenTitle;
|
||||
|
||||
private final int mIconResource;
|
||||
|
||||
@@ -84,7 +84,7 @@ public class SliceData {
|
||||
return mSummary;
|
||||
}
|
||||
|
||||
public String getScreenTitle() {
|
||||
public CharSequence getScreenTitle() {
|
||||
return mScreenTitle;
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ public class SliceData {
|
||||
|
||||
private String mSummary;
|
||||
|
||||
private String mScreenTitle;
|
||||
private CharSequence mScreenTitle;
|
||||
|
||||
private int mIconResource;
|
||||
|
||||
@@ -175,7 +175,7 @@ public class SliceData {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setScreenTitle(String screenTitle) {
|
||||
public Builder setScreenTitle(CharSequence screenTitle) {
|
||||
mScreenTitle = screenTitle;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,12 @@ import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_PLATFO
|
||||
import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_SUMMARY;
|
||||
import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_TITLE;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.os.Bundle;
|
||||
@@ -32,9 +37,14 @@ import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.Xml;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import com.android.settings.accessibility.AccessibilitySlicePreferenceController;
|
||||
import com.android.settings.core.PreferenceXmlParserUtils;
|
||||
import com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.accessibility.AccessibilitySettings;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.search.DatabaseIndexingUtils;
|
||||
@@ -46,10 +56,16 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Converts {@link DashboardFragment} to {@link SliceData}.
|
||||
* Converts all Slice sources into {@link SliceData}.
|
||||
* This includes:
|
||||
* - All {@link DashboardFragment DashboardFragments} indexed by settings search
|
||||
* - Accessibility services
|
||||
*/
|
||||
class SliceDataConverter {
|
||||
|
||||
@@ -101,6 +117,8 @@ class SliceDataConverter {
|
||||
mSliceData.addAll(providerSliceData);
|
||||
}
|
||||
|
||||
final List<SliceData> a11ySliceData = getAccessibilitySliceData();
|
||||
mSliceData.addAll(a11ySliceData);
|
||||
return mSliceData;
|
||||
}
|
||||
|
||||
@@ -208,4 +226,58 @@ class SliceDataConverter {
|
||||
}
|
||||
return xmlSliceData;
|
||||
}
|
||||
|
||||
private List<SliceData> getAccessibilitySliceData() {
|
||||
final List<SliceData> sliceData = new ArrayList<>();
|
||||
|
||||
final String accessibilityControllerClassName =
|
||||
AccessibilitySlicePreferenceController.class.getName();
|
||||
final String fragmentClassName = AccessibilitySettings.class.getName();
|
||||
final CharSequence screenTitle = mContext.getText(R.string.accessibility_settings);
|
||||
|
||||
final SliceData.Builder sliceDataBuilder = new SliceData.Builder()
|
||||
.setFragmentName(fragmentClassName)
|
||||
.setScreenTitle(screenTitle)
|
||||
.setPreferenceControllerClassName(accessibilityControllerClassName);
|
||||
|
||||
final Set<String> a11yServiceNames = new HashSet<>();
|
||||
Collections.addAll(a11yServiceNames, mContext.getResources()
|
||||
.getStringArray(R.array.config_settings_slices_accessibility_components));
|
||||
final List<AccessibilityServiceInfo> installedServices = getAccessibilityServiceInfoList();
|
||||
final PackageManager packageManager = mContext.getPackageManager();
|
||||
|
||||
for (AccessibilityServiceInfo a11yServiceInfo : installedServices) {
|
||||
final ResolveInfo resolveInfo = a11yServiceInfo.getResolveInfo();
|
||||
final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
|
||||
final String packageName = serviceInfo.packageName;
|
||||
final ComponentName componentName = new ComponentName(packageName, serviceInfo.name);
|
||||
final String flattenedName = componentName.flattenToString();
|
||||
|
||||
if (!a11yServiceNames.contains(flattenedName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final String title = resolveInfo.loadLabel(packageManager).toString();
|
||||
int iconResource = resolveInfo.getIconResource();
|
||||
if (iconResource == 0) {
|
||||
iconResource = R.mipmap.ic_accessibility_generic;
|
||||
}
|
||||
|
||||
sliceDataBuilder.setKey(flattenedName)
|
||||
.setTitle(title)
|
||||
.setIcon(iconResource)
|
||||
.setSliceType(SliceData.SliceType.SWITCH);
|
||||
|
||||
sliceData.add(sliceDataBuilder.build());
|
||||
}
|
||||
|
||||
return sliceData;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
List<AccessibilityServiceInfo> getAccessibilityServiceInfoList() {
|
||||
final AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(
|
||||
mContext);
|
||||
return accessibilityManager.getInstalledAccessibilityServiceList();
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ public class SlicesDatabaseAccessor {
|
||||
*/
|
||||
public SliceData getSliceDataFromKey(String key) {
|
||||
Cursor cursor = getIndexedSliceData(key);
|
||||
return buildSliceData(cursor, null /* uri */, false /* isInlineOnly */);
|
||||
return buildSliceData(cursor, null /* uri */, false /* isIntentOnly */);
|
||||
}
|
||||
|
||||
private Cursor getIndexedSliceData(String path) {
|
||||
@@ -111,7 +111,7 @@ public class SlicesDatabaseAccessor {
|
||||
.toString();
|
||||
}
|
||||
|
||||
private SliceData buildSliceData(Cursor cursor, Uri uri, boolean isInlineOnly) {
|
||||
private SliceData buildSliceData(Cursor cursor, Uri uri, boolean isIntentOnly) {
|
||||
final String key = cursor.getString(cursor.getColumnIndex(IndexColumns.KEY));
|
||||
final String title = cursor.getString(cursor.getColumnIndex(IndexColumns.TITLE));
|
||||
final String summary = cursor.getString(cursor.getColumnIndex(IndexColumns.SUMMARY));
|
||||
@@ -127,7 +127,7 @@ public class SlicesDatabaseAccessor {
|
||||
int sliceType = cursor.getInt(
|
||||
cursor.getColumnIndex(IndexColumns.SLICE_TYPE));
|
||||
|
||||
if (!isInlineOnly) {
|
||||
if (isIntentOnly) {
|
||||
sliceType = SliceData.SliceType.INTENT;
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ class SlicesIndexer implements Runnable {
|
||||
values.put(IndexColumns.KEY, dataRow.getKey());
|
||||
values.put(IndexColumns.TITLE, dataRow.getTitle());
|
||||
values.put(IndexColumns.SUMMARY, dataRow.getSummary());
|
||||
values.put(IndexColumns.SCREENTITLE, dataRow.getScreenTitle());
|
||||
values.put(IndexColumns.SCREENTITLE, dataRow.getScreenTitle().toString());
|
||||
values.put(IndexColumns.ICON_RESOURCE, dataRow.getIconResource());
|
||||
values.put(IndexColumns.FRAGMENT, dataRow.getFragmentClassName());
|
||||
values.put(IndexColumns.CONTROLLER, dataRow.getPreferenceController());
|
||||
|
||||
Reference in New Issue
Block a user