Support the rich content for accessibility service (1/n)
Goal: 1. let third party developer can use html text that include from their local image file and animated image instead of only plain text to rich their content. 2. Avoid malicious links made by third party developer Action: 1. Add html, static text, and animated image preferences. 2. Add android:AnimatedImageDrawable, and android:htmlDescription attributes. 3. Fine-tune interface and integration 4. Add custom tag filter Bug: 136292241 Test: Maunal & make RunSettingsRoboTests Change-Id: I82cd5319efb7faa1ff7e8354a279828fce5135b8
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());
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@@ -111,6 +111,8 @@ public class AccessibilitySettings extends DashboardFragment {
|
||||
static final String EXTRA_SETTINGS_COMPONENT_NAME = "settings_component_name";
|
||||
static final String EXTRA_VIDEO_RAW_RESOURCE_ID = "video_resource";
|
||||
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
|
||||
// since the AccessibilityManagerService has to do that processing first
|
||||
@@ -409,6 +411,10 @@ public class AccessibilitySettings extends DashboardFragment {
|
||||
extras.putString(EXTRA_TITLE, title);
|
||||
extras.putParcelable(EXTRA_RESOLVE_INFO, resolveInfo);
|
||||
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();
|
||||
if (!TextUtils.isEmpty(settingsClassName)) {
|
||||
|
@@ -169,6 +169,9 @@ public class AccessibilitySettingsForSetupWizard extends SettingsPreferenceFragm
|
||||
description = getString(R.string.accessibility_service_default_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) {
|
||||
|
@@ -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.settings.SettingsEnums;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
@@ -69,8 +70,6 @@ public class ToggleAccessibilityServicePreferenceFragment extends ToggleFeatureP
|
||||
}
|
||||
};
|
||||
|
||||
private ComponentName mComponentName;
|
||||
|
||||
private Dialog mDialog;
|
||||
|
||||
private final View.OnClickListener mViewOnClickListener =
|
||||
@@ -341,5 +340,15 @@ public class ToggleAccessibilityServicePreferenceFragment extends ToggleFeatureP
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.Html;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
@@ -29,7 +35,9 @@ import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settings.widget.SwitchBar;
|
||||
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 {
|
||||
|
||||
@@ -40,6 +48,30 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
||||
|
||||
protected CharSequence mSettingsTitle;
|
||||
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
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
@@ -63,15 +95,45 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
||||
onProcessArguments(getArguments());
|
||||
updateSwitchBarText(mSwitchBar);
|
||||
|
||||
PreferenceScreen preferenceScreen = getPreferenceScreen();
|
||||
|
||||
// Show the "Settings" menu as if it were a preference screen
|
||||
if (mSettingsTitle != null && mSettingsIntent != null) {
|
||||
PreferenceScreen preferenceScreen = getPreferenceScreen();
|
||||
Preference settingsPref = new Preference(preferenceScreen.getContext());
|
||||
settingsPref.setTitle(mSettingsTitle);
|
||||
settingsPref.setIconSpaceReserved(true);
|
||||
settingsPref.setIntent(mSettingsIntent);
|
||||
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
|
||||
@@ -139,17 +201,30 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
||||
// Summary.
|
||||
if (arguments.containsKey(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)) {
|
||||
final CharSequence summary = arguments.getCharSequence(
|
||||
AccessibilitySettings.EXTRA_SUMMARY);
|
||||
createFooterPreference(summary);
|
||||
mStaticDescription = summary;
|
||||
}
|
||||
}
|
||||
|
||||
private void createFooterPreference(CharSequence title) {
|
||||
final PreferenceScreen preferenceScreen = getPreferenceScreen();
|
||||
preferenceScreen.addPreference(new FooterPreference.Builder(getActivity()).setTitle(
|
||||
title).build());
|
||||
private Drawable getDrawableFromUri(Uri imageUri) {
|
||||
if (mImageGetterCacheView == null) {
|
||||
mImageGetterCacheView = new ImageView(getContext());
|
||||
}
|
||||
|
||||
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