Merge "Support the rich content for accessibility service (1/n)"
This commit is contained in:
33
res/layout/preference_animated_image.xml
Normal file
33
res/layout/preference_animated_image.xml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2019 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:importantForAccessibility="noHideDescendants">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/animated_img"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:focusable="false"
|
||||||
|
android:clickable="false"
|
||||||
|
android:adjustViewBounds="true"/>
|
||||||
|
</FrameLayout>
|
65
res/layout/preference_static_text.xml
Normal file
65
res/layout/preference_static_text.xml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2019 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||||
|
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||||
|
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:clipToPadding="false">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/icon_frame"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minWidth="56dp"
|
||||||
|
android:paddingEnd="12dp"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingBottom="4dp">
|
||||||
|
<ImageView
|
||||||
|
android:id="@+android:id/icon"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingBottom="16dp"
|
||||||
|
android:paddingTop="16dp">
|
||||||
|
|
||||||
|
<TextView android:id="@+android:id/title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+android:id/summary"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@android:id/title"
|
||||||
|
android:layout_alignStart="@android:id/title"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"/>
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<LinearLayout android:id="@+android:id/widget_frame"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="vertical" />
|
||||||
|
</LinearLayout>
|
@@ -164,7 +164,10 @@ public class AccessibilityDetailsSettingsFragment extends InstrumentedFragment {
|
|||||||
new ComponentName(packageName, settingsClassName).flattenToString());
|
new ComponentName(packageName, settingsClassName).flattenToString());
|
||||||
}
|
}
|
||||||
extras.putParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME, componentName);
|
extras.putParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME, componentName);
|
||||||
|
extras.putInt(AccessibilitySettings.EXTRA_ANIMATED_IMAGE_RES, info.getAnimatedImageRes());
|
||||||
|
|
||||||
|
final String htmlDescription = info.loadHtmlDescription(getActivity().getPackageManager());
|
||||||
|
extras.putString(AccessibilitySettings.EXTRA_HTML_DESCRIPTION, htmlDescription);
|
||||||
return extras;
|
return extras;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -111,6 +111,8 @@ public class AccessibilitySettings extends DashboardFragment {
|
|||||||
static final String EXTRA_SETTINGS_COMPONENT_NAME = "settings_component_name";
|
static final String EXTRA_SETTINGS_COMPONENT_NAME = "settings_component_name";
|
||||||
static final String EXTRA_VIDEO_RAW_RESOURCE_ID = "video_resource";
|
static final String EXTRA_VIDEO_RAW_RESOURCE_ID = "video_resource";
|
||||||
static final String EXTRA_LAUNCHED_FROM_SUW = "from_suw";
|
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";
|
||||||
|
|
||||||
// Timeout before we update the services if packages are added/removed
|
// Timeout before we update the services if packages are added/removed
|
||||||
// since the AccessibilityManagerService has to do that processing first
|
// since the AccessibilityManagerService has to do that processing first
|
||||||
@@ -409,6 +411,10 @@ public class AccessibilitySettings extends DashboardFragment {
|
|||||||
extras.putString(EXTRA_TITLE, title);
|
extras.putString(EXTRA_TITLE, title);
|
||||||
extras.putParcelable(EXTRA_RESOLVE_INFO, resolveInfo);
|
extras.putParcelable(EXTRA_RESOLVE_INFO, resolveInfo);
|
||||||
extras.putString(EXTRA_SUMMARY, description);
|
extras.putString(EXTRA_SUMMARY, description);
|
||||||
|
extras.putInt(EXTRA_ANIMATED_IMAGE_RES, info.getAnimatedImageRes());
|
||||||
|
|
||||||
|
final String htmlDescription = info.loadHtmlDescription(getPackageManager());
|
||||||
|
extras.putString(AccessibilitySettings.EXTRA_HTML_DESCRIPTION, htmlDescription);
|
||||||
|
|
||||||
final String settingsClassName = info.getSettingsActivityName();
|
final String settingsClassName = info.getSettingsActivityName();
|
||||||
if (!TextUtils.isEmpty(settingsClassName)) {
|
if (!TextUtils.isEmpty(settingsClassName)) {
|
||||||
|
@@ -169,6 +169,9 @@ public class AccessibilitySettingsForSetupWizard extends SettingsPreferenceFragm
|
|||||||
description = getString(R.string.accessibility_service_default_description);
|
description = getString(R.string.accessibility_service_default_description);
|
||||||
}
|
}
|
||||||
extras.putString(AccessibilitySettings.EXTRA_SUMMARY, description);
|
extras.putString(AccessibilitySettings.EXTRA_SUMMARY, description);
|
||||||
|
|
||||||
|
final String htmlDescription = info.loadHtmlDescription(getPackageManager());
|
||||||
|
extras.putString(AccessibilitySettings.EXTRA_HTML_DESCRIPTION, htmlDescription);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void configureMagnificationPreferenceIfNeeded(Preference preference) {
|
private static void configureMagnificationPreferenceIfNeeded(Preference preference) {
|
||||||
|
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.accessibility;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.drawable.AnimatedImageDrawable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.preference.PreferenceViewHolder;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom {@link ImageView} preference for showing animated or static image, such as <a
|
||||||
|
* href="https://developers.google.com/speed/webp/">animated webp</a> and static png.
|
||||||
|
*/
|
||||||
|
public class AnimatedImagePreference extends Preference {
|
||||||
|
|
||||||
|
private boolean mDividerAllowedAbove = false;
|
||||||
|
private Uri mImageUri;
|
||||||
|
|
||||||
|
AnimatedImagePreference(Context context) {
|
||||||
|
super(context);
|
||||||
|
setLayoutResource(R.layout.preference_animated_image);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||||
|
super.onBindViewHolder(holder);
|
||||||
|
holder.setDividerAllowedAbove(mDividerAllowedAbove);
|
||||||
|
|
||||||
|
final ImageView imageView = holder.itemView.findViewById(R.id.animated_img);
|
||||||
|
if (imageView != null && mImageUri != null) {
|
||||||
|
imageView.setImageURI(mImageUri);
|
||||||
|
|
||||||
|
final Drawable drawable = imageView.getDrawable();
|
||||||
|
if (drawable != null) {
|
||||||
|
if (drawable instanceof AnimatedImageDrawable) {
|
||||||
|
((AnimatedImageDrawable) drawable).start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets divider whether to show in preference above.
|
||||||
|
*
|
||||||
|
* @param allowed true will be drawn on above this item
|
||||||
|
*/
|
||||||
|
public void setDividerAllowedAbove(boolean allowed) {
|
||||||
|
if (allowed != mDividerAllowedAbove) {
|
||||||
|
mDividerAllowedAbove = allowed;
|
||||||
|
notifyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set image uri to display image in {@link ImageView}
|
||||||
|
*
|
||||||
|
* @param imageUri the Uri of an image
|
||||||
|
*/
|
||||||
|
public void setImageUri(Uri imageUri) {
|
||||||
|
if (imageUri != null && !imageUri.equals(mImageUri)) {
|
||||||
|
mImageUri = imageUri;
|
||||||
|
notifyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
139
src/com/android/settings/accessibility/HtmlTextPreference.java
Normal file
139
src/com/android/settings/accessibility/HtmlTextPreference.java
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.accessibility;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.text.Html;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceViewHolder;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom {@link android.widget.TextView} preference that shows html text with a custom tag
|
||||||
|
* filter.
|
||||||
|
*/
|
||||||
|
public final class HtmlTextPreference extends StaticTextPreference {
|
||||||
|
|
||||||
|
private boolean mDividerAllowedAbove = false;
|
||||||
|
private int mFlag = Html.FROM_HTML_MODE_COMPACT;
|
||||||
|
private Html.ImageGetter mImageGetter;
|
||||||
|
private Html.TagHandler mTagHandler;
|
||||||
|
private List<String> mUnsupportedTagList;
|
||||||
|
|
||||||
|
HtmlTextPreference(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||||
|
super.onBindViewHolder(holder);
|
||||||
|
holder.setDividerAllowedAbove(mDividerAllowedAbove);
|
||||||
|
|
||||||
|
final TextView summaryView = holder.itemView.findViewById(android.R.id.summary);
|
||||||
|
if (summaryView != null && !TextUtils.isEmpty(getSummary())) {
|
||||||
|
final String filteredText = getFilteredText(getSummary().toString());
|
||||||
|
summaryView.setText(Html.fromHtml(filteredText, mFlag, mImageGetter, mTagHandler));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets divider whether to show in preference above.
|
||||||
|
*
|
||||||
|
* @param allowed true will be drawn on above this item
|
||||||
|
*/
|
||||||
|
public void setDividerAllowedAbove(boolean allowed) {
|
||||||
|
if (allowed != mDividerAllowedAbove) {
|
||||||
|
mDividerAllowedAbove = allowed;
|
||||||
|
notifyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the flag to which text format to be applied.
|
||||||
|
*
|
||||||
|
* @param flag to indicate that html text format
|
||||||
|
*/
|
||||||
|
public void setFlag(int flag) {
|
||||||
|
if (flag != mFlag) {
|
||||||
|
mFlag = flag;
|
||||||
|
notifyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets image getter and help to load corresponding images when parsing.
|
||||||
|
*
|
||||||
|
* @param imageGetter to load image by image tag content
|
||||||
|
*/
|
||||||
|
public void setImageGetter(Html.ImageGetter imageGetter) {
|
||||||
|
if (imageGetter != null && !imageGetter.equals(mImageGetter)) {
|
||||||
|
mImageGetter = imageGetter;
|
||||||
|
notifyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets tag handler to handle the unsupported tags.
|
||||||
|
*
|
||||||
|
* @param tagHandler the handler for unhandled tags
|
||||||
|
*/
|
||||||
|
public void setTagHandler(Html.TagHandler tagHandler) {
|
||||||
|
if (tagHandler != null && !tagHandler.equals(mTagHandler)) {
|
||||||
|
mTagHandler = tagHandler;
|
||||||
|
notifyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets unsupported tag list, the text will be filtered though this list in advanced.
|
||||||
|
*
|
||||||
|
* @param unsupportedTagList the list of unsupported tags
|
||||||
|
*/
|
||||||
|
public void setUnsupportedTagList(List<String> unsupportedTagList) {
|
||||||
|
if (unsupportedTagList != null && !unsupportedTagList.equals(mUnsupportedTagList)) {
|
||||||
|
mUnsupportedTagList = unsupportedTagList;
|
||||||
|
notifyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFilteredText(String text) {
|
||||||
|
if (mUnsupportedTagList == null) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = 1;
|
||||||
|
for (String tag : mUnsupportedTagList) {
|
||||||
|
if (!TextUtils.isEmpty(text)) {
|
||||||
|
final String index = String.valueOf(i++);
|
||||||
|
final String targetStart1 = "(?i)<" + tag + " ";
|
||||||
|
final String targetStart2 = "(?i)<" + tag + ">";
|
||||||
|
final String replacementStart1 = "<unsupportedtag" + index + " ";
|
||||||
|
final String replacementStart2 = "<unsupportedtag" + index + ">";
|
||||||
|
final String targetEnd = "(?i)</" + tag + ">";
|
||||||
|
final String replacementEnd = "</unsupportedtag" + index + ">";
|
||||||
|
text = Pattern.compile(targetStart1).matcher(text).replaceAll(replacementStart1);
|
||||||
|
text = Pattern.compile(targetStart2).matcher(text).replaceAll(replacementStart2);
|
||||||
|
text = Pattern.compile(targetEnd).matcher(text).replaceAll(replacementEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.accessibility;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.preference.PreferenceViewHolder;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom {@link android.widget.TextView} preference that removes the title and summary
|
||||||
|
* restriction from platform {@link Preference} implementation and the icon location is kept as
|
||||||
|
* gravity top instead of center.
|
||||||
|
*/
|
||||||
|
public class StaticTextPreference extends Preference {
|
||||||
|
|
||||||
|
StaticTextPreference(Context context) {
|
||||||
|
super(context);
|
||||||
|
setLayoutResource(R.layout.preference_static_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||||
|
super.onBindViewHolder(holder);
|
||||||
|
}
|
||||||
|
}
|
@@ -24,6 +24,7 @@ import android.app.Dialog;
|
|||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -69,8 +70,6 @@ public class ToggleAccessibilityServicePreferenceFragment extends ToggleFeatureP
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private ComponentName mComponentName;
|
|
||||||
|
|
||||||
private Dialog mDialog;
|
private Dialog mDialog;
|
||||||
|
|
||||||
private final View.OnClickListener mViewOnClickListener =
|
private final View.OnClickListener mViewOnClickListener =
|
||||||
@@ -341,5 +340,15 @@ public class ToggleAccessibilityServicePreferenceFragment extends ToggleFeatureP
|
|||||||
}
|
}
|
||||||
|
|
||||||
mComponentName = arguments.getParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME);
|
mComponentName = arguments.getParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME);
|
||||||
|
|
||||||
|
// Settings animated image.
|
||||||
|
int animatedImageRes = arguments.getInt(AccessibilitySettings.EXTRA_ANIMATED_IMAGE_RES);
|
||||||
|
mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
|
||||||
|
.authority(mComponentName.getPackageName())
|
||||||
|
.appendPath(String.valueOf(animatedImageRes))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Settings html description.
|
||||||
|
mHtmlDescription = arguments.getCharSequence(AccessibilitySettings.EXTRA_HTML_DESCRIPTION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,10 +16,16 @@
|
|||||||
|
|
||||||
package com.android.settings.accessibility;
|
package com.android.settings.accessibility;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.ContentResolver;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.text.Html;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.PreferenceScreen;
|
import androidx.preference.PreferenceScreen;
|
||||||
@@ -29,7 +35,9 @@ import com.android.settings.SettingsActivity;
|
|||||||
import com.android.settings.SettingsPreferenceFragment;
|
import com.android.settings.SettingsPreferenceFragment;
|
||||||
import com.android.settings.widget.SwitchBar;
|
import com.android.settings.widget.SwitchBar;
|
||||||
import com.android.settings.widget.ToggleSwitch;
|
import com.android.settings.widget.ToggleSwitch;
|
||||||
import com.android.settingslib.widget.FooterPreference;
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public abstract class ToggleFeaturePreferenceFragment extends SettingsPreferenceFragment {
|
public abstract class ToggleFeaturePreferenceFragment extends SettingsPreferenceFragment {
|
||||||
|
|
||||||
@@ -40,6 +48,30 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
|||||||
|
|
||||||
protected CharSequence mSettingsTitle;
|
protected CharSequence mSettingsTitle;
|
||||||
protected Intent mSettingsIntent;
|
protected Intent mSettingsIntent;
|
||||||
|
protected ComponentName mComponentName;
|
||||||
|
protected Uri mImageUri;
|
||||||
|
protected CharSequence mStaticDescription;
|
||||||
|
protected CharSequence mHtmlDescription;
|
||||||
|
private static final String ANCHOR_TAG = "a";
|
||||||
|
private static final String DRAWABLE_FOLDER = "drawable";
|
||||||
|
|
||||||
|
// For html description of accessibility service, third party developer must follow the rule,
|
||||||
|
// such as <img src="R.drawable.fileName"/>, a11y settings will get third party resources
|
||||||
|
// by this.
|
||||||
|
private static final String IMG_PREFIX = "R.drawable.";
|
||||||
|
|
||||||
|
private ImageView mImageGetterCacheView;
|
||||||
|
|
||||||
|
private final Html.ImageGetter mImageGetter = (String str) -> {
|
||||||
|
if (str != null && str.startsWith(IMG_PREFIX)) {
|
||||||
|
final String fileName = str.substring(IMG_PREFIX.length());
|
||||||
|
return getDrawableFromUri(Uri.parse(
|
||||||
|
ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
|
||||||
|
+ mComponentName.getPackageName() + "/" + DRAWABLE_FOLDER + "/"
|
||||||
|
+ fileName));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -63,15 +95,45 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
|||||||
onProcessArguments(getArguments());
|
onProcessArguments(getArguments());
|
||||||
updateSwitchBarText(mSwitchBar);
|
updateSwitchBarText(mSwitchBar);
|
||||||
|
|
||||||
|
PreferenceScreen preferenceScreen = getPreferenceScreen();
|
||||||
|
|
||||||
// Show the "Settings" menu as if it were a preference screen
|
// Show the "Settings" menu as if it were a preference screen
|
||||||
if (mSettingsTitle != null && mSettingsIntent != null) {
|
if (mSettingsTitle != null && mSettingsIntent != null) {
|
||||||
PreferenceScreen preferenceScreen = getPreferenceScreen();
|
|
||||||
Preference settingsPref = new Preference(preferenceScreen.getContext());
|
Preference settingsPref = new Preference(preferenceScreen.getContext());
|
||||||
settingsPref.setTitle(mSettingsTitle);
|
settingsPref.setTitle(mSettingsTitle);
|
||||||
settingsPref.setIconSpaceReserved(true);
|
settingsPref.setIconSpaceReserved(true);
|
||||||
settingsPref.setIntent(mSettingsIntent);
|
settingsPref.setIntent(mSettingsIntent);
|
||||||
preferenceScreen.addPreference(settingsPref);
|
preferenceScreen.addPreference(settingsPref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mImageUri != null) {
|
||||||
|
final AnimatedImagePreference animatedImagePreference = new AnimatedImagePreference(
|
||||||
|
preferenceScreen.getContext());
|
||||||
|
animatedImagePreference.setImageUri(mImageUri);
|
||||||
|
animatedImagePreference.setDividerAllowedAbove(true);
|
||||||
|
preferenceScreen.addPreference(animatedImagePreference);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mStaticDescription != null) {
|
||||||
|
final StaticTextPreference staticTextPreference = new StaticTextPreference(
|
||||||
|
preferenceScreen.getContext());
|
||||||
|
staticTextPreference.setSummary(mStaticDescription);
|
||||||
|
preferenceScreen.addPreference(staticTextPreference);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mHtmlDescription != null) {
|
||||||
|
// For accessibility service, avoid malicious links made by third party developer
|
||||||
|
final List<String> unsupportedTagList = new ArrayList<>();
|
||||||
|
unsupportedTagList.add(ANCHOR_TAG);
|
||||||
|
|
||||||
|
final HtmlTextPreference htmlTextPreference = new HtmlTextPreference(
|
||||||
|
preferenceScreen.getContext());
|
||||||
|
htmlTextPreference.setSummary(mHtmlDescription);
|
||||||
|
htmlTextPreference.setImageGetter(mImageGetter);
|
||||||
|
htmlTextPreference.setUnsupportedTagList(unsupportedTagList);
|
||||||
|
htmlTextPreference.setDividerAllowedAbove(true);
|
||||||
|
preferenceScreen.addPreference(htmlTextPreference);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -139,17 +201,30 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
|||||||
// Summary.
|
// Summary.
|
||||||
if (arguments.containsKey(AccessibilitySettings.EXTRA_SUMMARY_RES)) {
|
if (arguments.containsKey(AccessibilitySettings.EXTRA_SUMMARY_RES)) {
|
||||||
final int summary = arguments.getInt(AccessibilitySettings.EXTRA_SUMMARY_RES);
|
final int summary = arguments.getInt(AccessibilitySettings.EXTRA_SUMMARY_RES);
|
||||||
createFooterPreference(getText(summary));
|
mStaticDescription = getText(summary);
|
||||||
} else if (arguments.containsKey(AccessibilitySettings.EXTRA_SUMMARY)) {
|
} else if (arguments.containsKey(AccessibilitySettings.EXTRA_SUMMARY)) {
|
||||||
final CharSequence summary = arguments.getCharSequence(
|
final CharSequence summary = arguments.getCharSequence(
|
||||||
AccessibilitySettings.EXTRA_SUMMARY);
|
AccessibilitySettings.EXTRA_SUMMARY);
|
||||||
createFooterPreference(summary);
|
mStaticDescription = summary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createFooterPreference(CharSequence title) {
|
private Drawable getDrawableFromUri(Uri imageUri) {
|
||||||
final PreferenceScreen preferenceScreen = getPreferenceScreen();
|
if (mImageGetterCacheView == null) {
|
||||||
preferenceScreen.addPreference(new FooterPreference.Builder(getActivity()).setTitle(
|
mImageGetterCacheView = new ImageView(getContext());
|
||||||
title).build());
|
}
|
||||||
|
|
||||||
|
mImageGetterCacheView.setAdjustViewBounds(true);
|
||||||
|
mImageGetterCacheView.setImageURI(imageUri);
|
||||||
|
|
||||||
|
final Drawable drawable = mImageGetterCacheView.getDrawable().mutate();
|
||||||
|
if (drawable != null) {
|
||||||
|
drawable.setBounds(/* left= */0, /* top= */0, drawable.getIntrinsicWidth(),
|
||||||
|
drawable.getIntrinsicHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
mImageGetterCacheView.setImageURI(null);
|
||||||
|
mImageGetterCacheView.setImageDrawable(null);
|
||||||
|
return drawable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.accessibility;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.Html;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceViewHolder;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
import org.xml.sax.XMLReader;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** Tests for {@link HtmlTextPreference} */
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public final class HtmlTextPreferenceTest {
|
||||||
|
|
||||||
|
private HtmlTextPreference mHtmlTextPreference;
|
||||||
|
private PreferenceViewHolder mPreferenceViewHolder;
|
||||||
|
private String mHandledTag;
|
||||||
|
private final Html.TagHandler mTagHandler = new Html.TagHandler() {
|
||||||
|
@Override
|
||||||
|
public void handleTag(boolean opening, String tag, Editable editable, XMLReader xmlReader) {
|
||||||
|
mHandledTag = tag;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
final Context context = RuntimeEnvironment.application;
|
||||||
|
mHtmlTextPreference = new HtmlTextPreference(context);
|
||||||
|
|
||||||
|
final LayoutInflater inflater = LayoutInflater.from(context);
|
||||||
|
final View view =
|
||||||
|
inflater.inflate(R.layout.preference_static_text, null);
|
||||||
|
mPreferenceViewHolder = PreferenceViewHolder.createInstanceForTests(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnsupportedTagList_keepRealContentWithoutTag() {
|
||||||
|
final List<String> testUnsupportedTagList = new ArrayList<>();
|
||||||
|
testUnsupportedTagList.add("testTag");
|
||||||
|
final String testStr = "<testTag>Real description</testTag>";
|
||||||
|
final String expectedStr = "Real description";
|
||||||
|
final String expectedTag = "unsupportedtag1";
|
||||||
|
|
||||||
|
mHtmlTextPreference.setUnsupportedTagList(testUnsupportedTagList);
|
||||||
|
mHtmlTextPreference.setSummary(testStr);
|
||||||
|
mHtmlTextPreference.setTagHandler(mTagHandler);
|
||||||
|
mHtmlTextPreference.onBindViewHolder(mPreferenceViewHolder);
|
||||||
|
|
||||||
|
final TextView summaryView = mPreferenceViewHolder.itemView.findViewById(
|
||||||
|
android.R.id.summary);
|
||||||
|
assertThat(summaryView.getText().toString()).isEqualTo(expectedStr);
|
||||||
|
assertThat(mHandledTag).isEqualTo(expectedTag);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user