1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tools.idea.validator; 18 19 import com.android.tools.layoutlib.annotations.NotNull; 20 import com.android.tools.layoutlib.annotations.Nullable; 21 22 import java.util.EnumSet; 23 import java.util.HashSet; 24 import java.util.List; 25 26 import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheck; 27 28 /** 29 * Data used for layout validation. 30 */ 31 public class ValidatorData { 32 33 /** 34 * Category of validation 35 */ 36 public enum Type { 37 ACCESSIBILITY, 38 RENDER, 39 INTERNAL_ERROR 40 } 41 42 /** 43 * Level of importance 44 */ 45 public enum Level { 46 ERROR, 47 WARNING, 48 INFO, 49 /** The test not ran or suppressed. */ 50 VERBOSE, 51 } 52 53 /** 54 * Determine what types and levels of validation to run. 55 */ 56 public static class Policy { 57 /** Sets of types to filter by. */ 58 @NotNull public final EnumSet<Type> mTypes; 59 /** Sets of levels to filter by. */ 60 @NotNull public final EnumSet<Level> mLevels; 61 /** 62 * List of checks to use for the scan. If empty we use the default set 63 * defined by {@link AccessibilityCheckPreset.LATEST} 64 */ 65 @NotNull public final HashSet<AccessibilityHierarchyCheck> mChecks = new HashSet(); 66 Policy(@otNull EnumSet<Type> types, @NotNull EnumSet<Level> levels)67 public Policy(@NotNull EnumSet<Type> types, @NotNull EnumSet<Level> levels) { 68 mTypes = types; 69 mLevels = levels; 70 } 71 } 72 73 /** 74 * Issue describing the layout problem. 75 */ 76 public static class Issue { 77 @NotNull 78 public final String mCategory; 79 @NotNull 80 public final Type mType; 81 @NotNull 82 public final String mMsg; 83 @NotNull 84 public final Level mLevel; 85 @Nullable 86 public final Long mSrcId; 87 @Nullable 88 public final Fix mFix; 89 @NotNull 90 public final String mSourceClass; 91 @Nullable 92 public final String mHelpfulUrl; 93 Issue( @otNull String category, @NotNull Type type, @NotNull String msg, @NotNull Level level, @Nullable Long srcId, @Nullable Fix fix, @NotNull String sourceClass, @Nullable String helpfulUrl)94 private Issue( 95 @NotNull String category, 96 @NotNull Type type, 97 @NotNull String msg, 98 @NotNull Level level, 99 @Nullable Long srcId, 100 @Nullable Fix fix, 101 @NotNull String sourceClass, 102 @Nullable String helpfulUrl) { 103 mCategory = category; 104 mType = type; 105 mMsg = msg; 106 mLevel = level; 107 mSrcId = srcId; 108 mFix = fix; 109 mSourceClass = sourceClass; 110 mHelpfulUrl = helpfulUrl; 111 } 112 113 public static class IssueBuilder { 114 private String mCategory; 115 private Type mType = Type.ACCESSIBILITY; 116 private String mMsg; 117 private Level mLevel; 118 private Long mSrcId; 119 private Fix mFix; 120 private String mSourceClass; 121 private String mHelpfulUrl; 122 setCategory(String category)123 public IssueBuilder setCategory(String category) { 124 mCategory = category; 125 return this; 126 } 127 setType(Type type)128 public IssueBuilder setType(Type type) { 129 mType = type; 130 return this; 131 } 132 setMsg(String msg)133 public IssueBuilder setMsg(String msg) { 134 mMsg = msg; 135 return this; 136 } 137 setLevel(Level level)138 public IssueBuilder setLevel(Level level) { 139 mLevel = level; 140 return this; 141 } 142 setSrcId(Long srcId)143 public IssueBuilder setSrcId(Long srcId) { 144 mSrcId = srcId; 145 return this; 146 } 147 setFix(Fix fix)148 public IssueBuilder setFix(Fix fix) { 149 mFix = fix; 150 return this; 151 } 152 setSourceClass(String sourceClass)153 public IssueBuilder setSourceClass(String sourceClass) { 154 mSourceClass = sourceClass; 155 return this; 156 } 157 setHelpfulUrl(String url)158 public IssueBuilder setHelpfulUrl(String url) { 159 mHelpfulUrl = url; 160 return this; 161 } 162 build()163 public Issue build() { 164 assert(mCategory != null); 165 assert(mType != null); 166 assert(mMsg != null); 167 assert(mLevel != null); 168 assert(mSourceClass != null); 169 return new Issue(mCategory, 170 mType, 171 mMsg, 172 mLevel, 173 mSrcId, 174 mFix, 175 mSourceClass, 176 mHelpfulUrl); 177 } 178 } 179 } 180 181 /** 182 * Represents a view attribute which contains a namespace and an attribute name. 183 */ 184 public static class ViewAttribute { 185 /** The namespace used in XML files for this view attribute. */ 186 @NotNull public final String mNamespaceUri; 187 /** The namespace of this view attribute. */ 188 @NotNull public final String mNamespace; 189 /** The attribute name of this view attribute. */ 190 @NotNull public final String mAttributeName; 191 ViewAttribute( @otNull String namespaceUri, @NotNull String namespace, @NotNull String attributeName)192 public ViewAttribute( 193 @NotNull String namespaceUri, 194 @NotNull String namespace, 195 @NotNull String attributeName) { 196 mNamespaceUri = namespaceUri; 197 mNamespace = namespace; 198 mAttributeName = attributeName; 199 } 200 } 201 202 /** 203 * Suggested fix to the user or to the studio. 204 */ 205 public static interface Fix { 206 /** 207 * @return a human-readable description for this fix. 208 */ getDescription()209 @NotNull String getDescription(); 210 } 211 212 /** 213 * Suggest setting a value to a {@link ViewAttribute} to fix a specific {@link Issue}. 214 * 215 * <ul> 216 * <li>If the view attribute has not been set before, add the view attribute and set its value 217 * to the suggested value. 218 * <li>If the view attribute has been set before, replace its value with the suggested value. 219 * <li>If the suggested value is an empty string, ask the developer to set the view attribute 220 * to a meaningful non-empty string or resource reference. DO NOT set the view attribute 221 * to an empty string. 222 * </ul> 223 */ 224 public static class SetViewAttributeFix implements Fix { 225 /** The {@link ViewAttribute} suggested to be changed. */ 226 @NotNull public final ViewAttribute mViewAttribute; 227 228 /** The suggested value of the {@link ViewAttribute} suggested to be changed. */ 229 @NotNull public final String mSuggestedValue; 230 231 @NotNull private final String mDescription; 232 SetViewAttributeFix(@otNull ViewAttribute viewAttribute, @NotNull String suggestedValue, @NotNull String description)233 public SetViewAttributeFix(@NotNull ViewAttribute viewAttribute, 234 @NotNull String suggestedValue, @NotNull String description) { 235 mViewAttribute = viewAttribute; 236 mSuggestedValue = suggestedValue; 237 mDescription = description; 238 } 239 240 @Override getDescription()241 @NotNull public String getDescription() { 242 return mDescription; 243 } 244 } 245 246 /** 247 * Suggest removing a {@link ViewAttribute} to fix a specific {@link Issue}. 248 */ 249 public static class RemoveViewAttributeFix implements Fix { 250 /** The {@link ViewAttribute} suggested to be removed. */ 251 @NotNull public final ViewAttribute mViewAttribute; 252 253 @NotNull private final String mDescription; 254 RemoveViewAttributeFix(@otNull ViewAttribute viewAttribute, @NotNull String description)255 public RemoveViewAttributeFix(@NotNull ViewAttribute viewAttribute, 256 @NotNull String description) { 257 mViewAttribute = viewAttribute; 258 mDescription = description; 259 } 260 261 @Override getDescription()262 @NotNull public String getDescription() { 263 return mDescription; 264 } 265 } 266 267 /** 268 * Suggest applying multiple {@link Fix} together to fix a specific {@link Issue}. 269 * 270 * <p>A {@link CompoundFix} must contain at least 2 {@link Fix}. 271 */ 272 public static class CompoundFix implements Fix { 273 /** Lists of {@link Fix} suggested to be applied together. */ 274 @NotNull public final List<Fix> mFixes; 275 276 @NotNull private final String mDescription; 277 CompoundFix(@otNull List<Fix> fixes, String description)278 public CompoundFix(@NotNull List<Fix> fixes, String description) { 279 mFixes = fixes; 280 mDescription = description; 281 } 282 283 @Override getDescription()284 @NotNull public String getDescription() { 285 return mDescription; 286 } 287 } 288 } 289