Loading core/java/android/accessibilityservice/AccessibilityServiceInfo.java +121 −15 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; Loading Loading @@ -61,6 +62,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.regex.Pattern; /** * This class describes an {@link AccessibilityService}. The system notifies an Loading Loading @@ -552,6 +554,13 @@ public class AccessibilityServiceInfo implements Parcelable { */ private int mHtmlDescriptionRes; // Used for html description of accessibility service. The <img> src tag must follow the // prefix rule. e.g. <img src="R.drawable.fileName"/> private static final String IMG_PREFIX = "R.drawable."; private static final String ANCHOR_TAG = "a"; private static final List<String> UNSUPPORTED_TAG_LIST = new ArrayList<>( Collections.singletonList(ANCHOR_TAG)); /** * Creates a new instance. */ Loading Loading @@ -782,12 +791,10 @@ public class AccessibilityServiceInfo implements Parcelable { } /** * The animated image resource id. * <p> * <strong>Statically set from * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> * </p> * Gets the animated image resource id. * * @return The animated image resource id. * * @hide */ public int getAnimatedImageRes() { Loading @@ -797,10 +804,12 @@ public class AccessibilityServiceInfo implements Parcelable { /** * The animated image drawable. * <p> * Image can not exceed the screen size. * <strong>Statically set from * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> * </p> * @return The animated image drawable. * @return The animated image drawable, or null if the resource is invalid or the image * exceed the screen size. */ @Nullable public Drawable loadAnimatedImage(@NonNull Context context) { Loading @@ -808,11 +817,8 @@ public class AccessibilityServiceInfo implements Parcelable { return null; } final PackageManager packageManager = context.getPackageManager(); final String packageName = mComponentName.getPackageName(); final ApplicationInfo applicationInfo = mResolveInfo.serviceInfo.applicationInfo; return packageManager.getDrawable(packageName, mAnimatedImageRes, applicationInfo); return loadSafeAnimatedImage(context, mResolveInfo.serviceInfo.applicationInfo, mAnimatedImageRes); } /** Loading Loading @@ -924,16 +930,17 @@ public class AccessibilityServiceInfo implements Parcelable { } /** * The localized html description of the accessibility service. * The localized and restricted html description of the accessibility service. * <p> * Filters the <img> tag which do not meet the custom specification and the <a> tag. * <strong>Statically set from * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> * </p> * @return The localized html description. * @return The localized and restricted html description. */ @Nullable public String loadHtmlDescription(@NonNull PackageManager packageManager) { if (mHtmlDescriptionRes == 0) { if (mHtmlDescriptionRes == /* invalid */ 0) { return null; } Loading @@ -941,7 +948,7 @@ public class AccessibilityServiceInfo implements Parcelable { final CharSequence htmlDescription = packageManager.getText(serviceInfo.packageName, mHtmlDescriptionRes, serviceInfo.applicationInfo); if (htmlDescription != null) { return htmlDescription.toString().trim(); return getFilteredHtmlText(htmlDescription.toString().trim()); } return null; } Loading Loading @@ -1414,4 +1421,103 @@ public class AccessibilityServiceInfo implements Parcelable { return new AccessibilityServiceInfo[size]; } }; /** * Gets the filtered html string for * {@link android.accessibilityservice.AccessibilityServiceInfo} and * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It filters * the <img> tag which do not meet the custom specification and the <a> tag. * * @param text the target text is html format. * @return the filtered html string. * * @hide */ public static @NonNull String getFilteredHtmlText(@NonNull String text) { final String replacementStart = "<invalidtag "; final String replacementEnd = "</invalidtag>"; for (String tag : UNSUPPORTED_TAG_LIST) { final String regexStart = "(?i)<" + tag + "(\\s+|>)"; final String regexEnd = "(?i)</" + tag + "\\s*>"; text = Pattern.compile(regexStart).matcher(text).replaceAll(replacementStart); text = Pattern.compile(regexEnd).matcher(text).replaceAll(replacementEnd); } final String regexInvalidImgTag = "(?i)<img\\s+(?!src\\s*=\\s*\"(?-i)" + IMG_PREFIX + ")"; text = Pattern.compile(regexInvalidImgTag).matcher(text).replaceAll( replacementStart); return text; } /** * Loads the animated image for * {@link android.accessibilityservice.AccessibilityServiceInfo} and * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It checks the resource * whether to exceed the screen size. * * @param context the current context. * @param applicationInfo the current application. * @param resId the animated image resource id. * @return the animated image which is safe. * * @hide */ @Nullable public static Drawable loadSafeAnimatedImage(@NonNull Context context, @NonNull ApplicationInfo applicationInfo, @StringRes int resId) { if (resId == /* invalid */ 0) { return null; } final PackageManager packageManager = context.getPackageManager(); final String packageName = applicationInfo.packageName; final Drawable bannerDrawable = packageManager.getDrawable(packageName, resId, applicationInfo); if (bannerDrawable == null) { return null; } final boolean isImageWidthOverScreenLength = bannerDrawable.getIntrinsicWidth() > getScreenWidthPixels(context); final boolean isImageHeightOverScreenLength = bannerDrawable.getIntrinsicHeight() > getScreenHeightPixels(context); return (isImageWidthOverScreenLength || isImageHeightOverScreenLength) ? null : bannerDrawable; } /** * Gets the width of the screen. * * @param context the current context. * @return the width of the screen in term of pixels. * * @hide */ private static int getScreenWidthPixels(@NonNull 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 term of pixels. * * @hide */ private static int getScreenHeightPixels(@NonNull 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())); } } core/java/android/accessibilityservice/AccessibilityShortcutInfo.java +124 −11 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.accessibilityservice; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; import android.content.ComponentName; import android.content.Context; import android.content.Intent; Loading @@ -28,13 +29,19 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.util.AttributeSet; import android.util.TypedValue; import android.util.Xml; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.regex.Pattern; /** * Activities of interest to users with accessibility needs may request to be targets of the Loading Loading @@ -87,6 +94,13 @@ public final class AccessibilityShortcutInfo { */ private final int mHtmlDescriptionRes; // Used for html description of accessibility service. The <img> src tag must follow the // prefix rule. e.g. <img src="R.drawable.fileName"/> private static final String IMG_PREFIX = "R.drawable."; private static final String ANCHOR_TAG = "a"; private static final List<String> UNSUPPORTED_TAG_LIST = new ArrayList<>( Collections.singletonList(ANCHOR_TAG)); /** * Creates a new instance. * Loading Loading @@ -134,7 +148,7 @@ public final class AccessibilityShortcutInfo { // Gets animated image mAnimatedImageRes = asAttributes.getResourceId( com.android.internal.R.styleable .AccessibilityShortcutTarget_animatedImageDrawable, 0); .AccessibilityShortcutTarget_animatedImageDrawable, /* defValue= */ 0); // Gets html description mHtmlDescriptionRes = asAttributes.getResourceId( com.android.internal.R.styleable.AccessibilityShortcutTarget_htmlDescription, Loading Loading @@ -192,7 +206,7 @@ public final class AccessibilityShortcutInfo { } /** * The animated image resource id of the accessibility shortcut target. * Gets the animated image resource id. * * @return The animated image resource id. * Loading @@ -205,7 +219,8 @@ public final class AccessibilityShortcutInfo { /** * The animated image drawable of the accessibility shortcut target. * * @return The animated image drawable. * @return The animated image drawable, or null if the resource is invalid or the image * exceed the screen size. */ @Nullable public Drawable loadAnimatedImage(@NonNull Context context) { Loading @@ -213,21 +228,20 @@ public final class AccessibilityShortcutInfo { return null; } final PackageManager packageManager = context.getPackageManager(); final String packageName = mComponentName.getPackageName(); final ApplicationInfo applicationInfo = mActivityInfo.applicationInfo; return packageManager.getDrawable(packageName, mAnimatedImageRes, applicationInfo); return loadSafeAnimatedImage(context, mActivityInfo.applicationInfo, mAnimatedImageRes); } /** * The localized html description of the accessibility shortcut target. * The localized and restricted html description of the accessibility shortcut target. * It filters the <img> tag which do not meet the custom specification and the <a> tag. * * @return The localized html description. * @return The localized and restricted html description. */ @Nullable public String loadHtmlDescription(@NonNull PackageManager packageManager) { return loadResourceString(packageManager, mActivityInfo, mHtmlDescriptionRes); final String htmlDescription = loadResourceString(packageManager, mActivityInfo, mHtmlDescriptionRes); return TextUtils.isEmpty(htmlDescription) ? null : getFilteredHtmlText(htmlDescription); } /** Loading Loading @@ -291,4 +305,103 @@ public final class AccessibilityShortcutInfo { stringBuilder.append("]"); return stringBuilder.toString(); } /** * Gets the filtered html string for * {@link android.accessibilityservice.AccessibilityServiceInfo} and * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It filters * the <img> tag which do not meet the custom specification and the <a> tag. * * @param text the target text is html format. * @return the filtered html string. * * @hide */ public static @NonNull String getFilteredHtmlText(@NonNull String text) { final String replacementStart = "<invalidtag "; final String replacementEnd = "</invalidtag>"; for (String tag : UNSUPPORTED_TAG_LIST) { final String regexStart = "(?i)<" + tag + "(\\s+|>)"; final String regexEnd = "(?i)</" + tag + "\\s*>"; text = Pattern.compile(regexStart).matcher(text).replaceAll(replacementStart); text = Pattern.compile(regexEnd).matcher(text).replaceAll(replacementEnd); } final String regexInvalidImgTag = "(?i)<img\\s+(?!src\\s*=\\s*\"(?-i)" + IMG_PREFIX + ")"; text = Pattern.compile(regexInvalidImgTag).matcher(text).replaceAll( replacementStart); return text; } /** * Loads the animated image for * {@link android.accessibilityservice.AccessibilityServiceInfo} and * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It checks the resource * whether to exceed the screen size. * * @param context the current context. * @param applicationInfo the current application. * @param resId the animated image resource id. * @return the animated image which is safe. * * @hide */ @Nullable public static Drawable loadSafeAnimatedImage(@NonNull Context context, @NonNull ApplicationInfo applicationInfo, @StringRes int resId) { if (resId == /* invalid */ 0) { return null; } final PackageManager packageManager = context.getPackageManager(); final String packageName = applicationInfo.packageName; final Drawable bannerDrawable = packageManager.getDrawable(packageName, resId, applicationInfo); if (bannerDrawable == null) { return null; } final boolean isImageWidthOverScreenLength = bannerDrawable.getIntrinsicWidth() > getScreenWidthPixels(context); final boolean isImageHeightOverScreenLength = bannerDrawable.getIntrinsicHeight() > getScreenHeightPixels(context); return (isImageWidthOverScreenLength || isImageHeightOverScreenLength) ? null : bannerDrawable; } /** * Gets the width of the screen. * * @param context the current context. * @return the width of the screen in term of pixels. * * @hide */ private static int getScreenWidthPixels(@NonNull 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 term of pixels. * * @hide */ private static int getScreenHeightPixels(@NonNull 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())); } } Loading
core/java/android/accessibilityservice/AccessibilityServiceInfo.java +121 −15 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; Loading Loading @@ -61,6 +62,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.regex.Pattern; /** * This class describes an {@link AccessibilityService}. The system notifies an Loading Loading @@ -552,6 +554,13 @@ public class AccessibilityServiceInfo implements Parcelable { */ private int mHtmlDescriptionRes; // Used for html description of accessibility service. The <img> src tag must follow the // prefix rule. e.g. <img src="R.drawable.fileName"/> private static final String IMG_PREFIX = "R.drawable."; private static final String ANCHOR_TAG = "a"; private static final List<String> UNSUPPORTED_TAG_LIST = new ArrayList<>( Collections.singletonList(ANCHOR_TAG)); /** * Creates a new instance. */ Loading Loading @@ -782,12 +791,10 @@ public class AccessibilityServiceInfo implements Parcelable { } /** * The animated image resource id. * <p> * <strong>Statically set from * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> * </p> * Gets the animated image resource id. * * @return The animated image resource id. * * @hide */ public int getAnimatedImageRes() { Loading @@ -797,10 +804,12 @@ public class AccessibilityServiceInfo implements Parcelable { /** * The animated image drawable. * <p> * Image can not exceed the screen size. * <strong>Statically set from * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> * </p> * @return The animated image drawable. * @return The animated image drawable, or null if the resource is invalid or the image * exceed the screen size. */ @Nullable public Drawable loadAnimatedImage(@NonNull Context context) { Loading @@ -808,11 +817,8 @@ public class AccessibilityServiceInfo implements Parcelable { return null; } final PackageManager packageManager = context.getPackageManager(); final String packageName = mComponentName.getPackageName(); final ApplicationInfo applicationInfo = mResolveInfo.serviceInfo.applicationInfo; return packageManager.getDrawable(packageName, mAnimatedImageRes, applicationInfo); return loadSafeAnimatedImage(context, mResolveInfo.serviceInfo.applicationInfo, mAnimatedImageRes); } /** Loading Loading @@ -924,16 +930,17 @@ public class AccessibilityServiceInfo implements Parcelable { } /** * The localized html description of the accessibility service. * The localized and restricted html description of the accessibility service. * <p> * Filters the <img> tag which do not meet the custom specification and the <a> tag. * <strong>Statically set from * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> * </p> * @return The localized html description. * @return The localized and restricted html description. */ @Nullable public String loadHtmlDescription(@NonNull PackageManager packageManager) { if (mHtmlDescriptionRes == 0) { if (mHtmlDescriptionRes == /* invalid */ 0) { return null; } Loading @@ -941,7 +948,7 @@ public class AccessibilityServiceInfo implements Parcelable { final CharSequence htmlDescription = packageManager.getText(serviceInfo.packageName, mHtmlDescriptionRes, serviceInfo.applicationInfo); if (htmlDescription != null) { return htmlDescription.toString().trim(); return getFilteredHtmlText(htmlDescription.toString().trim()); } return null; } Loading Loading @@ -1414,4 +1421,103 @@ public class AccessibilityServiceInfo implements Parcelable { return new AccessibilityServiceInfo[size]; } }; /** * Gets the filtered html string for * {@link android.accessibilityservice.AccessibilityServiceInfo} and * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It filters * the <img> tag which do not meet the custom specification and the <a> tag. * * @param text the target text is html format. * @return the filtered html string. * * @hide */ public static @NonNull String getFilteredHtmlText(@NonNull String text) { final String replacementStart = "<invalidtag "; final String replacementEnd = "</invalidtag>"; for (String tag : UNSUPPORTED_TAG_LIST) { final String regexStart = "(?i)<" + tag + "(\\s+|>)"; final String regexEnd = "(?i)</" + tag + "\\s*>"; text = Pattern.compile(regexStart).matcher(text).replaceAll(replacementStart); text = Pattern.compile(regexEnd).matcher(text).replaceAll(replacementEnd); } final String regexInvalidImgTag = "(?i)<img\\s+(?!src\\s*=\\s*\"(?-i)" + IMG_PREFIX + ")"; text = Pattern.compile(regexInvalidImgTag).matcher(text).replaceAll( replacementStart); return text; } /** * Loads the animated image for * {@link android.accessibilityservice.AccessibilityServiceInfo} and * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It checks the resource * whether to exceed the screen size. * * @param context the current context. * @param applicationInfo the current application. * @param resId the animated image resource id. * @return the animated image which is safe. * * @hide */ @Nullable public static Drawable loadSafeAnimatedImage(@NonNull Context context, @NonNull ApplicationInfo applicationInfo, @StringRes int resId) { if (resId == /* invalid */ 0) { return null; } final PackageManager packageManager = context.getPackageManager(); final String packageName = applicationInfo.packageName; final Drawable bannerDrawable = packageManager.getDrawable(packageName, resId, applicationInfo); if (bannerDrawable == null) { return null; } final boolean isImageWidthOverScreenLength = bannerDrawable.getIntrinsicWidth() > getScreenWidthPixels(context); final boolean isImageHeightOverScreenLength = bannerDrawable.getIntrinsicHeight() > getScreenHeightPixels(context); return (isImageWidthOverScreenLength || isImageHeightOverScreenLength) ? null : bannerDrawable; } /** * Gets the width of the screen. * * @param context the current context. * @return the width of the screen in term of pixels. * * @hide */ private static int getScreenWidthPixels(@NonNull 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 term of pixels. * * @hide */ private static int getScreenHeightPixels(@NonNull 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())); } }
core/java/android/accessibilityservice/AccessibilityShortcutInfo.java +124 −11 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.accessibilityservice; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; import android.content.ComponentName; import android.content.Context; import android.content.Intent; Loading @@ -28,13 +29,19 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.util.AttributeSet; import android.util.TypedValue; import android.util.Xml; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.regex.Pattern; /** * Activities of interest to users with accessibility needs may request to be targets of the Loading Loading @@ -87,6 +94,13 @@ public final class AccessibilityShortcutInfo { */ private final int mHtmlDescriptionRes; // Used for html description of accessibility service. The <img> src tag must follow the // prefix rule. e.g. <img src="R.drawable.fileName"/> private static final String IMG_PREFIX = "R.drawable."; private static final String ANCHOR_TAG = "a"; private static final List<String> UNSUPPORTED_TAG_LIST = new ArrayList<>( Collections.singletonList(ANCHOR_TAG)); /** * Creates a new instance. * Loading Loading @@ -134,7 +148,7 @@ public final class AccessibilityShortcutInfo { // Gets animated image mAnimatedImageRes = asAttributes.getResourceId( com.android.internal.R.styleable .AccessibilityShortcutTarget_animatedImageDrawable, 0); .AccessibilityShortcutTarget_animatedImageDrawable, /* defValue= */ 0); // Gets html description mHtmlDescriptionRes = asAttributes.getResourceId( com.android.internal.R.styleable.AccessibilityShortcutTarget_htmlDescription, Loading Loading @@ -192,7 +206,7 @@ public final class AccessibilityShortcutInfo { } /** * The animated image resource id of the accessibility shortcut target. * Gets the animated image resource id. * * @return The animated image resource id. * Loading @@ -205,7 +219,8 @@ public final class AccessibilityShortcutInfo { /** * The animated image drawable of the accessibility shortcut target. * * @return The animated image drawable. * @return The animated image drawable, or null if the resource is invalid or the image * exceed the screen size. */ @Nullable public Drawable loadAnimatedImage(@NonNull Context context) { Loading @@ -213,21 +228,20 @@ public final class AccessibilityShortcutInfo { return null; } final PackageManager packageManager = context.getPackageManager(); final String packageName = mComponentName.getPackageName(); final ApplicationInfo applicationInfo = mActivityInfo.applicationInfo; return packageManager.getDrawable(packageName, mAnimatedImageRes, applicationInfo); return loadSafeAnimatedImage(context, mActivityInfo.applicationInfo, mAnimatedImageRes); } /** * The localized html description of the accessibility shortcut target. * The localized and restricted html description of the accessibility shortcut target. * It filters the <img> tag which do not meet the custom specification and the <a> tag. * * @return The localized html description. * @return The localized and restricted html description. */ @Nullable public String loadHtmlDescription(@NonNull PackageManager packageManager) { return loadResourceString(packageManager, mActivityInfo, mHtmlDescriptionRes); final String htmlDescription = loadResourceString(packageManager, mActivityInfo, mHtmlDescriptionRes); return TextUtils.isEmpty(htmlDescription) ? null : getFilteredHtmlText(htmlDescription); } /** Loading Loading @@ -291,4 +305,103 @@ public final class AccessibilityShortcutInfo { stringBuilder.append("]"); return stringBuilder.toString(); } /** * Gets the filtered html string for * {@link android.accessibilityservice.AccessibilityServiceInfo} and * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It filters * the <img> tag which do not meet the custom specification and the <a> tag. * * @param text the target text is html format. * @return the filtered html string. * * @hide */ public static @NonNull String getFilteredHtmlText(@NonNull String text) { final String replacementStart = "<invalidtag "; final String replacementEnd = "</invalidtag>"; for (String tag : UNSUPPORTED_TAG_LIST) { final String regexStart = "(?i)<" + tag + "(\\s+|>)"; final String regexEnd = "(?i)</" + tag + "\\s*>"; text = Pattern.compile(regexStart).matcher(text).replaceAll(replacementStart); text = Pattern.compile(regexEnd).matcher(text).replaceAll(replacementEnd); } final String regexInvalidImgTag = "(?i)<img\\s+(?!src\\s*=\\s*\"(?-i)" + IMG_PREFIX + ")"; text = Pattern.compile(regexInvalidImgTag).matcher(text).replaceAll( replacementStart); return text; } /** * Loads the animated image for * {@link android.accessibilityservice.AccessibilityServiceInfo} and * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It checks the resource * whether to exceed the screen size. * * @param context the current context. * @param applicationInfo the current application. * @param resId the animated image resource id. * @return the animated image which is safe. * * @hide */ @Nullable public static Drawable loadSafeAnimatedImage(@NonNull Context context, @NonNull ApplicationInfo applicationInfo, @StringRes int resId) { if (resId == /* invalid */ 0) { return null; } final PackageManager packageManager = context.getPackageManager(); final String packageName = applicationInfo.packageName; final Drawable bannerDrawable = packageManager.getDrawable(packageName, resId, applicationInfo); if (bannerDrawable == null) { return null; } final boolean isImageWidthOverScreenLength = bannerDrawable.getIntrinsicWidth() > getScreenWidthPixels(context); final boolean isImageHeightOverScreenLength = bannerDrawable.getIntrinsicHeight() > getScreenHeightPixels(context); return (isImageWidthOverScreenLength || isImageHeightOverScreenLength) ? null : bannerDrawable; } /** * Gets the width of the screen. * * @param context the current context. * @return the width of the screen in term of pixels. * * @hide */ private static int getScreenWidthPixels(@NonNull 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 term of pixels. * * @hide */ private static int getScreenHeightPixels(@NonNull 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())); } }