Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ab05476f authored by Peter_Liang's avatar Peter_Liang Committed by PETER LIANG
Browse files

Fine-tune the widget of "Allow rich content in Accessibility Settings" for security.

1. Remove tag checker in HtmlTextPreference.
2. Set the max height for AnimatedImagePreference.

Bug: 149516547
Test: manaul test
Change-Id: I98f49d055db9427d91a3f1ca816e94a11d29cd3d
Merged-In: I98f49d055db9427d91a3f1ca816e94a11d29cd3d
(cherry picked from commit 10bc3751)
parent 490eba35
Loading
Loading
Loading
Loading
+30 −0
Original line number Diff line number Diff line
@@ -21,9 +21,11 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.accessibility.AccessibilityManager;

import androidx.annotation.IntDef;
@@ -350,4 +352,32 @@ final class AccessibilityUtil {
                        "Unsupported userShortcutType " + shortcutType);
        }
    }

    /**
     * Gets the width of the screen.
     *
     * @param context the current context.
     * @return the width of the screen in terms of pixels.
     */
    public static int getScreenWidthPixels(Context context) {
        final Resources resources = context.getResources();
        final int screenWidthDp = resources.getConfiguration().screenWidthDp;

        return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenWidthDp,
                resources.getDisplayMetrics()));
    }

    /**
     * Gets the height of the screen.
     *
     * @param context the current context.
     * @return the height of the screen in terms of pixels.
     */
    public static int getScreenHeightPixels(Context context) {
        final Resources resources = context.getResources();
        final int screenHeightDp = resources.getConfiguration().screenHeightDp;

        return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenHeightDp,
                resources.getDisplayMetrics()));
    }
}
+25 −6
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import com.android.settings.R;
public class AnimatedImagePreference extends Preference {

    private Uri mImageUri;
    private int mMaxHeight = -1;

    AnimatedImagePreference(Context context) {
        super(context);
@@ -45,20 +46,26 @@ public class AnimatedImagePreference extends Preference {
        super.onBindViewHolder(holder);

        final ImageView imageView = holder.itemView.findViewById(R.id.animated_img);
        if (imageView != null && mImageUri != null) {
        if (imageView == null) {
            return;
        }

        if (mImageUri != null) {
            imageView.setImageURI(mImageUri);

            final Drawable drawable = imageView.getDrawable();
            if (drawable != null) {
            if (drawable instanceof AnimatedImageDrawable) {
                ((AnimatedImageDrawable) drawable).start();
            }
        }

        if (mMaxHeight > -1) {
            imageView.setMaxWidth(mMaxHeight);
        }
    }

    /**
     * Set image uri to display image in {@link ImageView}
     * Sets image uri to display image in {@link ImageView}
     *
     * @param imageUri the Uri of an image
     */
@@ -68,4 +75,16 @@ public class AnimatedImagePreference extends Preference {
            notifyChanged();
        }
    }

    /**
     * Sets the maximum height of the view.
     *
     * @param maxHeight the maximum height of ImageView in terms of pixels.
     */
    public void setMaxHeight(int maxHeight) {
        if (maxHeight != mMaxHeight) {
            mMaxHeight = maxHeight;
            notifyChanged();
        }
    }
}
+2 −41
Original line number Diff line number Diff line
@@ -23,9 +23,6 @@ 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.
@@ -35,7 +32,6 @@ public final class HtmlTextPreference extends StaticTextPreference {
    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);
@@ -47,8 +43,8 @@ public final class HtmlTextPreference extends StaticTextPreference {

        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));
            summaryView.setText(
                    Html.fromHtml(getSummary().toString(), mFlag, mImageGetter, mTagHandler));
        }
    }

@@ -87,39 +83,4 @@ public final class HtmlTextPreference extends StaticTextPreference {
            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;
    }
}
+22 −14
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.settings.accessibility;

import static com.android.settings.accessibility.AccessibilityUtil.getScreenHeightPixels;
import static com.android.settings.accessibility.AccessibilityUtil.getScreenWidthPixels;

import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
@@ -80,7 +83,6 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
    protected CharSequence mPackageName;
    protected Uri mImageUri;
    protected CharSequence mHtmlDescription;
    private static final String ANCHOR_TAG = "a";
    private static final String DRAWABLE_FOLDER = "drawable";
    protected static final String KEY_USE_SERVICE_PREFERENCE = "use_service";
    protected static final String KEY_GENERAL_CATEGORY = "general_categories";
@@ -94,9 +96,8 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
    private CheckBox mSoftwareTypeCheckBox;
    private CheckBox mHardwareTypeCheckBox;

    // 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.
    // For html description of accessibility service, must follow the rule, such as
    // <img src="R.drawable.fileName"/>, a11y settings will get the resources successfully.
    private static final String IMG_PREFIX = "R.drawable.";

    private ImageView mImageGetterCacheView;
@@ -147,10 +148,12 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference

        PreferenceScreen preferenceScreen = getPreferenceScreen();
        if (mImageUri != null) {
            final int screenHalfHeight = getScreenHeightPixels(getPrefContext()) / /* half */ 2;
            final AnimatedImagePreference animatedImagePreference = new AnimatedImagePreference(
                    getPrefContext());
            animatedImagePreference.setImageUri(mImageUri);
            animatedImagePreference.setSelectable(false);
            animatedImagePreference.setMaxHeight(screenHalfHeight);
            preferenceScreen.addPreference(animatedImagePreference);
        }

@@ -195,14 +198,9 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
            introductionCategory.setTitle(title);
            preferenceScreen.addPreference(introductionCategory);

            // 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(getPrefContext());
            htmlTextPreference.setSummary(mHtmlDescription);
            htmlTextPreference.setImageGetter(mImageGetter);
            htmlTextPreference.setUnsupportedTagList(unsupportedTagList);
            htmlTextPreference.setSelectable(false);
            introductionCategory.addPreference(htmlTextPreference);
        }
@@ -383,14 +381,24 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
        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());
        if (mImageGetterCacheView.getDrawable() == null) {
            return null;
        }

        final Drawable drawable =
                mImageGetterCacheView.getDrawable().mutate().getConstantState().newDrawable();
        mImageGetterCacheView.setImageURI(null);
        mImageGetterCacheView.setImageDrawable(null);
        final int imageWidth = drawable.getIntrinsicWidth();
        final int imageHeight = drawable.getIntrinsicHeight();
        final int screenHalfHeight = getScreenHeightPixels(getPrefContext()) / /* half */ 2;
        if ((imageWidth > getScreenWidthPixels(getPrefContext()))
                || (imageHeight > screenHalfHeight)) {
            return null;
        }

        drawable.setBounds(/* left= */0, /* top= */0, drawable.getIntrinsicWidth(),
                drawable.getIntrinsicHeight());

        return drawable;
    }

+2 −14
Original line number Diff line number Diff line
@@ -23,7 +23,6 @@ import android.text.Editable;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

import androidx.preference.PreferenceViewHolder;

@@ -36,9 +35,6 @@ 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 {
@@ -65,21 +61,13 @@ public final class HtmlTextPreferenceTest {
    }

    @Test
    public void testUnsupportedTagList_keepRealContentWithoutTag() {
        final List<String> testUnsupportedTagList = new ArrayList<>();
        testUnsupportedTagList.add("testTag");
    public void testTagHandler() {
        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);
        assertThat(mHandledTag).isEqualTo("testTag");
    }
}