diff options
Diffstat (limited to 'validator')
4 files changed, 51 insertions, 5 deletions
diff --git a/validator/resources/strings.properties b/validator/resources/strings.properties index 27f310cfa3..3a07104c0c 100644 --- a/validator/resources/strings.properties +++ b/validator/resources/strings.properties @@ -16,6 +16,7 @@ # result_message_not_important_for_accessibility = This item was not found to be important for accessibility. result_message_no_content_desc = This item has no <tt>android:contentDescription</tt>. +result_message_no_content_desc_generic = This item has no content description. result_message_not_visible = This item is not visible. result_message_not_enabled = This item isn\'t enabled. result_message_not_text_view = This item is not a <tt>TextView</tt>. @@ -38,6 +39,7 @@ non_clickable = non-clickable long_clickable = long clickable clickable_and_long_clickable = clickable and long clickable actionable = actionable +result_message_addendum_conflicting_elements_list = Conflicting element(s): <tt>%1$s</tt>. check_title_duplicate_speakable_text = Item descriptions result_message_brief_same_speakable_text = Multiple items have the same description. result_message_same_speakable_text = This %1$s item\'s speakable text: \"<tt>%2$s</tt>\" is identical to that of %3$d other item(s). @@ -55,10 +57,14 @@ result_message_image_customized_contrast_not_sufficient_confirmed = The image\'s check_title_redundant_description = Item type label result_message_english_locale_only = This check only runs on devices with locales set to English. result_message_brief_content_desc_contains_redundant_word = This item\'s <tt>android:contentDescription</tt> might contain unnecessary text. +result_message_brief_content_desc_contains_redundant_word_generic = This item\'s content description might contain unnecessary text. result_message_content_desc_ends_with_view_type = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\" ends with the item\'s type. result_message_content_desc_contains_redundant_word = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\" contains the item type \"<tt>%2$s</tt>\". +result_message_content_desc_contains_redundant_word_generic = This item\'s content description, \"<tt>%1$s</tt>\" contains the item type \"<tt>%2$s</tt>\". result_message_content_desc_contains_action = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\", contains the action \"<tt>%2$s</tt>\". +result_message_content_desc_contains_action_generic = This item\'s content description, \"<tt>%1$s</tt>\", contains the action \"<tt>%2$s</tt>\". result_message_content_desc_contains_state = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\", contains the state \"<tt>%2$s</tt>\". +result_message_content_desc_contains_state_generic = This item\'s content description, \"<tt>%1$s</tt>\", contains the state \"<tt>%2$s</tt>\". button_item_type = button checkbox_item_type = checkbox checkbox_item_type_separate_words = check box @@ -73,6 +79,7 @@ check_title_speakable_text_present = Item label result_message_should_not_focus = This item would not be focused by a screen reader. result_message_web_content = Web content is not evaluated. result_message_unsupported_compose_content = Composable content is not evaluated in this environment. +result_message_unsupported_flutter_content = Flutter content is not evaluated in this environment. result_message_missing_speakable_text = This item may not have a label readable by screen readers. check_title_text_contrast = Text contrast result_message_textview_empty = This <tt>TextView</tt> is empty. @@ -158,12 +165,19 @@ result_message_item_type_with_text_size_unit = <tt>%1$s</tt> with text size unit result_message_small_fixed_text_size = This text is small and may be difficult for some users to read. Consider using a larger size or specifying the text size in scaled pixels (<tt>sp</tt>). result_message_fixed_text_size = Consider specifying the text size in scaled pixels (<tt>sp</tt>). result_message_brief_fixed_width_text_view_with_scaled_text = This <tt>TextView</tt> has a fixed width and scalable text. +result_message_brief_fixed_width_text_view_with_scaled_text_compose = This <tt>Text</tt> has a fixed width and scalable text. result_message_brief_fixed_height_text_view_with_scaled_text = This <tt>TextView</tt> has a fixed height and scalable text. +result_message_brief_fixed_height_text_with_scaled_text_compose = This <tt>Text</tt> has a fixed height and scalable text. result_message_brief_fixed_size_text_view_with_scaled_text = This <tt>TextView</tt> has a fixed size and scalable text. +result_message_brief_fixed_size_text_with_scaled_text_compose = This <tt>Text</tt> has a fixed size and scalable text. result_message_brief_fixed_width_view_group_with_scaled_text = This <tt>ViewGroup</tt> has a fixed width and contains a <tt>TextView</tt> with scalable text. +result_message_brief_fixed_width_parent_with_scaled_text_compose = This element has a fixed width and contains a <tt>Text</tt> element with scalable text. result_message_brief_fixed_height_view_group_with_scaled_text = This <tt>ViewGroup</tt> has a fixed height and contains a <tt>TextView</tt> with scalable text. +result_message_brief_fixed_height_parent_with_scaled_text_compose = This element has a fixed height and contains a <tt>Text</tt> element with scalable text. result_message_brief_fixed_size_view_group_with_scaled_text = This <tt>ViewGroup</tt> has a fixed size and contains a <tt>TextView</tt> with scalable text. +result_message_brief_fixed_size_parent_with_scaled_text_compose = This element has a fixed size and contains a <tt>Text</tt> element with scalable text. result_message_fixed_size_text_view_with_scaled_text = Consider modifying the <tt>LayoutParams</tt> to allow for text expansion. +result_message_fixed_size_text_with_scaled_text_compose = Consider modifying the size modifiers using <tt>sizeIn</tt> to allow for text expansion. check_title_unexposed_text = Unexposed Text result_message_unexposed_text = Ensure this item's accessibility label includes its visible text. result_message_text_detected_in_image_view = OCR results were detected inside this ImageView. diff --git a/validator/src/com/android/tools/idea/validator/ValidatorResult.java b/validator/src/com/android/tools/idea/validator/ValidatorResult.java index 7f25d46489..9d708b5bdf 100644 --- a/validator/src/com/android/tools/idea/validator/ValidatorResult.java +++ b/validator/src/com/android/tools/idea/validator/ValidatorResult.java @@ -21,6 +21,7 @@ import com.android.tools.idea.validator.ValidatorData.Level; import com.android.tools.layoutlib.annotations.NotNull; import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; import java.util.ArrayList; import java.util.List; @@ -35,14 +36,21 @@ import com.google.common.collect.ImmutableBiMap; public class ValidatorResult { @NotNull private final ImmutableBiMap<Long, View> mSrcMap; + @NotNull private final ImmutableBiMap<Long, AccessibilityNodeInfo> mNodeInfoMap; @NotNull private final ArrayList<Issue> mIssues; @NotNull private final Metric mMetric; /** * Please use {@link Builder} for creating results. */ - private ValidatorResult(BiMap<Long, View> srcMap, ArrayList<Issue> issues, Metric metric) { + private ValidatorResult(BiMap<Long, View> srcMap, + BiMap<Long, AccessibilityNodeInfo> nodeInfoMap, + ArrayList<Issue> issues, + Metric metric) { mSrcMap = ImmutableBiMap.<Long, View>builder().putAll(srcMap).build(); + mNodeInfoMap = ImmutableBiMap.<Long, AccessibilityNodeInfo>builder() + .putAll(nodeInfoMap) + .build(); mIssues = issues; mMetric = metric; } @@ -55,6 +63,13 @@ public class ValidatorResult { } /** + * @return the map from source ID to AccessibilityNodeInfo. + */ + public ImmutableBiMap<Long, AccessibilityNodeInfo> getNodeInfoMap() { + return mNodeInfoMap; + } + + /** * @return list of issues. */ public List<Issue> getIssues() { @@ -89,11 +104,12 @@ public class ValidatorResult { public static class Builder { @NotNull public final BiMap<Long, View> mSrcMap = HashBiMap.create(); + @NotNull public final BiMap<Long, AccessibilityNodeInfo> mNodeInfoMap = HashBiMap.create(); @NotNull public final ArrayList<Issue> mIssues = new ArrayList<>(); @NotNull public final Metric mMetric = new Metric(); public ValidatorResult build() { - return new ValidatorResult(mSrcMap, mIssues, mMetric); + return new ValidatorResult(mSrcMap, mNodeInfoMap, mIssues, mMetric); } } diff --git a/validator/src/com/android/tools/idea/validator/ValidatorUtil.java b/validator/src/com/android/tools/idea/validator/ValidatorUtil.java index 6078046de5..fa2862d191 100644 --- a/validator/src/com/android/tools/idea/validator/ValidatorUtil.java +++ b/validator/src/com/android/tools/idea/validator/ValidatorUtil.java @@ -83,8 +83,6 @@ public class ValidatorUtil { * uses be redirected. */ StringManager.setResourceBundleProvider(locale -> ResourceBundle.getBundle("strings")); - // Enable using AccessibilityNodeInfo in addition to View for accessibility testing - AccessibilityHierarchyAndroid.viewOverlayEnabled = true; } // Visible for testing. @@ -130,7 +128,9 @@ public class ValidatorUtil { try { hierarchy.mView = AccessibilityHierarchyAndroid .newBuilder(view) + .enableViewOverlay() .setViewOriginMap(builder.mSrcMap) + .setNodeInfoOriginMap(builder.mNodeInfoMap) .setObtainCharacterLocations(LayoutValidator.obtainCharacterLocations()) .setCharacterLocationArgMaxLength(CHARACTER_LOCATION_ARG_MAX_LENGTH) .setCustomViewBuilder(new CustomViewBuilderAndroid() { diff --git a/validator/src/com/android/tools/idea/validator/hierarchy/CustomHierarchyHelper.java b/validator/src/com/android/tools/idea/validator/hierarchy/CustomHierarchyHelper.java index eee2f32c36..5c8b2e9a39 100644 --- a/validator/src/com/android/tools/idea/validator/hierarchy/CustomHierarchyHelper.java +++ b/validator/src/com/android/tools/idea/validator/hierarchy/CustomHierarchyHelper.java @@ -48,13 +48,29 @@ public class CustomHierarchyHelper { // This is required as layoutlib does not know the support library such as // MaterialButton. LayoutlibCallback calls for studio which understands all the maven // pulled library. - Class button = callback.findClass( + Class<?> button = callback.findClass( "com.google.android.material.button.MaterialButton"); if (button.isInstance(fromView)) { Method isCheckable = button.getMethod("isCheckable"); Object toReturn = isCheckable.invoke(fromView); return (toReturn instanceof Boolean) && ((Boolean) toReturn); } + + Class<?> card = callback.findClass( + "com.google.android.material.card.MaterialCardView"); + if (card.isInstance(fromView)) { + Method isCheckable = card.getMethod("isCheckable"); + Object toReturn = isCheckable.invoke(fromView); + return (toReturn instanceof Boolean) && ((Boolean) toReturn); + } + + Class<?> chip = callback.findClass( + "com.google.android.material.chip.Chip"); + if (chip.isInstance(fromView)) { + Method isCheckable = chip.getMethod("isCheckable"); + Object toReturn = isCheckable.invoke(fromView); + return (toReturn instanceof Boolean) && ((Boolean) toReturn); + } } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | |