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