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