• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ScrollActions;
20 import android.platform.spectatio.constants.JsonConfigConstants.ScrollDirection;
21 import android.platform.spectatio.constants.JsonConfigConstants.SupportedWorkFlowTasks;
22 import android.platform.spectatio.exceptions.MissingUiElementException;
23 import android.platform.spectatio.utils.SpectatioUiUtil;
24 import android.util.Log;
25 
26 import androidx.test.uiautomator.BySelector;
27 import androidx.test.uiautomator.UiObject2;
28 
29 import com.google.common.base.Strings;
30 import com.google.gson.annotations.SerializedName;
31 
32 /** Workflow Task For Workflows in Spectatio Config JSON Config */
33 public class WorkflowTask {
34     private static final String LOG_TAG = WorkflowTask.class.getSimpleName();
35 
36     @SerializedName("NAME")
37     private String mName;
38 
39     @SerializedName("TYPE")
40     private String mType;
41 
42     // TEXT or UI_ELEMENT based on the Type of Workflow Task
43     @SerializedName("CONFIG")
44     private WorkflowTaskConfig mTaskConfig;
45 
46     // Number of times to repeat given task, default is 0
47     // By default, task will be execute once and won't be repeated as repeat count is 0
48     @SerializedName("REPEAT_COUNT")
49     private int mRepeatCount;
50 
51     // If task needs scrolling, provide Scroll Config
52     @SerializedName("SCROLL_CONFIG")
53     private ScrollConfig mScrollConfig;
54 
WorkflowTask( String name, String type, WorkflowTaskConfig taskConfig, int repeatCount, ScrollConfig scrollConfig)55     public WorkflowTask(
56             String name,
57             String type,
58             WorkflowTaskConfig taskConfig,
59             int repeatCount,
60             ScrollConfig scrollConfig) {
61         mName = name;
62         mType = type;
63         mTaskConfig = taskConfig;
64         mRepeatCount = repeatCount;
65         mScrollConfig = scrollConfig;
66     }
67 
getTaskName()68     public String getTaskName() {
69         return mName;
70     }
71 
getTaskType()72     public String getTaskType() {
73         return mType;
74     }
75 
getTaskConfig()76     public WorkflowTaskConfig getTaskConfig() {
77         return mTaskConfig;
78     }
79 
getRepeatCount()80     public int getRepeatCount() {
81         return mRepeatCount;
82     }
83 
getScrollConfig()84     public ScrollConfig getScrollConfig() {
85         return mScrollConfig;
86     }
87 
executeTask(String workflowName, SpectatioUiUtil spectatioUiUtil)88     public void executeTask(String workflowName, SpectatioUiUtil spectatioUiUtil) {
89         Log.i(
90                 LOG_TAG,
91                 String.format(
92                         "Executing Task %s with Type %s for Workflow %s",
93                         mName, mType, workflowName));
94 
95         SupportedWorkFlowTasks taskType = null;
96         try {
97             taskType = SupportedWorkFlowTasks.valueOf(mType);
98         } catch (IllegalArgumentException ex) {
99             throwRuntimeException("Workflow Task Type", mType, workflowName, "Not Supported");
100         }
101 
102         int executionCount = 0;
103         // Execute Task once by default. Repeat it again based on repeat count i.e. mRepeatCount > 0
104         do {
105             executeTask(taskType, workflowName, spectatioUiUtil);
106             executionCount++;
107             Log.i(
108                     LOG_TAG,
109                     String.format(
110                             "Completed executing Task %s, %d time(s).", mName, executionCount));
111 
112             // Wait for 1 Second before executing another task
113             spectatioUiUtil.wait1Second();
114         } while (executionCount < (1 + mRepeatCount));
115 
116         Log.i(
117                 LOG_TAG,
118                 String.format(
119                         "Done Executing Task %s with Type %s for Workflow %s",
120                         mName, mType, workflowName));
121     }
122 
executeTask( SupportedWorkFlowTasks taskType, String workflowName, SpectatioUiUtil spectatioUiUtil)123     private void executeTask(
124             SupportedWorkFlowTasks taskType, String workflowName, SpectatioUiUtil spectatioUiUtil) {
125         switch (taskType) {
126             case COMMAND:
127                 validateAndExecuteCommand(workflowName, spectatioUiUtil);
128                 break;
129             case HAS_PACKAGE_IN_FOREGROUND:
130                 validateAndVerifyPackage(workflowName, spectatioUiUtil);
131                 break;
132             case HAS_UI_ELEMENT_IN_FOREGROUND:
133                 validateAndVerifyUiElement(workflowName, spectatioUiUtil);
134                 break;
135             case CLICK:
136                 validateAndClickUiElement(
137                         workflowName,
138                         spectatioUiUtil,
139                         /* scrollToFind= */ false,
140                         /* isLongClick= */ false,
141                         /* isOptional= */ false);
142                 break;
143             case CLICK_IF_EXIST:
144                 validateAndClickUiElement(
145                         workflowName,
146                         spectatioUiUtil,
147                         /* scrollToFind= */ false,
148                         /* isLongClick= */ false,
149                         /* isOptional= */ true);
150                 break;
151             case LONG_CLICK:
152                 validateAndClickUiElement(
153                         workflowName,
154                         spectatioUiUtil,
155                         /* scrollToFind= */ false,
156                         /* isLongClick= */ true,
157                         /* isOptional= */ false);
158                 break;
159             case PRESS:
160                 validateAndPressKey(workflowName, spectatioUiUtil, /* isLongPress= */ false);
161                 break;
162             case LONG_PRESS:
163                 validateAndPressKey(workflowName, spectatioUiUtil, /* isLongPress= */ true);
164                 break;
165             case SCROLL_TO_FIND_AND_CLICK:
166                 validateAndClickUiElement(
167                         workflowName,
168                         spectatioUiUtil,
169                         /* scrollToFind= */ true,
170                         /* isLongClick= */ false,
171                         /* isOptional= */ false);
172                 break;
173             case SCROLL_TO_FIND_AND_CLICK_IF_EXIST:
174                 validateAndClickUiElement(
175                         workflowName,
176                         spectatioUiUtil,
177                         /* scrollToFind= */ true,
178                         /* isLongClick= */ false,
179                         /* isOptional= */ true);
180                 break;
181             case WAIT_MS:
182                 validateAndWait(workflowName, spectatioUiUtil);
183                 break;
184             default:
185                 throwRuntimeException("Workflow Task Type", mType, workflowName, "Not Supported");
186         }
187     }
188 
validateAndWait(String workflowName, SpectatioUiUtil spectatioUiUtil)189     private void validateAndWait(String workflowName, SpectatioUiUtil spectatioUiUtil) {
190         String waitTime = validateAndGetTaskConfigText(workflowName);
191         if (!isValidInteger(/* action= */ "WAIT_MS Value", waitTime, workflowName)) {
192             throwRuntimeException("Wait", waitTime, workflowName, "Invalid");
193         }
194         spectatioUiUtil.waitNSeconds(Integer.parseInt(waitTime));
195     }
196 
validateAndExecuteCommand(String workflowName, SpectatioUiUtil spectatioUiUtil)197     private void validateAndExecuteCommand(String workflowName, SpectatioUiUtil spectatioUiUtil) {
198         String command = validateAndGetTaskConfigText(workflowName);
199         spectatioUiUtil.executeShellCommand(command);
200     }
201 
validateAndVerifyUiElement(String workflowName, SpectatioUiUtil spectatioUiUtil)202     private void validateAndVerifyUiElement(String workflowName, SpectatioUiUtil spectatioUiUtil) {
203         UiElement uiElement = validateAndGetTaskConfigUiElement(workflowName);
204         BySelector selector = uiElement.getBySelectorForUiElement();
205         if (!spectatioUiUtil.hasUiElement(selector)) {
206             throwRuntimeException(
207                     "UI Element", selector.toString(), workflowName, "Not in Foreground");
208         }
209     }
210 
validateAndVerifyPackage(String workflowName, SpectatioUiUtil spectatioUiUtil)211     private void validateAndVerifyPackage(String workflowName, SpectatioUiUtil spectatioUiUtil) {
212         String pkg = validateAndGetTaskConfigText(workflowName);
213         if (!spectatioUiUtil.hasPackageInForeground(pkg)) {
214             throwRuntimeException("Package", pkg, workflowName, "Not in Foreground");
215         }
216     }
217 
validateAndClickUiElement( String workflowName, SpectatioUiUtil spectatioUiUtil, boolean scrollToFind, boolean isLongClick, boolean isOptional)218     private void validateAndClickUiElement(
219             String workflowName,
220             SpectatioUiUtil spectatioUiUtil,
221             boolean scrollToFind,
222             boolean isLongClick,
223             boolean isOptional) {
224         UiElement uiElement = validateAndGetTaskConfigUiElement(workflowName);
225         BySelector selector = uiElement.getBySelectorForUiElement();
226         UiObject2 uiObject = spectatioUiUtil.findUiObject(selector);
227         if (scrollToFind && !isValidUiObject(uiObject)) {
228             ScrollConfig scrollConfig = validateAndGetTaskScrollConfig(workflowName);
229             ScrollActions scrollAction = null;
230             try {
231                 scrollAction = ScrollActions.valueOf(scrollConfig.getScrollAction());
232             } catch (IllegalArgumentException ex) {
233                 throwRuntimeException(
234                         "Scroll Action",
235                         scrollConfig.getScrollAction(),
236                         workflowName,
237                         "Not Supported");
238             }
239             try {
240                 switch (scrollAction) {
241                     case USE_BUTTON:
242                         BySelector forwardButtonSelector =
243                                 scrollConfig.getScrollForwardButton().getBySelectorForUiElement();
244                         BySelector backwardButtonSelector =
245                                 scrollConfig.getScrollBackwardButton().getBySelectorForUiElement();
246                         uiObject =
247                                 spectatioUiUtil.scrollAndFindUiObject(
248                                         forwardButtonSelector, backwardButtonSelector, selector);
249                         break;
250                     case USE_GESTURE:
251                         BySelector scrollElementSelector =
252                                 scrollConfig.getScrollElement().getBySelectorForUiElement();
253                         Integer scrollMargin = Integer.valueOf(scrollConfig.getScrollMargin());
254                         Integer scrollWaitTime = Integer.valueOf(scrollConfig.getScrollWaitTime());
255 
256                         ScrollDirection scrollDirection = null;
257                         try {
258                             scrollDirection =
259                                     ScrollDirection.valueOf(scrollConfig.getScrollDirection());
260                         } catch (IllegalArgumentException ex) {
261                             throwRuntimeException(
262                                     "Scroll Direction",
263                                     scrollConfig.getScrollDirection(),
264                                     workflowName,
265                                     "Not Supported");
266                         }
267                         spectatioUiUtil.addScrollValues(scrollMargin, scrollWaitTime);
268                         uiObject =
269                                 spectatioUiUtil.scrollAndFindUiObject(
270                                         scrollElementSelector,
271                                         selector,
272                                         (scrollDirection == ScrollDirection.VERTICAL));
273                         break;
274                     default:
275                         throwRuntimeException(
276                                 "Scroll Action",
277                                 scrollConfig.getScrollAction(),
278                                 workflowName,
279                                 "Not Supported");
280                 }
281             } catch (MissingUiElementException ex) {
282                 throwRuntimeException(
283                         "Scroll Button or Element for Scroll Action",
284                         scrollConfig.getScrollAction(),
285                         workflowName,
286                         String.format("Missing. Error: %s", ex.getMessage()));
287             }
288         }
289         if (isOptional && !isValidUiObject(uiObject)) {
290             return;
291         }
292         validateUiObject(uiObject, workflowName);
293         if (isLongClick) {
294             spectatioUiUtil.longPress(uiObject);
295         } else {
296             spectatioUiUtil.clickAndWait(uiObject);
297         }
298     }
299 
validateAndPressKey( String workflowName, SpectatioUiUtil spectatioUiUtil, boolean isLongPress)300     private void validateAndPressKey(
301             String workflowName, SpectatioUiUtil spectatioUiUtil, boolean isLongPress) {
302         String key = validateAndGetTaskConfigText(workflowName);
303         // Check if Key is an integer i.e. KeyCode
304         if (isValidInteger(/* action= */ "PRESS", key, workflowName)) {
305             int keyCode = Integer.parseInt(key);
306             if (isLongPress) {
307                 spectatioUiUtil.longPressKey(keyCode);
308             } else {
309                 spectatioUiUtil.pressKeyCode(keyCode);
310             }
311             return;
312         }
313         switch (key) {
314             case "POWER":
315                 if (isLongPress) {
316                     spectatioUiUtil.longPressPower();
317                 } else {
318                     spectatioUiUtil.pressPower();
319                 }
320                 break;
321             case "HOME":
322                 if (isLongPress) {
323                     throwRuntimeException("Long Press", key, workflowName, "Not Supported");
324                 } else {
325                     spectatioUiUtil.pressHome();
326                 }
327                 break;
328             case "BACK":
329                 if (isLongPress) {
330                     throwRuntimeException("Long Press", key, workflowName, "Not Supported");
331                 } else {
332                     spectatioUiUtil.pressBack();
333                 }
334                 break;
335             case "SCREEN_CENTER":
336                 if (isLongPress) {
337                     spectatioUiUtil.longPressScreenCenter();
338                 } else {
339                     throwRuntimeException("Press", key, workflowName, "Not Supported");
340                 }
341                 break;
342             case "WAKE_UP":
343                 if (isLongPress) {
344                     throwRuntimeException("Long Press", key, workflowName, "Not Supported");
345                 } else {
346                     spectatioUiUtil.wakeUp();
347                 }
348                 break;
349             default:
350                 throwRuntimeException("Config", key, workflowName, "Not Supported");
351         }
352     }
353 
isValidInteger(String action, String value, String workflowName)354     private boolean isValidInteger(String action, String value, String workflowName) {
355         try {
356             int intValue = Integer.parseInt(value);
357             if (intValue < 0) {
358                 throwRuntimeException(action, value, workflowName, "Invalid");
359             }
360         } catch (NumberFormatException ex) {
361             return false;
362         }
363         return true;
364     }
365 
isValidUiObject(UiObject2 uiObject)366     private boolean isValidUiObject(UiObject2 uiObject) {
367         return uiObject != null;
368     }
369 
validateUiObject(UiObject2 uiObject, String workflowName)370     private void validateUiObject(UiObject2 uiObject, String workflowName) {
371         if (!isValidUiObject(uiObject)) {
372             throwRuntimeException(
373                     "UI Element for Config", "UI_ELEMENT", workflowName, "Missing on Device UI");
374         }
375     }
376 
validateAndGetTaskConfigText(String workflowName)377     private String validateAndGetTaskConfigText(String workflowName) {
378         String taskConfigText = mTaskConfig.getText();
379         if (Strings.isNullOrEmpty(taskConfigText)) {
380             throwRuntimeException(
381                     "Config Text", taskConfigText, workflowName, "Missing or Invalid");
382         }
383         return taskConfigText.trim();
384     }
385 
validateAndGetTaskConfigUiElement(String workflowName)386     private UiElement validateAndGetTaskConfigUiElement(String workflowName) {
387         UiElement uiElement = mTaskConfig.getUiElement();
388         if (uiElement == null) {
389             throwRuntimeException("Config", "UI_ELEMENT", workflowName, "Missing or Invalid");
390         }
391         return uiElement;
392     }
393 
validateAndGetTaskScrollConfig(String workflowName)394     private ScrollConfig validateAndGetTaskScrollConfig(String workflowName) {
395         if (mScrollConfig == null) {
396             throwRuntimeException("Config", "SCROLL_CONFIG", workflowName, "Missing or Invalid");
397         }
398         return mScrollConfig;
399     }
400 
throwRuntimeException( String property, String value, String workflowName, String reason)401     private void throwRuntimeException(
402             String property, String value, String workflowName, String reason) {
403         throw new RuntimeException(
404                 String.format(
405                         "%s %s for task %s with type %s in Workflow %s is %s.",
406                         property, value, mName, mType, workflowName, reason));
407     }
408 }
409