Home | History | Annotate | Download | only in helpers
      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 
     74     public SettingsHelper() {
     75         mContext = InstrumentationRegistry.getTargetContext();
     76         mResolver = mContext.getContentResolver();
     77         mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
     78         mActivityHelper = ActivityHelper.getInstance();
     79     }
     80 
     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      */
     95     public String getPackage() {
     96         return SETTINGS_PACKAGE;
     97     }
     98 
     99     /**
    100      * @return Settings app name
    101      */
    102     public String getLauncherName() {
    103         return SETTINGS_APP;
    104     }
    105 
    106     /**
    107      * Scroll through settings page
    108      * @param numberOfFlings
    109      * @throws Exception
    110      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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 
    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 
    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 
    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 
    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      */
    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 
    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 
    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 
    581     public void launchQuickSettingsAndWait() throws Exception {
    582         mDevice.openQuickSettings();
    583         Thread.sleep(TIMEOUT * 2);
    584     }
    585 
    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