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.app.UiModeManager; 21 import android.bluetooth.BluetoothAdapter; 22 import android.content.Context; 23 import android.net.wifi.WifiManager; 24 import android.os.SystemClock; 25 import android.support.test.uiautomator.By; 26 import android.support.test.uiautomator.BySelector; 27 import android.support.test.uiautomator.UiObject2; 28 import android.support.test.uiautomator.UiObjectNotFoundException; 29 import android.support.test.uiautomator.UiScrollable; 30 import android.support.test.uiautomator.UiSelector; 31 import androidx.test.InstrumentationRegistry; 32 33 import java.util.List; 34 import java.util.regex.Pattern; 35 36 public class SettingHelperImpl extends AbstractAutoStandardAppHelper implements IAutoSettingHelper { 37 38 private static final String LOG_TAG = SettingHelperImpl.class.getSimpleName(); 39 40 // Wait Time 41 private static final int UI_RESPONSE_WAIT_MS = 5000; 42 43 private UiModeManager mUiModeManager; 44 private Context mContext; 45 SettingHelperImpl(Instrumentation instr)46 public SettingHelperImpl(Instrumentation instr) { 47 super(instr); 48 mUiModeManager = 49 InstrumentationRegistry.getInstrumentation() 50 .getContext() 51 .getSystemService(UiModeManager.class); 52 mContext = InstrumentationRegistry.getContext(); 53 } 54 55 /** {@inheritDoc} */ 56 @Override open()57 public void open() { 58 openFullSettings(); 59 } 60 61 /** {@inheritDoc} */ 62 @Override getPackage()63 public String getPackage() { 64 return getApplicationConfig(AutoConfigConstants.SETTINGS_PACKAGE); 65 } 66 67 /** {@inheritDoc} */ 68 @Override stopSettingsApplication()69 public void stopSettingsApplication() { 70 String cmd = 71 String.format( 72 "am force-stop %s", 73 getApplicationConfig(AutoConfigConstants.SETTINGS_PACKAGE)); 74 executeShellCommand(cmd); 75 } 76 77 /** {@inheritDoc} */ 78 @Override getLauncherName()79 public String getLauncherName() { 80 return "Settings"; 81 } 82 83 /** {@inheritDoc} */ 84 @Override openSetting(String setting)85 public void openSetting(String setting) { 86 openFullSettings(); 87 openMenuWith(getSettingPath(setting)); 88 verifyAvailableOptions(setting); 89 } 90 91 /** {@inheritDoc} */ 92 @Override findSettingMenu(String setting)93 public UiObject2 findSettingMenu(String setting) { 94 UiObject2 menuObject = null; 95 String[] menuOptions = getSettingPath(setting); 96 int currentIndex = 0; 97 int lastIndex = menuOptions.length - 1; 98 for (String menu : menuOptions) { 99 int scrollableScreenIndex = 0; 100 if (hasSplitScreenSettingsUI() && currentIndex > 0) { 101 scrollableScreenIndex = 1; 102 } 103 menuObject = getMenu(menu, scrollableScreenIndex); 104 if (currentIndex == lastIndex) { 105 return menuObject; 106 } 107 clickAndWaitForIdleScreen(menuObject); 108 waitForIdle(); 109 currentIndex++; 110 } 111 return menuObject; 112 } 113 114 @Override findSettingMenuAndClick(String setting)115 public void findSettingMenuAndClick(String setting) { 116 UiObject2 settingMenu = findSettingMenu(setting); 117 if (settingMenu != null) { 118 clickAndWaitForIdleScreen(settingMenu); 119 } else { 120 throw new RuntimeException("Unable to find setting menu"); 121 } 122 } 123 124 @Override getPageTitleText()125 public String getPageTitleText() { 126 UiObject2 pageToolbarTitle = getPageTitle(); 127 return pageToolbarTitle.getText(); 128 } 129 130 /** {@inheritDoc} */ 131 @Override openFullSettings()132 public void openFullSettings() { 133 executeShellCommand(getApplicationConfig(AutoConfigConstants.OPEN_SETTINGS_COMMAND)); 134 } 135 136 /** {@inheritDoc} */ 137 @Override openQuickSettings()138 public void openQuickSettings() { 139 pressHome(); 140 executeShellCommand(getApplicationConfig(AutoConfigConstants.OPEN_QUICK_SETTINGS_COMMAND)); 141 UiObject2 settingObject = 142 findUiObject( 143 getResourceFromConfig( 144 AutoConfigConstants.SETTINGS, 145 AutoConfigConstants.QUICK_SETTINGS, 146 AutoConfigConstants.OPEN_MORE_SETTINGS)); 147 if (settingObject == null) { 148 throw new RuntimeException("Failed to open quick settings."); 149 } 150 } 151 verifyAvailableOptions(String setting)152 private void verifyAvailableOptions(String setting) { 153 String[] expectedOptions = getSettingOptions(setting); 154 if (expectedOptions == null) { 155 return; 156 } 157 for (String option : expectedOptions) { 158 if (mDevice.hasObject(By.clickable(false).textContains(option))) { 159 continue; 160 } 161 Pattern menuPattern = Pattern.compile(option, Pattern.CASE_INSENSITIVE); 162 BySelector selector = By.text(menuPattern); 163 int scrollScreenIndex = 0; 164 if (hasSplitScreenSettingsUI()) { 165 scrollScreenIndex = 1; 166 } 167 if (scrollAndFindUiObject(selector, scrollScreenIndex) == null) { 168 throw new RuntimeException("Cannot find settings option: " + option); 169 } 170 } 171 } 172 173 /** {@inheritDoc} */ 174 @Override turnOnOffWifi(boolean onOff)175 public void turnOnOffWifi(boolean onOff) { 176 boolean isOn = isWifiOn(); 177 if (isOn != onOff) { 178 UiObject2 enableOption = 179 findUiObject( 180 getResourceFromConfig( 181 AutoConfigConstants.SETTINGS, 182 AutoConfigConstants.NETWORK_AND_INTERNET_SETTINGS, 183 AutoConfigConstants.TOGGLE_WIFI)); 184 clickAndWaitForWindowUpdate( 185 getApplicationConfig(AutoConfigConstants.SETTINGS_PACKAGE), enableOption); 186 } else { 187 throw new RuntimeException("Wi-Fi enabled state is already " + (onOff ? "on" : "off")); 188 } 189 } 190 191 /** {@inheritDoc} */ 192 @Override isWifiOn()193 public boolean isWifiOn() { 194 WifiManager wifi = (WifiManager) this.mContext.getSystemService(Context.WIFI_SERVICE); 195 return wifi.isWifiEnabled(); 196 } 197 198 /** {@inheritDoc} */ 199 @Override turnOnOffHotspot(boolean onOff)200 public void turnOnOffHotspot(boolean onOff) { 201 boolean isOn = isHotspotOn(); 202 if (isOn != onOff) { 203 UiObject2 enableOption = 204 findUiObject( 205 getResourceFromConfig( 206 AutoConfigConstants.SETTINGS, 207 AutoConfigConstants.NETWORK_AND_INTERNET_SETTINGS, 208 AutoConfigConstants.TOGGLE_HOTSPOT)); 209 clickAndWaitForWindowUpdate( 210 getApplicationConfig(AutoConfigConstants.SETTINGS_PACKAGE), enableOption); 211 } else { 212 throw new RuntimeException( 213 "Hotspot enabled state is already " + (onOff ? "on" : "off")); 214 } 215 } 216 217 /** {@inheritDoc} */ 218 @Override isHotspotOn()219 public boolean isHotspotOn() { 220 UiObject2 enableOption = 221 findUiObject( 222 getResourceFromConfig( 223 AutoConfigConstants.SETTINGS, 224 AutoConfigConstants.NETWORK_AND_INTERNET_SETTINGS, 225 AutoConfigConstants.TOGGLE_HOTSPOT)); 226 return enableOption.isChecked(); 227 } 228 229 /** {@inheritDoc} */ 230 @Override turnOnOffBluetooth(boolean onOff)231 public void turnOnOffBluetooth(boolean onOff) { 232 boolean isOn = isBluetoothOn(); 233 if (isOn != onOff) { 234 UiObject2 enableOption = 235 findUiObject( 236 getResourceFromConfig( 237 AutoConfigConstants.SETTINGS, 238 AutoConfigConstants.BLUETOOTH_SETTINGS, 239 AutoConfigConstants.TOGGLE_BLUETOOTH)); 240 clickAndWaitForWindowUpdate( 241 getApplicationConfig(AutoConfigConstants.SETTINGS_PACKAGE), enableOption); 242 } else { 243 throw new RuntimeException( 244 "Bluetooth enabled state is already " + (onOff ? "on" : "off")); 245 } 246 } 247 248 /** {@inheritDoc} */ 249 @Override isBluetoothOn()250 public boolean isBluetoothOn() { 251 BluetoothAdapter ba = BluetoothAdapter.getDefaultAdapter(); 252 return ba.isEnabled(); 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 UiObject2 search_button = 265 findUiObject( 266 getResourceFromConfig( 267 AutoConfigConstants.SETTINGS, 268 AutoConfigConstants.FULL_SETTINGS, 269 AutoConfigConstants.SEARCH)); 270 clickAndWaitForIdleScreen(search_button); 271 UiObject2 search_box = 272 findUiObject( 273 getResourceFromConfig( 274 AutoConfigConstants.SETTINGS, 275 AutoConfigConstants.FULL_SETTINGS, 276 AutoConfigConstants.SEARCH_BOX)); 277 search_box.setText(item); 278 SystemClock.sleep(UI_RESPONSE_WAIT_MS); 279 // close the keyboard to reveal all search results. 280 mDevice.pressBack(); 281 282 UiObject2 searchResults = 283 findUiObject( 284 getResourceFromConfig( 285 AutoConfigConstants.SETTINGS, 286 AutoConfigConstants.FULL_SETTINGS, 287 AutoConfigConstants.SEARCH_RESULTS)); 288 int numberOfResults = searchResults.getChildren().size(); 289 if (numberOfResults == 0) { 290 throw new RuntimeException("No results found"); 291 } 292 clickAndWaitForIdleScreen(searchResults.getChildren().get(selectedIndex)); 293 SystemClock.sleep(UI_RESPONSE_WAIT_MS); 294 295 UiObject2 object = findUiObject(By.textContains(item)); 296 if (object == null) { 297 throw new RuntimeException("Opened page does not contain searched item"); 298 } 299 } 300 301 /** {@inheritDoc} */ 302 @Override isValidPageTitle(String item)303 public boolean isValidPageTitle(String item) { 304 UiObject2 pageTitle = getPageTitle(); 305 return pageTitle.getText().contains(item); 306 } 307 getPageTitle()308 private UiObject2 getPageTitle() { 309 BySelector[] selectors = 310 new BySelector[] { 311 getResourceFromConfig( 312 AutoConfigConstants.SETTINGS, 313 AutoConfigConstants.FULL_SETTINGS, 314 AutoConfigConstants.PAGE_TITLE), 315 getResourceFromConfig( 316 AutoConfigConstants.SETTINGS, 317 AutoConfigConstants.APPS_SETTINGS, 318 AutoConfigConstants.PERMISSIONS_PAGE_TITLE) 319 }; 320 for (BySelector selector : selectors) { 321 List<UiObject2> pageTitles = findUiObjects(selector); 322 if (pageTitles != null && pageTitles.size() > 0) { 323 return pageTitles.get(pageTitles.size() - 1); 324 } 325 } 326 throw new RuntimeException("Unable to find page title"); 327 } 328 329 /** {@inheritDoc} */ 330 @Override goBackToSettingsScreen()331 public void goBackToSettingsScreen() { 332 // count is used to avoid infinite loop in case someone invokes 333 // after exiting settings application 334 int count = 5; 335 while (count > 0 336 && isAppInForeground() 337 && findUiObject( 338 By.text( 339 getApplicationConfig( 340 AutoConfigConstants.SETTINGS_TITLE_TEXT))) 341 == null) { 342 pressBack(); 343 SystemClock.sleep(UI_RESPONSE_WAIT_MS); // to avoid stale object error 344 count--; 345 } 346 } 347 348 /** {@inheritDoc} */ 349 @Override openMenuWith(String... menuOptions)350 public void openMenuWith(String... menuOptions) { 351 for (String menu : menuOptions) { 352 Pattern menuPattern = Pattern.compile(menu, Pattern.CASE_INSENSITIVE); 353 UiObject2 menuButton = scrollAndFindUiObject(By.text(menuPattern)); 354 if (menuButton == null) { 355 throw new RuntimeException("Unable to find menu item"); 356 } 357 clickAndWaitForIdleScreen(menuButton); 358 waitForIdle(); 359 } 360 } 361 362 /** 363 * Checks whether a setting menu is enabled or not. When not enabled, the menu item cannot be 364 * clicked. 365 */ 366 @Override isSettingMenuEnabled(String menu)367 public boolean isSettingMenuEnabled(String menu) { 368 boolean isSettingMenuEnabled = false; 369 String[] menuOptions = getSettingPath(menu); 370 int currentIndex = 0; 371 int lastIndex = menuOptions.length - 1; 372 for (String menuOption : menuOptions) { 373 int scrollableScreenIndex = 0; 374 if (hasSplitScreenSettingsUI() && currentIndex > 0) { 375 scrollableScreenIndex = 1; 376 } 377 UiObject2 menuObject = getMenu(menuOption, scrollableScreenIndex); 378 if (currentIndex == lastIndex) { 379 return menuObject.isEnabled(); 380 } 381 if (!menuObject.isEnabled()) { 382 return isSettingMenuEnabled; 383 } 384 clickAndWaitForIdleScreen(menuObject); 385 waitForIdle(); 386 currentIndex++; 387 } 388 return isSettingMenuEnabled; 389 } 390 getMenu(String menu, int index)391 private UiObject2 getMenu(String menu, int index) { 392 Pattern menuPattern = Pattern.compile(menu, Pattern.CASE_INSENSITIVE); 393 UiObject2 menuButton = scrollAndFindUiObject(By.text(menuPattern), index); 394 if (menuButton == null) { 395 throw new RuntimeException("Unable to find menu item"); 396 } 397 return menuButton; 398 } 399 400 /** {@inheritDoc} */ 401 @Override getValue(String setting)402 public int getValue(String setting) { 403 String cmd = String.format("settings get system %s", setting); 404 String value = executeShellCommand(cmd); 405 return Integer.parseInt(value.replaceAll("\\s", "")); 406 } 407 408 /** {@inheritDoc} */ 409 @Override setValue(String setting, int value)410 public void setValue(String setting, int value) { 411 String cmd = String.format("settings put system %s %d", setting, value); 412 executeShellCommand(cmd); 413 } 414 415 /** {@inheritDoc} */ 416 @Override changeSeekbarLevel(int index, ChangeType changeType)417 public void changeSeekbarLevel(int index, ChangeType changeType) { 418 try { 419 String seekBar = 420 String.format( 421 "%s:id/%s", 422 getResourcePackage( 423 AutoConfigConstants.SETTINGS, 424 AutoConfigConstants.DISPLAY_SETTINGS, 425 AutoConfigConstants.BRIGHTNESS_LEVEL), 426 getResourceValue( 427 AutoConfigConstants.SETTINGS, 428 AutoConfigConstants.DISPLAY_SETTINGS, 429 AutoConfigConstants.BRIGHTNESS_LEVEL)); 430 UiScrollable seekbar = 431 new UiScrollable(new UiSelector().resourceId(seekBar).instance(index)); 432 if (changeType == ChangeType.INCREASE) { 433 seekbar.scrollForward(1); 434 } else { 435 seekbar.scrollBackward(1); 436 } 437 waitForWindowUpdate(getApplicationConfig(AutoConfigConstants.SETTINGS_PACKAGE)); 438 } catch (UiObjectNotFoundException exception) { 439 throw new RuntimeException("Unable to find seekbar"); 440 } 441 } 442 443 /** {@inheritDoc} */ 444 @Override setDayNightMode(DayNightMode mode)445 public void setDayNightMode(DayNightMode mode) { 446 if (mode == DayNightMode.DAY_MODE 447 && getDayNightModeStatus().getValue() == mUiModeManager.MODE_NIGHT_YES 448 || mode == DayNightMode.NIGHT_MODE 449 && getDayNightModeStatus().getValue() != mUiModeManager.MODE_NIGHT_YES) { 450 clickAndWaitForWindowUpdate( 451 getApplicationConfig(AutoConfigConstants.SETTINGS_PACKAGE), 452 getNightModeButton()); 453 } 454 } 455 getNightModeButton()456 private UiObject2 getNightModeButton() { 457 UiObject2 nightModeButton = 458 scrollAndFindUiObject( 459 getResourceFromConfig( 460 AutoConfigConstants.SETTINGS, 461 AutoConfigConstants.QUICK_SETTINGS, 462 AutoConfigConstants.NIGHT_MODE)); 463 if (nightModeButton == null) { 464 throw new RuntimeException("Unable to find night mode button"); 465 } 466 return nightModeButton.getParent(); 467 } 468 469 /** {@inheritDoc} */ 470 @Override getDayNightModeStatus()471 public DayNightMode getDayNightModeStatus() { 472 return mUiModeManager.getNightMode() == mUiModeManager.MODE_NIGHT_YES 473 ? DayNightMode.NIGHT_MODE 474 : DayNightMode.DAY_MODE; 475 } 476 } 477