• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.system.helpers;
18 
19 import android.bluetooth.BluetoothAdapter;
20 import android.bluetooth.BluetoothManager;
21 import android.content.ComponentName;
22 import android.content.ContentResolver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.net.wifi.WifiManager;
26 import android.provider.Settings;
27 import android.provider.Settings.SettingNotFoundException;
28 import android.support.test.InstrumentationRegistry;
29 import android.support.test.uiautomator.By;
30 import android.support.test.uiautomator.BySelector;
31 import android.support.test.uiautomator.Direction;
32 import android.support.test.uiautomator.UiDevice;
33 import android.support.test.uiautomator.UiObject;
34 import android.support.test.uiautomator.UiObject2;
35 import android.support.test.uiautomator.UiObjectNotFoundException;
36 import android.support.test.uiautomator.UiScrollable;
37 import android.support.test.uiautomator.UiSelector;
38 import android.support.test.uiautomator.Until;
39 import android.util.Log;
40 import android.widget.Switch;
41 import android.widget.TextView;
42 
43 import junit.framework.Assert;
44 
45 import java.util.regex.Pattern;
46 
47 /**
48  * Implement common helper methods for settings.
49  */
50 public class SettingsHelper {
51     private static final String TAG = SettingsHelper.class.getSimpleName();
52     private static final String SETTINGS_PACKAGE = "com.android.settings";
53     private static final String SETTINGS_APP = "Settings";
54     private static final String SWITCH_WIDGET = "switch_widget";
55     private static final String WIFI = "Wi-Fi";
56     private static final String BLUETOOTH = "Bluetooth";
57     private static final String AIRPLANE = "Airplane mode";
58     private static final String LOCATION = "Location";
59     private static final String DND = "Do not disturb";
60     private static final String ZEN_MODE = "zen_mode";
61     private static final String FLASHLIGHT = "Flashlight";
62     private static final String AUTO_ROTATE_SCREEN = "Auto-rotate screen";
63     private static final BySelector SETTINGS_DASHBOARD = By.res(SETTINGS_PACKAGE,
64             "dashboard_container");
65     private static final UiSelector LIST_ITEM_VALUE =
66             new UiSelector().className(TextView.class);
67     public static final int TIMEOUT = 2000;
68     private static SettingsHelper sInstance = null;
69     private ActivityHelper mActivityHelper = null;
70     private ContentResolver mResolver = null;
71     private Context mContext = null;
72     private UiDevice mDevice = null;
73 
SettingsHelper()74     public SettingsHelper() {
75         mContext = InstrumentationRegistry.getTargetContext();
76         mResolver = mContext.getContentResolver();
77         mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
78         mActivityHelper = ActivityHelper.getInstance();
79     }
80 
getInstance()81     public static SettingsHelper getInstance() {
82         if (sInstance == null) {
83             sInstance = new SettingsHelper();
84         }
85         return sInstance;
86     }
87 
88     public static enum SettingsType {
89         SYSTEM, SECURE, GLOBAL
90     }
91 
92     /**
93      * @return Settings package name
94      */
getPackage()95     public String getPackage() {
96         return SETTINGS_PACKAGE;
97     }
98 
99     /**
100      * @return Settings app name
101      */
getLauncherName()102     public String getLauncherName() {
103         return SETTINGS_APP;
104     }
105 
106     /**
107      * Scroll through settings page
108      * @param numberOfFlings
109      * @throws Exception
110      */
scrollThroughSettings(int numberOfFlings)111     public void scrollThroughSettings(int numberOfFlings) throws Exception {
112         UiObject2 settingsList = loadAllSettings();
113         int count = 0;
114         while (count <= numberOfFlings && settingsList.fling(Direction.DOWN)) {
115             count++;
116         }
117     }
118 
119     /**
120      * Move to top of settings page
121      * @throws Exception
122      */
flingSettingsToStart()123     public void flingSettingsToStart() throws Exception {
124         UiObject2 settingsList = loadAllSettings();
125         while (settingsList.fling(Direction.UP));
126     }
127 
128     /**
129      * Launch specific settings page
130      * @param ctx
131      * @param pageName
132      * @throws Exception
133      */
launchSettingsPage(Context ctx, String pageName)134     public static void launchSettingsPage(Context ctx, String pageName) throws Exception {
135         Intent intent = new Intent(pageName);
136         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
137         ctx.startActivity(intent);
138         Thread.sleep(TIMEOUT * 2);
139     }
140 
141     /**
142      * Scroll vertically up or down
143      * @param isUp
144      */
scrollVert(boolean isUp)145     public void scrollVert(boolean isUp) {
146         int w = mDevice.getDisplayWidth();
147         int h = mDevice.getDisplayHeight();
148         mDevice.swipe(w / 2, h / 2, w / 2, isUp ? h : 0, 2);
149     }
150 
151     /**
152      * On N, the settingsDashboard is initially collapsed, and the user can see the "See all"
153      * element. On hitting "See all", the same settings dashboard element is now scrollable. For
154      * pre-N, the settings Dashboard is always scrollable, hence the check in the while loop. All
155      * this method does is expand the Settings list if needed, before returning the element.
156      */
loadAllSettings()157     public UiObject2 loadAllSettings() throws Exception {
158         UiObject2 settingsDashboard = mDevice.wait(Until.findObject(SETTINGS_DASHBOARD),
159                 TIMEOUT * 2);
160         Assert.assertNotNull("Could not find the settings dashboard object.", settingsDashboard);
161         int count = 0;
162         while (!settingsDashboard.isScrollable() && count <= 2) {
163             mDevice.wait(Until.findObject(By.text("SEE ALL")), TIMEOUT * 2).click();
164             settingsDashboard = mDevice.wait(Until.findObject(SETTINGS_DASHBOARD),
165                     TIMEOUT * 2);
166             count++;
167         }
168         return settingsDashboard;
169     }
170 
171     /**
172      * Performs click action on a setting when setting name is provided as exact string
173      * @param settingName
174      * @throws InterruptedException
175      */
clickSetting(String settingName)176     public void clickSetting(String settingName) throws InterruptedException {
177         int count = 5;
178         while (count > 0 && mDevice.wait(Until.findObject(By.text(settingName)), TIMEOUT) == null) {
179             scrollVert(false);
180             count--;
181         }
182         mDevice.wait(Until.findObject(By.text(settingName)), TIMEOUT).click();
183         Thread.sleep(TIMEOUT);
184     }
185 
186     /**
187      * Performs click action on a setting when setting has been found
188      * @param name
189      * @throws InterruptedException,UiObjectNotFoundException
190      */
selectSettingFor(String settingName)191     public boolean selectSettingFor(String settingName)
192             throws InterruptedException, UiObjectNotFoundException {
193         UiScrollable settingsList = new UiScrollable(
194                 new UiSelector().resourceId("android:id/content"));
195         UiObject appSettings = settingsList.getChildByText(LIST_ITEM_VALUE, settingName);
196         if (appSettings != null) {
197             return appSettings.click();
198         }
199         return false;
200     }
201 
202     /**
203      * Performs click action on a setting when setting name is provided as pattern
204      *
205      * @param settingName
206      * @throws InterruptedException
207      */
clickSetting(Pattern settingName)208     public void clickSetting(Pattern settingName) throws InterruptedException {
209         mDevice.wait(Until.findObject(By.text(settingName)), TIMEOUT).click();
210         Thread.sleep(400);
211     }
212 
213     /**
214      * Gets string value of a setting
215      * @param type
216      * @param sName
217      * @return
218      */
getStringSetting(SettingsType type, String sName)219     public String getStringSetting(SettingsType type, String sName) {
220         switch (type) {
221             case SYSTEM:
222                 return Settings.System.getString(mResolver, sName);
223             case GLOBAL:
224                 return Settings.Global.getString(mResolver, sName);
225             case SECURE:
226                 return Settings.Secure.getString(mResolver, sName);
227         }
228         return "";
229     }
230 
231     /**
232      * Get int value of a setting
233      * @param type
234      * @param sName
235      * @return
236      * @throws SettingNotFoundException
237      */
getIntSetting(SettingsType type, String sName)238     public int getIntSetting(SettingsType type, String sName) throws SettingNotFoundException {
239         switch (type) {
240             case SYSTEM:
241                 return Settings.System.getInt(mResolver, sName);
242             case GLOBAL:
243                 return Settings.Global.getInt(mResolver, sName);
244             case SECURE:
245                 return Settings.Secure.getInt(mResolver, sName);
246         }
247         return Integer.MIN_VALUE;
248     }
249 
250     /**
251      * Set string value of a setting
252      * @param type
253      * @param sName
254      * @param value
255      */
setStringSetting(SettingsType type, String sName, String value)256     public void setStringSetting(SettingsType type, String sName, String value)
257             throws InterruptedException {
258         switch (type) {
259             case SYSTEM:
260                 Settings.System.putString(mResolver, sName, value);
261                 break;
262             case GLOBAL:
263                 Settings.Global.putString(mResolver, sName, value);
264                 break;
265             case SECURE:
266                 Settings.Secure.putString(mResolver, sName, value);
267                 break;
268         }
269         Thread.sleep(TIMEOUT);
270     }
271 
272     /**
273      * Sets int value of a setting
274      * @param type
275      * @param sName
276      * @param value
277      */
setIntSetting(SettingsType type, String sName, int value)278     public void setIntSetting(SettingsType type, String sName, int value)
279             throws InterruptedException {
280         switch (type) {
281             case SYSTEM:
282                 Settings.System.putInt(mResolver, sName, value);
283                 break;
284             case GLOBAL:
285                 Settings.Global.putInt(mResolver, sName, value);
286                 break;
287             case SECURE:
288                 Settings.Secure.putInt(mResolver, sName, value);
289                 break;
290         }
291         Thread.sleep(TIMEOUT);
292     }
293 
294     /**
295      * Toggles setting and verifies the action, when setting name is passed as string
296      * @param type
297      * @param settingAction
298      * @param settingName
299      * @param internalName
300      * @return
301      * @throws Exception
302      */
verifyToggleSetting(SettingsType type, String settingAction, String settingName, String internalName)303     public boolean verifyToggleSetting(SettingsType type, String settingAction,
304             String settingName, String internalName) throws Exception {
305         return verifyToggleSetting(
306                 type, settingAction, Pattern.compile(settingName), internalName, true);
307     }
308 
309     /**
310      * Toggles setting and verifies the action, when setting name is passed as pattern
311      * @param type
312      * @param settingAction
313      * @param settingName
314      * @param internalName
315      * @return
316      * @throws Exception
317      */
verifyToggleSetting(SettingsType type, String settingAction, Pattern settingName, String internalName)318     public boolean verifyToggleSetting(SettingsType type, String settingAction,
319             Pattern settingName, String internalName) throws Exception {
320         return verifyToggleSetting(type, settingAction, settingName, internalName, true);
321     }
322 
323     /**
324      * Toggles setting and verifies the action, when setting name is passed as string
325      * and settings page needs to be launched or not
326      * @param type
327      * @param settingAction
328      * @param settingName
329      * @param internalName
330      * @param doLaunch
331      * @return
332      * @throws Exception
333      */
verifyToggleSetting(SettingsType type, String settingAction, String settingName, String internalName, boolean doLaunch)334     public boolean verifyToggleSetting(SettingsType type, String settingAction,
335             String settingName, String internalName, boolean doLaunch) throws Exception {
336         return verifyToggleSetting(
337                 type, settingAction, Pattern.compile(settingName), internalName, doLaunch);
338     }
339 
340     /**
341      * Toggles setting and verifies the action
342      * @param type
343      * @param settingAction
344      * @param settingName
345      * @param internalName
346      * @param doLaunch
347      * @return
348      * @throws Exception
349      */
verifyToggleSetting(SettingsType type, String settingAction, Pattern settingName, String internalName, boolean doLaunch)350     public boolean verifyToggleSetting(SettingsType type, String settingAction,
351             Pattern settingName, String internalName, boolean doLaunch) throws Exception {
352         String onSettingBaseVal = getStringSetting(type, internalName);
353         if (onSettingBaseVal == null) {
354             // Per bug b/35717943 default for charging sounds is ON
355             // So if null, the value should be set to 1.
356             if (settingName.matcher("Charging sounds").matches()) {
357                 onSettingBaseVal = "1";
358             }
359             else {
360                 onSettingBaseVal = "0";
361             }
362         }
363         int onSetting = Integer.parseInt(onSettingBaseVal);
364         Log.d(TAG, "On Setting value is : " + onSetting);
365         if (doLaunch) {
366             launchSettingsPage(mContext, settingAction);
367         }
368         clickSetting(settingName);
369         Log.d(TAG, "Clicked setting : " + settingName);
370         Thread.sleep(5000);
371         String changedSetting = getStringSetting(type, internalName);
372         Log.d(TAG, "Changed Setting value is : " + changedSetting);
373         if (changedSetting == null) {
374             Log.d(TAG, "Changed Setting value is : NULL");
375             changedSetting = "0";
376         }
377         return (1 - onSetting) == Integer.parseInt(changedSetting);
378     }
379 
380     /**
381      * @param type
382      * @param settingAction
383      * @param baseName
384      * @param settingName
385      * @param internalName
386      * @param testVal
387      * @return
388      * @throws Exception
389      */
verifyRadioSetting(SettingsType type, String settingAction, String baseName, String settingName, String internalName, String testVal)390     public boolean verifyRadioSetting(SettingsType type, String settingAction,
391             String baseName, String settingName,
392             String internalName, String testVal) throws Exception {
393         if (baseName != null)
394             clickSetting(baseName);
395         clickSetting(settingName);
396         Thread.sleep(500);
397         return getStringSetting(type, internalName).equals(testVal);
398     }
399 
toggleWiFiOnOffAndVerify(boolean verifyOn, boolean isQuickSettings)400     public void toggleWiFiOnOffAndVerify(boolean verifyOn, boolean isQuickSettings)
401             throws Exception {
402         String switchText = (verifyOn ? "OFF" : "ON");
403         WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
404         wifiManager.setWifiEnabled(!verifyOn);
405         Thread.sleep(TIMEOUT * 3);
406         if (isQuickSettings) {
407             launchAndClickSettings(isQuickSettings, null, By.descContains(WIFI)
408                     .clazz(Switch.class));
409         } else {
410             launchAndClickSettings(isQuickSettings, Settings.ACTION_WIFI_SETTINGS,
411                     By.res(SETTINGS_PACKAGE, SWITCH_WIDGET).text(switchText));
412         }
413         Thread.sleep(TIMEOUT * 3);
414         String wifiValue = Settings.Global.getString(mResolver, Settings.Global.WIFI_ON);
415         if (verifyOn) {
416             Assert.assertFalse(wifiValue == "0");
417         } else {
418             Assert.assertEquals("0", wifiValue);
419         }
420         mDevice.pressHome();
421         Thread.sleep(TIMEOUT * 3);
422     }
423 
toggleBTOnOffAndVerify(boolean verifyOn, boolean isQuickSettings)424     public void toggleBTOnOffAndVerify(boolean verifyOn, boolean isQuickSettings)
425             throws Exception {
426         String switchText = (verifyOn ? "OFF" : "ON");
427         BluetoothAdapter bluetoothAdapter = ((BluetoothManager) mContext
428                 .getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter();
429         boolean isEnabled = bluetoothAdapter.isEnabled();
430         boolean success = (verifyOn ? bluetoothAdapter.disable() : bluetoothAdapter.enable());
431         Thread.sleep(TIMEOUT * 3);
432         if (isQuickSettings) {
433             launchAndClickSettings(isQuickSettings, null,
434                     By.descContains(BLUETOOTH).clazz(Switch.class));
435         } else {
436             launchAndClickSettings(isQuickSettings, Settings.ACTION_BLUETOOTH_SETTINGS,
437                     By.res(SETTINGS_PACKAGE, SWITCH_WIDGET).text(switchText));
438         }
439         Thread.sleep(TIMEOUT * 3);
440         String bluetoothValue = Settings.Global.getString(
441                 mResolver,
442                 Settings.Global.BLUETOOTH_ON);
443         Assert.assertEquals((verifyOn ? "1" : "0"), bluetoothValue);
444         if (isEnabled) {
445             bluetoothAdapter.enable();
446         } else {
447             bluetoothAdapter.disable();
448         }
449         mDevice.pressHome();
450         Thread.sleep(TIMEOUT * 3);
451     }
452 
toggleAirplaneModeOnOrOffAndVerify(boolean verifyOn, boolean isQuickSettings)453     public void toggleAirplaneModeOnOrOffAndVerify(boolean verifyOn, boolean isQuickSettings)
454             throws Exception {
455         String settingValToPut = (verifyOn ? "0" : "1");
456         Settings.Global.putString(mResolver, Settings.Global.AIRPLANE_MODE_ON, settingValToPut);
457         if (isQuickSettings) {
458             launchAndClickSettings(isQuickSettings, null, By.descContains(AIRPLANE));
459         } else {
460             launchAndClickSettings(isQuickSettings, Settings.ACTION_WIRELESS_SETTINGS,
461                     By.text(AIRPLANE));
462         }
463         Thread.sleep(TIMEOUT * 3);
464         String airplaneModeValue = Settings.Global
465                 .getString(mResolver,
466                         Settings.Global.AIRPLANE_MODE_ON);
467         Assert.assertEquals((verifyOn ? "1" : "0"), airplaneModeValue);
468         mDevice.pressHome();
469         Thread.sleep(TIMEOUT * 3);
470     }
471 
toggleLocationSettingsOnOrOffAndVerify(boolean verifyOn, boolean isQuickSettings)472     public void toggleLocationSettingsOnOrOffAndVerify(boolean verifyOn, boolean isQuickSettings)
473             throws Exception {
474         // Set location flag
475         int settingValToPut = (verifyOn ? Settings.Secure.LOCATION_MODE_OFF
476                 : Settings.Secure.LOCATION_MODE_SENSORS_ONLY);
477         Settings.Secure.putInt(mResolver, Settings.Secure.LOCATION_MODE, settingValToPut);
478         // Load location settings
479         if (isQuickSettings) {
480             launchAndClickSettings(isQuickSettings, null, By.descContains(LOCATION));
481         } else {
482             launchAndClickSettings(isQuickSettings, Settings.ACTION_LOCATION_SOURCE_SETTINGS,
483                     By.res(SETTINGS_PACKAGE, SWITCH_WIDGET));
484         }
485         Thread.sleep(TIMEOUT * 3);
486         // Verify change in setting
487         int locationEnabled = Settings.Secure.getInt(mResolver,
488                 Settings.Secure.LOCATION_MODE);
489         if (verifyOn) {
490             Assert.assertFalse("Location not enabled correctly", locationEnabled == 0);
491         } else {
492             Assert.assertEquals("Location not disabled correctly", 0, locationEnabled);
493         }
494         mDevice.pressHome();
495         Thread.sleep(TIMEOUT * 3);
496     }
497 
launchAndClickSettings(boolean isQuickSettings, String settingsPage, BySelector bySelector)498     public void launchAndClickSettings(boolean isQuickSettings, String settingsPage,
499             BySelector bySelector) throws Exception {
500         if (isQuickSettings) {
501             launchQuickSettingsAndWait();
502             UiObject2 qsTile = mDevice.wait(Until.findObject(bySelector), TIMEOUT * 3);
503             qsTile.findObject(By.clazz("android.widget.FrameLayout")).click();
504         } else {
505             mActivityHelper.launchIntent(settingsPage);
506             mDevice.wait(Until.findObject(bySelector), TIMEOUT * 3).click();
507         }
508     }
509 
510     /**
511      * Verify Quick Setting DND can be toggled DND default value is OFF
512      * @throws Exception
513      */
toggleQuickSettingDNDAndVerify()514     public void toggleQuickSettingDNDAndVerify() throws Exception {
515         try {
516             int onSetting = Settings.Global.getInt(mResolver, ZEN_MODE);
517             launchQuickSettingsAndWait();
518             mDevice.wait(Until.findObject(By.descContains(DND)),
519                     TIMEOUT * 3).getChildren().get(0).click();
520             Thread.sleep(TIMEOUT * 3);
521             int changedSetting = Settings.Global.getInt(mResolver, ZEN_MODE);
522             Assert.assertFalse(onSetting == changedSetting);
523             mDevice.pressHome();
524             Thread.sleep(TIMEOUT * 3);
525         } finally {
526             // change to DND default value
527             int setting = Settings.Global.getInt(mResolver, ZEN_MODE);
528             if (setting > 0) {
529                 launchQuickSettingsAndWait();
530                 mDevice.wait(Until.findObject(By.descContains(DND)),
531                         TIMEOUT * 3).getChildren().get(0).click();
532                 Thread.sleep(TIMEOUT * 3);
533             }
534         }
535     }
536 
toggleQuickSettingFlashLightAndVerify()537     public void toggleQuickSettingFlashLightAndVerify() throws Exception {
538         String lightOn = "On";
539         String lightOff = "Off";
540         boolean verifyOn = false;
541         launchQuickSettingsAndWait();
542         UiObject2 flashLight = mDevice.wait(
543                 Until.findObject(By.desc(FLASHLIGHT)),
544                 TIMEOUT * 3);
545         if (flashLight != null && flashLight.getText().equals(lightOn)) {
546             verifyOn = true;
547         }
548         mDevice.wait(Until.findObject(By.desc(FLASHLIGHT)),
549                 TIMEOUT * 3).click();
550         Thread.sleep(TIMEOUT * 3);
551         flashLight = mDevice.wait(
552                 Until.findObject(By.desc(FLASHLIGHT)),
553                 TIMEOUT * 3);
554         if (flashLight != null) {
555             String txt = flashLight.getText();
556             if (verifyOn) {
557                 Assert.assertTrue(txt.equals(lightOff));
558             } else {
559                 Assert.assertTrue(txt.equals(lightOn));
560                 mDevice.wait(Until.findObject(By.textContains(FLASHLIGHT)),
561                         TIMEOUT * 3).click();
562             }
563         }
564         mDevice.pressHome();
565         Thread.sleep(TIMEOUT * 3);
566     }
567 
toggleQuickSettingOrientationAndVerify()568     public void toggleQuickSettingOrientationAndVerify() throws Exception {
569         launchQuickSettingsAndWait();
570         mDevice.wait(Until.findObject(By.descContains(AUTO_ROTATE_SCREEN)),
571                 TIMEOUT * 3).click();
572         Thread.sleep(TIMEOUT * 3);
573         String rotation = Settings.System.getString(mResolver,
574                 Settings.System.ACCELEROMETER_ROTATION);
575         Assert.assertEquals("1", rotation);
576         mDevice.setOrientationNatural();
577         mDevice.pressHome();
578         Thread.sleep(TIMEOUT * 3);
579     }
580 
launchQuickSettingsAndWait()581     public void launchQuickSettingsAndWait() throws Exception {
582         mDevice.openQuickSettings();
583         Thread.sleep(TIMEOUT * 2);
584     }
585 
launchSettingsPageByComponentName(Context ctx, String name)586     public void launchSettingsPageByComponentName(Context ctx, String name) {
587         Intent intent = new Intent(Intent.ACTION_MAIN);
588         ComponentName settingComponent = new ComponentName(SETTINGS_PACKAGE,
589                 String.format("%s.%s$%s", SETTINGS_PACKAGE, SETTINGS_APP, name));
590         intent.setComponent(settingComponent);
591         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
592         ctx.startActivity(intent);
593     }
594 }
595