1 /* 2 * Copyright (C) 2021 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.helpers; 18 19 import android.app.Instrumentation; 20 import android.bluetooth.BluetoothAdapter; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.net.wifi.WifiManager; 24 import android.os.UserHandle; 25 import android.platform.helpers.ScrollUtility.ScrollActions; 26 import android.platform.helpers.ScrollUtility.ScrollDirection; 27 import android.util.Log; 28 29 import androidx.test.InstrumentationRegistry; 30 import androidx.test.uiautomator.By; 31 import androidx.test.uiautomator.BySelector; 32 import androidx.test.uiautomator.UiObject2; 33 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.util.Locale; 37 import java.util.regex.Pattern; 38 39 /** Setting Helper class for Android Auto platform functional tests */ 40 public class SettingHelperImpl extends AbstractStandardAppHelper implements IAutoSettingHelper { 41 private static final String LOG_TAG = SettingHelperImpl.class.getSimpleName(); 42 43 private static final String SCREEN_BRIGHTNESS = "screen_brightness"; 44 45 private ScrollUtility mScrollUtility; 46 47 private HelperAccessor<IAutoUISettingsHelper> mSettingUIHelper = 48 new HelperAccessor<>(IAutoUISettingsHelper.class); 49 private ScrollActions mScrollAction; 50 private BySelector mBackwardButtonSelector; 51 private BySelector mForwardButtonSelector; 52 private BySelector mScrollableElementSelector; 53 private ScrollDirection mScrollDirection; 54 private final SeekUtility mSeekUtility; 55 private Context mContext; 56 private boolean mUseCommandToOpenSettings = true; 57 SettingHelperImpl(Instrumentation instr)58 public SettingHelperImpl(Instrumentation instr) { 59 super(instr); 60 mContext = InstrumentationRegistry.getContext(); 61 mUseCommandToOpenSettings = 62 Boolean.parseBoolean( 63 InstrumentationRegistry.getArguments() 64 .getString("use_command_to_open_settings", "true")); 65 mScrollUtility = ScrollUtility.getInstance(getSpectatioUiUtil()); 66 mScrollAction = 67 ScrollActions.valueOf( 68 getActionFromConfig(AutomotiveConfigConstants.SETTINGS_SCROLL_ACTION)); 69 mBackwardButtonSelector = 70 getUiElementFromConfig(AutomotiveConfigConstants.SETTINGS_SCROLL_BACKWARD_BUTTON); 71 mForwardButtonSelector = 72 getUiElementFromConfig(AutomotiveConfigConstants.SETTINGS_SCROLL_FORWARD_BUTTON); 73 mScrollableElementSelector = 74 getUiElementFromConfig(AutomotiveConfigConstants.SETTINGS_SCROLL_ELEMENT); 75 mScrollDirection = 76 ScrollDirection.valueOf( 77 getActionFromConfig(AutomotiveConfigConstants.SETTINGS_SCROLL_DIRECTION)); 78 mSeekUtility = SeekUtility.getInstance(getSpectatioUiUtil()); 79 } 80 81 /** {@inheritDoc} */ 82 @Override open()83 public void open() { 84 if (mUseCommandToOpenSettings) { 85 Log.i(LOG_TAG, "Using Command to open Settings."); 86 openFullSettings(); 87 } else { 88 Log.i(LOG_TAG, "Using Intent to open Settings."); 89 openSettingsIntent(); 90 } 91 } 92 93 /** {@inheritDoc} */ 94 @Override getPackage()95 public String getPackage() { 96 return getPackageFromConfig(AutomotiveConfigConstants.SETTINGS_PACKAGE); 97 } 98 99 @Override dismissInitialDialogs()100 public void dismissInitialDialogs() { 101 // Nothing to dismiss 102 } 103 104 /** {@inheritDoc} */ 105 @Override stopSettingsApplication()106 public void stopSettingsApplication() { 107 getSpectatioUiUtil() 108 .executeShellCommand( 109 getCommandFromConfig(AutomotiveConfigConstants.STOP_SETTING_APP_COMMAND)); 110 } 111 112 /** {@inheritDoc} */ 113 @Override getLauncherName()114 public String getLauncherName() { 115 return "Settings"; 116 } 117 118 /** {@inheritDoc} */ 119 @Override exit()120 public void exit() { 121 getSpectatioUiUtil().pressHome(); 122 getSpectatioUiUtil().wait1Second(); 123 } 124 125 /** {@inheritDoc} */ 126 @Override openSetting(String setting)127 public void openSetting(String setting) { 128 executeWorkflow(setting); 129 } 130 131 @Override getPageTitleText()132 public String getPageTitleText() { 133 UiObject2 pageToolbarTitle = getPageTitle(); 134 return pageToolbarTitle.getText(); 135 } 136 137 /** {@inheritDoc} */ 138 @Override openFullSettings()139 public void openFullSettings() { 140 getSpectatioUiUtil() 141 .executeShellCommand( 142 getCommandFromConfig(AutomotiveConfigConstants.OPEN_SETTINGS_COMMAND)); 143 } 144 openSettingsIntent()145 private void openSettingsIntent() { 146 // Launch the application as normal. 147 String pkg = getPackage(); 148 Log.i(LOG_TAG, String.format("Sending command to launch app: %s", pkg)); 149 mInstrumentation 150 .getContext() 151 .startActivityAsUser( 152 getOpenAppIntent().addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 153 UserHandle.CURRENT); 154 } 155 156 /** {@inheritDoc} */ 157 @Override turnOnOffWifi(boolean onOff)158 public void turnOnOffWifi(boolean onOff) { 159 boolean isOn = isWifiOn(); 160 if (isOn != onOff) { 161 BySelector enableOptionSelector = 162 getUiElementFromConfig(AutomotiveConfigConstants.TOGGLE_WIFI); 163 UiObject2 enableOption = getSpectatioUiUtil().findUiObject(enableOptionSelector); 164 getSpectatioUiUtil() 165 .validateUiObject(enableOption, AutomotiveConfigConstants.TOGGLE_WIFI); 166 getSpectatioUiUtil().clickAndWait(enableOption); 167 } else { 168 throw new RuntimeException("Wi-Fi enabled state is already " + (onOff ? "on" : "off")); 169 } 170 } 171 172 /** {@inheritDoc} */ 173 @Override isWifiOn()174 public boolean isWifiOn() { 175 WifiManager wifi = (WifiManager) this.mContext.getSystemService(Context.WIFI_SERVICE); 176 return wifi.isWifiEnabled(); 177 } 178 179 /** {@inheritDoc} */ 180 @Override turnOnOffHotspot(boolean onOff)181 public void turnOnOffHotspot(boolean onOff) { 182 boolean isOn = isHotspotOn(); 183 if (isOn != onOff) { 184 BySelector enableOptionSelector = 185 getUiElementFromConfig(AutomotiveConfigConstants.TOGGLE_HOTSPOT); 186 UiObject2 enableOption = getSpectatioUiUtil().findUiObject(enableOptionSelector); 187 getSpectatioUiUtil() 188 .validateUiObject(enableOption, AutomotiveConfigConstants.TOGGLE_HOTSPOT); 189 getSpectatioUiUtil().clickAndWait(enableOption); 190 } else { 191 throw new RuntimeException( 192 "Hotspot enabled state is already " + (onOff ? "on" : "off")); 193 } 194 } 195 196 /** {@inheritDoc} */ 197 @Override toggleHotspot()198 public void toggleHotspot() { 199 BySelector enableOptionSelector = 200 getUiElementFromConfig(AutomotiveConfigConstants.TOGGLE_HOTSPOT); 201 UiObject2 enableOption = getSpectatioUiUtil().findUiObject(enableOptionSelector); 202 getSpectatioUiUtil() 203 .validateUiObject(enableOption, AutomotiveConfigConstants.TOGGLE_HOTSPOT); 204 getSpectatioUiUtil().clickAndWait(enableOption); 205 } 206 207 /** {@inheritDoc} */ 208 @Override isHotspotOn()209 public boolean isHotspotOn() { 210 return !(getSpectatioUiUtil().hasUiElement("Off")); 211 } 212 213 /** {@inheritDoc} */ 214 @Override turnOnOffBluetooth(boolean onOff)215 public void turnOnOffBluetooth(boolean onOff) { 216 boolean isOn = isBluetoothOn(); 217 if (isOn != onOff) { 218 BySelector enableOptionSelector = 219 getUiElementFromConfig(AutomotiveConfigConstants.TOGGLE_BLUETOOTH); 220 UiObject2 enableOption = getSpectatioUiUtil().findUiObject(enableOptionSelector); 221 getSpectatioUiUtil() 222 .validateUiObject(enableOption, AutomotiveConfigConstants.TOGGLE_BLUETOOTH); 223 getSpectatioUiUtil().clickAndWait(enableOption); 224 } else { 225 throw new RuntimeException( 226 "Bluetooth enabled state is already " + (onOff ? "on" : "off")); 227 } 228 } 229 230 /** {@inheritDoc} */ 231 @Override isBluetoothOn()232 public boolean isBluetoothOn() { 233 BluetoothAdapter ba = BluetoothAdapter.getDefaultAdapter(); 234 return ba.isEnabled(); 235 } 236 237 /** {@inheritDoc} */ 238 @Override scrollBackward()239 public boolean scrollBackward() { 240 return mScrollUtility.scrollBackward( 241 mScrollAction, 242 mScrollDirection, 243 mBackwardButtonSelector, 244 mScrollableElementSelector, 245 String.format("Scroll backward on the settings menu page")); 246 } 247 248 /** {@inheritDoc} */ 249 @Override scrollForward()250 public boolean scrollForward() { 251 return mScrollUtility.scrollForward( 252 mScrollAction, 253 mScrollDirection, 254 mForwardButtonSelector, 255 mScrollableElementSelector, 256 String.format("Scroll forward on the settings menu page")); 257 } 258 259 /** {@inheritDoc} */ 260 @Override searchAndSelect(String item)261 public void searchAndSelect(String item) { 262 searchAndSelect(item, 0); 263 } 264 265 /** {@inheritDoc} */ 266 @Override searchAndSelect(String item, int selectedIndex)267 public void searchAndSelect(String item, int selectedIndex) { 268 BySelector searchButtonSelector = getUiElementFromConfig(AutomotiveConfigConstants.SEARCH); 269 UiObject2 searchButton = getSpectatioUiUtil().findUiObject(searchButtonSelector); 270 getSpectatioUiUtil().validateUiObject(searchButton, AutomotiveConfigConstants.SEARCH); 271 getSpectatioUiUtil().clickAndWait(searchButton); 272 getSpectatioUiUtil().waitForIdle(); 273 274 BySelector searchBoxSelector = getUiElementFromConfig(AutomotiveConfigConstants.SEARCH_BOX); 275 UiObject2 searchBox = getSpectatioUiUtil().findUiObject(searchBoxSelector); 276 getSpectatioUiUtil().validateUiObject(searchBox, AutomotiveConfigConstants.SEARCH_BOX); 277 searchBox.setText(item); 278 getSpectatioUiUtil().wait5Seconds(); 279 280 BySelector searchResultsSelector = 281 getUiElementFromConfig(AutomotiveConfigConstants.SEARCH_RESULTS); 282 UiObject2 searchResults = getSpectatioUiUtil().findUiObject(searchResultsSelector); 283 284 getSpectatioUiUtil() 285 .validateUiObject(searchResults, AutomotiveConfigConstants.SEARCH_RESULTS); 286 int numberOfResults = searchResults.getChildren().get(0).getChildren().size(); 287 if (numberOfResults == 0) { 288 throw new RuntimeException("No results found"); 289 } 290 getSpectatioUiUtil() 291 .clickAndWait(searchResults.getChildren().get(0).getChildren().get(selectedIndex)); 292 getSpectatioUiUtil().waitForIdle(); 293 getSpectatioUiUtil().wait5Seconds(); 294 295 BySelector objectSelector = By.textContains(item); 296 UiObject2 object = getSpectatioUiUtil().findUiObject(objectSelector); 297 getSpectatioUiUtil().validateUiObject(object, AutomotiveConfigConstants.SEARCH_RESULTS); 298 getSpectatioUiUtil() 299 .validateUiObject( 300 object, String.format("Opened page does not contain searched item")); 301 } 302 303 /** {@inheritDoc} */ 304 @Override isValidPageTitle(String item)305 public boolean isValidPageTitle(String item) { 306 UiObject2 pageTitle = getPageTitle(); 307 return pageTitle.getText().contains(item); 308 } 309 getPageTitle()310 private UiObject2 getPageTitle() { 311 getSpectatioUiUtil().wait5Seconds(); 312 BySelector[] selectors = 313 new BySelector[] { 314 getUiElementFromConfig(AutomotiveConfigConstants.PAGE_TITLE), 315 getUiElementFromConfig(AutomotiveConfigConstants.PERMISSIONS_PAGE_TITLE) 316 }; 317 318 for (BySelector selector : selectors) { 319 List<UiObject2> pageTitles = getSpectatioUiUtil().findUiObjects(selector); 320 if (pageTitles != null && pageTitles.size() > 0) { 321 return pageTitles.get(pageTitles.size() - 1); 322 } 323 } 324 throw new RuntimeException("Unable to find page title"); 325 } 326 327 @Override getSettingsPageTitleText()328 public String getSettingsPageTitleText() { 329 getSpectatioUiUtil().wait5Seconds(); 330 BySelector selector = getUiElementFromConfig(AutomotiveConfigConstants.PAGE_TITLE); 331 List<UiObject2> pageTitles = getSpectatioUiUtil().findUiObjects(selector); 332 List<String> pageTitlesText = new ArrayList<String>(); 333 for (UiObject2 pageTitle : pageTitles) { 334 pageTitlesText.add(pageTitle.getText()); 335 } 336 pageTitlesText.remove("Settings"); 337 return pageTitlesText.get(0); 338 } 339 340 /** {@inheritDoc} */ 341 @Override goBackToSettingsScreen()342 public void goBackToSettingsScreen() { 343 // count is used to avoid infinite loop in case someone invokes 344 // after exiting settings application 345 int count = 5; 346 BySelector titleText = 347 getUiElementFromConfig(AutomotiveConfigConstants.SETTINGS_TITLE_TEXT); 348 while (count > 0 349 && isAppInForeground() 350 && getSpectatioUiUtil().findUiObjects(titleText) == null) { 351 pressSettingsBackNavIcon(); 352 getSpectatioUiUtil().wait5Seconds(); // to avoid stale object error 353 count--; 354 } 355 } 356 357 /** {@inheritDoc} */ 358 @Override pressSettingsBackNavIcon()359 public void pressSettingsBackNavIcon() { 360 UiObject2 navIcon = 361 getSpectatioUiUtil() 362 .findUiObject( 363 getUiElementFromConfig( 364 AutomotiveConfigConstants.SETTINGS_BACK_NAV_ICON)); 365 if (navIcon == null) { 366 // if there is no nav button, use device back for confirmation dialog case 367 getSpectatioUiUtil().pressBack(); 368 return; 369 } 370 getSpectatioUiUtil().clickAndWait(navIcon); 371 } 372 373 /** {@inheritDoc} */ 374 @Override openMenuWith(String... menuOptions)375 public void openMenuWith(String... menuOptions) { 376 // Scroll and Find Subsettings 377 for (String menu : menuOptions) { 378 Pattern menuPattern = Pattern.compile(menu, Pattern.CASE_INSENSITIVE); 379 BySelector selector = By.text(menuPattern); 380 381 ScrollActions scrollAction = 382 ScrollActions.valueOf( 383 getActionFromConfig( 384 AutomotiveConfigConstants.SETTINGS_SUB_SETTING_SCROLL_ACTION)); 385 386 BySelector forwardButtonSelector = 387 getUiElementFromConfig( 388 AutomotiveConfigConstants.SETTINGS_SUB_SETTING_SCROLL_FORWARD_BUTTON); 389 BySelector backwardButtonSelector = 390 getUiElementFromConfig( 391 AutomotiveConfigConstants.SETTINGS_SUB_SETTING_SCROLL_BACKWARD_BUTTON); 392 393 BySelector scrollableElementSelector = 394 getUiElementFromConfig( 395 AutomotiveConfigConstants.SETTINGS_SUB_SETTING_SCROLL_ELEMENT); 396 ScrollDirection scrollDirection = 397 ScrollDirection.valueOf( 398 getActionFromConfig( 399 AutomotiveConfigConstants 400 .SETTINGS_SUB_SETTING_SCROLL_DIRECTION)); 401 402 UiObject2 object = 403 mScrollUtility.scrollAndFindUiObject( 404 scrollAction, 405 scrollDirection, 406 forwardButtonSelector, 407 backwardButtonSelector, 408 scrollableElementSelector, 409 selector, 410 String.format("Scroll on setting to find subssetting %s", selector)); 411 412 getSpectatioUiUtil() 413 .validateUiObject( 414 object, 415 String.format("Unable to find UI Element %s.", selector.toString())); 416 getSpectatioUiUtil().clickAndWait(object); 417 getSpectatioUiUtil().waitForIdle(); 418 } 419 } 420 421 /** {@inheritDoc} */ 422 @Override getValue(String setting)423 public int getValue(String setting) { 424 String cmd = String.format("settings get system %s", setting); 425 String value = getSpectatioUiUtil().executeShellCommand(cmd); 426 return Integer.parseInt(value.replaceAll("\\s", "")); 427 } 428 429 /** {@inheritDoc} */ 430 @Override setValue(String setting, int value)431 public void setValue(String setting, int value) { 432 String cmd = String.format(Locale.US, "settings put system %s %d", setting, value); 433 getSpectatioUiUtil().executeShellCommand(cmd); 434 } 435 436 /** {@inheritDoc} */ 437 @Override checkMenuExists(String setting)438 public boolean checkMenuExists(String setting) { 439 return getSpectatioUiUtil().hasUiElement(setting); 440 } 441 442 /** {@inheritDoc} */ 443 @Override scrollAndCheckMenuExists(String setting)444 public boolean scrollAndCheckMenuExists(String setting) { 445 return mSettingUIHelper.get().hasUIElement(setting); 446 } 447 448 /** 449 * TODO - Keeping the below empty functions for now, to avoid the compilation error in Vendor it 450 * will be removed after vendor clean up (b/266450258) 451 */ 452 453 /** {@inheritDoc} */ 454 @Override findSettingMenu(String setting)455 public UiObject2 findSettingMenu(String setting) { 456 UiObject2 menuObject = null; 457 return menuObject; 458 } 459 460 @Override findSettingMenuAndClick(String setting)461 public void findSettingMenuAndClick(String setting) {} 462 463 @Override setBrightness(float targetPercentage)464 public int setBrightness(float targetPercentage) { 465 mSeekUtility.registerSeekBar( 466 SCREEN_BRIGHTNESS, 467 AutomotiveConfigConstants.BRIGHTNESS_SEEKBAR, 468 SeekUtility.SeekLayout.HORIZONTAL, 469 () -> getValue(SCREEN_BRIGHTNESS)); 470 return mSeekUtility.seek(SCREEN_BRIGHTNESS, targetPercentage); 471 } 472 473 /** 474 * Checks whether a setting menu is enabled or not. When not enabled, the menu item cannot be 475 * clicked. 476 */ 477 @Override isSettingMenuEnabled(String menu)478 public boolean isSettingMenuEnabled(String menu) { 479 boolean isSettingMenuEnabled = false; 480 return isSettingMenuEnabled; 481 } 482 getMenu(String menu, int index)483 private UiObject2 getMenu(String menu, int index) { 484 UiObject2 menuButton = null; 485 return menuButton; 486 } 487 488 /** {@inheritDoc} */ 489 @Override isRecentAppDisplayedInLocationSettings(String app)490 public boolean isRecentAppDisplayedInLocationSettings(String app) { 491 UiObject2 recentApp = getSpectatioUiUtil().findUiObject("Maps"); 492 getSpectatioUiUtil() 493 .validateUiObject(recentApp, String.format("Recently accessed app - %s", app)); 494 UiObject2 recentAppsTime = recentApp.getParent(); 495 if (recentAppsTime.getChildren().size() < 2) { 496 throw new RuntimeException("TimeStamp not displayed for Recently accessed app"); 497 } 498 BySelector recentAppTimeStampSelector = 499 getUiElementFromConfig(AutomotiveConfigConstants.RECENT_APPS_TIMESTAMP); 500 UiObject2 timestampObject = 501 getSpectatioUiUtil() 502 .findUiObjectInGivenElement(recentAppsTime, recentAppTimeStampSelector); 503 getSpectatioUiUtil().validateUiObject(timestampObject, String.format("timestamp object")); 504 String timestamp = timestampObject.getText(); 505 String recentAppTimeStampTxt = 506 getActionFromConfig(AutomotiveConfigConstants.RECENT_APPS_TIMESTAMP_TEXT); 507 return timestamp.contains(recentAppTimeStampTxt); 508 } 509 } 510