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