1 /* 2 * Copyright (C) 2022 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 android.platform.spectatio.configs; 18 19 import android.platform.spectatio.constants.JsonConfigConstants; 20 21 import androidx.test.uiautomator.By; 22 import androidx.test.uiautomator.BySelector; 23 24 import com.google.gson.annotations.SerializedName; 25 26 import java.util.List; 27 import java.util.regex.Pattern; 28 29 /** 30 * UI Resource For Spectatio Config JSON Config { "UI_ELEMENTS": { "CONFIG_NAME": { "TYPE": 31 * "RESOURCE_TYPE", "VALUE": "RESOURCE_VALUE", "PACKAGE": "RESOURCE_PACKAGE" } } } 32 * 33 * <p>RESOURCE_TYPE: TEXT, DESCRIPTION, RESOURCE_ID, TEXT_CONTAINS, CLASS; RESOURCE_VALUE: Value of 34 * the Resource; RESOURCE_PACKAGE: Package is required only to type RESOURCE_ID 35 * 36 * <p>Resource Value JSON { "TYPE": "RESOURCE_TYPE", "VALUE": "RESOURCE_VALUE", "PACKAGE": 37 * "RESOURCE_PACKAGE" } } } is referred in code using this class 38 */ 39 public class UiElement { 40 // Type of UI Element - Resource ID, Text, Description, Class 41 @SerializedName("TYPE") 42 private String mType; 43 44 @SerializedName("FLAG") 45 private boolean mFlag; 46 47 @SerializedName("MAX_DEPTH") 48 private int mMaxDepth; 49 50 // Value for the UI Resource - id, text value, description or class for the resource 51 @SerializedName("VALUE") 52 private String mValue; 53 54 // Application Package for the UI Resource if the type is Resource ID, 55 @SerializedName("PACKAGE") 56 private String mPackage; 57 58 // Each UiElementSpecifier that comprises a MULTIPLE specifier 59 @SerializedName("SPECIFIERS") 60 private List<UiElement> mSpecifiers; 61 62 // The specifier for the parent of a HAS_ASCENDANT specifier 63 @SerializedName("ANCESTOR") 64 private UiElement mAncestor; 65 66 // The specifier for the child of a HAS_DESCENDANT specifier 67 @SerializedName("DESCENDANT") 68 private UiElement mDescendant; 69 UiElement(String type, boolean flag)70 public UiElement(String type, boolean flag) { 71 mType = type; 72 mFlag = flag; 73 } 74 UiElement(String type, String value, String pkg)75 public UiElement(String type, String value, String pkg) { 76 mType = type; 77 mValue = value; 78 mPackage = pkg; 79 } 80 UiElement(List<UiElement> specifiers)81 public UiElement(List<UiElement> specifiers) { 82 mType = JsonConfigConstants.MULTIPLE; 83 mSpecifiers = specifiers; 84 } 85 UiElement(String type, UiElement relative, int maxDepth)86 public UiElement(String type, UiElement relative, int maxDepth) { 87 mType = type; 88 switch (type) { 89 case JsonConfigConstants.HAS_DESCENDANT: 90 mDescendant = relative; 91 break; 92 case JsonConfigConstants.HAS_ANCESTOR: 93 mAncestor = relative; 94 break; 95 default: 96 throw new RuntimeException( 97 "Unrecognized type given to UiElement constructor with relative argument"); 98 } 99 mMaxDepth = maxDepth; 100 } 101 102 /** Get Resource Type ( RESOURCE_ID, TEXT, DESCRIPTION, CLASS ) */ getType()103 public String getType() { 104 return mType; 105 } 106 107 /** Get Resource Value ( resource id, text value, description, class ) */ getValue()108 public String getValue() { 109 return mValue; 110 } 111 getPackage()112 public String getPackage() { 113 return mPackage; 114 } 115 116 /** Convert a UI element from the config into a BySelector */ getBySelectorForUiElement()117 public BySelector getBySelectorForUiElement() { 118 switch (mType) { 119 case JsonConfigConstants.RESOURCE_ID: 120 if (mPackage == null) { 121 return By.res(Pattern.compile(".*" + Pattern.quote(":id/" + mValue))); 122 } 123 return By.res(mPackage, mValue); 124 case JsonConfigConstants.CLICKABLE: 125 return By.clickable(mFlag); 126 case JsonConfigConstants.SCROLLABLE: 127 return By.scrollable(mFlag); 128 case JsonConfigConstants.TEXT: 129 return By.text(Pattern.compile(mValue, Pattern.CASE_INSENSITIVE)); 130 case JsonConfigConstants.TEXT_CONTAINS: 131 return By.textContains(mValue); 132 case JsonConfigConstants.DESCRIPTION: 133 return By.desc(Pattern.compile(mValue, Pattern.CASE_INSENSITIVE)); 134 case JsonConfigConstants.CLASS: 135 if (mPackage != null && !mPackage.isEmpty()) { 136 return By.clazz(mPackage, mValue); 137 } 138 return By.clazz(mValue); 139 case JsonConfigConstants.HAS_ANCESTOR: 140 return By.hasAncestor(mAncestor.getBySelectorForUiElement(), mMaxDepth); 141 case JsonConfigConstants.HAS_DESCENDANT: 142 return By.hasDescendant(mDescendant.getBySelectorForUiElement(), mMaxDepth); 143 case JsonConfigConstants.MULTIPLE: 144 BySelector selector = null; 145 for (UiElement specifier : mSpecifiers) { 146 if (selector == null) { 147 selector = specifier.getBySelectorForUiElement(); 148 } else { 149 specifier.extendBySelectorForUiElement(selector); 150 } 151 } 152 return selector; 153 154 default: 155 // Unknown UI Resource Type 156 return null; 157 } 158 } 159 extendBySelectorForUiElement(BySelector s)160 private void extendBySelectorForUiElement(BySelector s) { 161 switch (mType) { 162 case JsonConfigConstants.RESOURCE_ID: 163 s.res(mPackage, mValue); 164 break; 165 case JsonConfigConstants.CLICKABLE: 166 s.clickable(mFlag); 167 break; 168 case JsonConfigConstants.SCROLLABLE: 169 s.scrollable(mFlag); 170 break; 171 case JsonConfigConstants.TEXT: 172 s.text(Pattern.compile(mValue, Pattern.CASE_INSENSITIVE)); 173 break; 174 case JsonConfigConstants.TEXT_CONTAINS: 175 s.textContains(mValue); 176 break; 177 case JsonConfigConstants.DESCRIPTION: 178 s.desc(Pattern.compile(mValue, Pattern.CASE_INSENSITIVE)); 179 break; 180 case JsonConfigConstants.CLASS: 181 if (mPackage != null && !mPackage.isEmpty()) { 182 s.clazz(mPackage, mValue); 183 return; 184 } 185 s.clazz(mValue); 186 break; 187 case JsonConfigConstants.HAS_ANCESTOR: 188 s.hasAncestor(mAncestor.getBySelectorForUiElement(), mMaxDepth); 189 break; 190 case JsonConfigConstants.HAS_DESCENDANT: 191 s.hasDescendant(mDescendant.getBySelectorForUiElement(), mMaxDepth); 192 break; 193 case JsonConfigConstants.MULTIPLE: 194 throw new UnsupportedOperationException( 195 "You can't put a multiple-specifier inside a multiple-specifier."); 196 default: 197 break; 198 } 199 } 200 } 201