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 static android.content.Context.CONTEXT_IGNORE_SECURITY;
20 
21 import android.app.Instrumentation;
22 import android.content.Context;
23 import android.content.pm.PackageManager;
24 import android.content.res.Resources;
25 import android.graphics.Point;
26 import android.provider.Settings;
27 import android.text.TextUtils;
28 import android.util.Log;
29 
30 import androidx.annotation.NonNull;
31 import androidx.test.uiautomator.By;
32 import androidx.test.uiautomator.BySelector;
33 import androidx.test.uiautomator.UiDevice;
34 import androidx.test.uiautomator.UiObject2;
35 import androidx.test.uiautomator.Until;
36 
37 import org.junit.Assert;
38 
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.List;
42 
43 /**
44  * Implement common helper methods for Quick settings.
45  *
46  * @deprecated use classes from the "systemui-tapl" library instead
47  */
48 @Deprecated
49 public class QuickSettingsHelper {
50     private static final String LOG_TAG = QuickSettingsHelper.class.getSimpleName();
51     private static final int LONG_TIMEOUT = 2000;
52     private static final int SHORT_TIMEOUT = 500;
53     private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
54     private static final String QS_DEFAULT_TILES_RES = "quick_settings_tiles_default";
55     private static final BySelector FOOTER_SELECTOR = By.res(SYSTEMUI_PACKAGE, "qs_footer");
56     private static final String SYSUI_QS_TILES_SETTING = "sysui_qs_tiles";
57     private static final String SET_QS_TILES_COMMAND = "cmd statusbar set-tiles ";
58 
59     @NonNull private final UiDevice mDevice;
60     @NonNull private final Instrumentation mInstrumentation;
61     private List<String> mDefaultQSTileList = null;
62     private List<String> mPreviousQSTileList = null;
63     private final CommandsHelper mCommandsHelper;
64 
QuickSettingsHelper(@onNull UiDevice device, @NonNull Instrumentation inst)65     public QuickSettingsHelper(@NonNull UiDevice device, @NonNull Instrumentation inst) {
66         this.mDevice = device;
67         mInstrumentation = inst;
68         mCommandsHelper = CommandsHelper.getInstance(mInstrumentation);
69         try {
70             obtainDefaultQSTiles();
71         } catch (Exception e) {
72             Log.e(LOG_TAG, "obtainDefaultQSTiles fails!", e);
73         }
74     }
75 
obtainDefaultQSTiles()76     private void obtainDefaultQSTiles() throws PackageManager.NameNotFoundException {
77         final Context sysUIContext =
78                 mInstrumentation
79                         .getContext()
80                         .createPackageContext(SYSTEMUI_PACKAGE, CONTEXT_IGNORE_SECURITY);
81         final int qsTileListResId =
82                 sysUIContext
83                         .getResources()
84                         .getIdentifier(QS_DEFAULT_TILES_RES, "string", SYSTEMUI_PACKAGE);
85         final String defaultQSTiles = sysUIContext.getString(qsTileListResId);
86         mDefaultQSTileList = Arrays.asList(defaultQSTiles.split(","));
87     }
88 
89     public enum QuickSettingDefaultTiles {
90         WIFI("Wi-Fi"),
91         SIM("Mobile data"),
92         DND("Do not disturb"),
93         FLASHLIGHT("Flashlight"),
94         SCREEN("Auto-rotate screen"),
95         BLUETOOTH("Bluetooth"),
96         AIRPLANE("Airplane mode"),
97         BRIGHTNESS("Display brightness");
98 
99         private final String name;
100 
QuickSettingDefaultTiles(String name)101         QuickSettingDefaultTiles(String name) {
102             this.name = name;
103         }
104 
getName()105         public String getName() {
106             return this.name;
107         }
108     }
109 
110     public enum QuickSettingEditMenuTiles {
111         LOCATION("Location"),
112         HOTSPOT("Hotspot"),
113         INVERTCOLORS("Invert colors"),
114         DATASAVER("Data Saver"),
115         CAST("Cast"),
116         NEARBY("Nearby");
117 
118         private final String name;
119 
QuickSettingEditMenuTiles(String name)120         QuickSettingEditMenuTiles(String name) {
121             this.name = name;
122         }
123 
getName()124         public String getName() {
125             return this.name;
126         }
127     }
128 
addQuickSettingTileFromEditMenu(String quickSettingTile, String quickSettingTileToReplace, String quickSettingTileToCheckForInCSV)129     public void addQuickSettingTileFromEditMenu(String quickSettingTile,
130             String quickSettingTileToReplace, String quickSettingTileToCheckForInCSV)
131             throws Exception {
132         // Draw down quick settings
133         launchQuickSetting();
134         // Press Edit button
135         UiObject2 quickSettingEdit = mDevice.wait(Until.findObject
136                 (By.descContains("Edit")), LONG_TIMEOUT);
137         quickSettingEdit.click();
138         // Scroll down to bottom to see all QS options on Edit
139         swipeDown();
140         // Drag and drop QS item onto existing QS tile to replace it
141         // This is because we need specific coordinates on which to
142         // drop the quick setting tile.
143         UiObject2 quickSettingTileObject = mDevice.wait(Until.findObject
144                 (By.descContains(quickSettingTile)), LONG_TIMEOUT);
145         Point destination = mDevice.wait(Until.findObject
146                 (By.descContains(quickSettingTileToReplace)), LONG_TIMEOUT)
147                 .getVisibleCenter();
148         Assert.assertNotNull(quickSettingTile + " in Edit menu can't be found",
149                 quickSettingTileObject);
150         Assert.assertNotNull(quickSettingTileToReplace + " in QS menu can't be found",
151                 destination);
152         // Long press the icon, then drag it to the destination slowly.
153         // Without the long press, it ends up scrolling down quick settings.
154         quickSettingTileObject.click(2000);
155         quickSettingTileObject.drag(destination, 1000);
156         // Hit the back button in the QS menu to go back to quick settings.
157         mDevice.wait(Until.findObject(By.descContains("Navigate up")), LONG_TIMEOUT);
158         // Retrieve the quick settings CSV string and verify that the newly
159         // added item is present.
160         String quickSettingsList =
161                 Settings.Secure.getString(
162                         mInstrumentation.getContext().getContentResolver(), SYSUI_QS_TILES_SETTING);
163         Assert.assertTrue(
164                 quickSettingTile + " not present in qs tiles after addition.",
165                 quickSettingsList.contains(quickSettingTileToCheckForInCSV));
166     }
167 
168     /** Sets default quick settings tile list pre-load in SystemUI resource. */
setQuickSettingsDefaultTiles()169     public void setQuickSettingsDefaultTiles() {
170         modifyQSTileList(mDefaultQSTileList);
171     }
172 
173     /** Gets the default list of QuickSettings */
getQSDefaultTileList()174     public List<String> getQSDefaultTileList() {
175         return mDefaultQSTileList;
176     }
177 
178     /**
179      * Set the tileName to be the first item for QS tiles.
180      *
181      * @param tileName tile name that will been set to the first position.
182      */
setFirstQS(@onNull String tileName)183     public void setFirstQS(@NonNull String tileName) {
184         mPreviousQSTileList = getCurrentTilesList();
185 
186         ArrayList<String> list = new ArrayList<>(mPreviousQSTileList);
187         for (int i = 0; i < list.size(); ++i) {
188             if (TextUtils.equals(tileName, list.get(i))) {
189                 list.remove(i);
190                 break;
191             }
192         }
193         list.add(0, tileName);
194         modifyQSTileList(list);
195     }
196 
getCurrentTilesList()197     public List<String> getCurrentTilesList() {
198         String previousQSTiles =
199                 Settings.Secure.getString(
200                         mInstrumentation.getContext().getContentResolver(), SYSUI_QS_TILES_SETTING);
201         return Arrays.asList(previousQSTiles.split(","));
202     }
203 
204     /** Reset to previous QS tile list if exist */
resetToPreviousQSTileList()205     public void resetToPreviousQSTileList() {
206         if (mPreviousQSTileList == null) {
207             return;
208         }
209         modifyQSTileList(mPreviousQSTileList);
210     }
211 
212     /**
213      * Sets customized tile list to secure settings entry 'sysui_qs_tiles' directly.
214      *
215      * @param list The quick settings tile list to be set
216      */
modifyQSTileList(@onNull List<String> list)217     public void modifyQSTileList(@NonNull List<String> list) {
218         if (list.isEmpty()) {
219             return;
220         }
221 
222         try {
223             String settings = String.join(",", list);
224             mCommandsHelper.executeShellCommand(SET_QS_TILES_COMMAND + settings);
225             Thread.sleep(LONG_TIMEOUT);
226         } catch (Resources.NotFoundException | InterruptedException e) {
227             Log.e(LOG_TAG, "modifyQSTileList fails!", e);
228         }
229     }
230 
231     /** Opens quick settings panel through {@link UiDevice#openQuickSettings()} */
launchQuickSetting()232     public void launchQuickSetting() {
233         mDevice.pressHome();
234         mDevice.openQuickSettings();
235         // Quick Settings isn't always open when this is complete. Explicitly wait for the Quick
236         // Settings footer to make sure that the buttons are accessible when the bar is open and
237         // this call is complete.
238         mDevice.wait(Until.findObject(FOOTER_SELECTOR), SHORT_TIMEOUT);
239         // Wait an extra bit for the animation to complete. If we return to early, future callers
240         // that are trying to find the location of the footer will get incorrect coordinates
241         mDevice.waitForIdle(LONG_TIMEOUT);
242     }
243 
swipeUp()244     public void swipeUp() throws Exception {
245         mDevice.swipe(mDevice.getDisplayWidth() / 2, mDevice.getDisplayHeight(),
246                 mDevice.getDisplayWidth() / 2, 0, 30);
247         Thread.sleep(SHORT_TIMEOUT);
248     }
249 
swipeDown()250     public void swipeDown() throws Exception {
251         mDevice.swipe(
252                 mDevice.getDisplayWidth() / 2,
253                 0,
254                 mDevice.getDisplayWidth() / 2,
255                 mDevice.getDisplayHeight(),
256                 20);
257         Thread.sleep(SHORT_TIMEOUT);
258     }
259 
swipeLeft()260     public void swipeLeft() {
261         mDevice.swipe(mDevice.getDisplayWidth() / 2, mDevice.getDisplayHeight() / 2, 0,
262                 mDevice.getDisplayHeight() / 2, 5);
263     }
264 }
265