1 /*
2  * Copyright (C) 2017 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.preference.cts;
18 
19 import android.app.Instrumentation;
20 import android.app.UiAutomation;
21 import android.app.UiModeManager;
22 import android.content.Context;
23 import android.content.res.Configuration;
24 import android.graphics.Bitmap;
25 import android.support.test.uiautomator.By;
26 import android.support.test.uiautomator.UiDevice;
27 import android.support.test.uiautomator.UiObject2;
28 import android.support.test.uiautomator.UiObjectNotFoundException;
29 import android.support.test.uiautomator.UiScrollable;
30 import android.support.test.uiautomator.UiSelector;
31 import android.support.test.uiautomator.Until;
32 
33 import androidx.test.platform.app.InstrumentationRegistry;
34 
35 /**
36  * Collection of helper utils for testing preferences.
37  */
38 public class TestUtils {
39 
40     public final UiDevice mDevice;
41 
42     private final Context mContext;
43     private final Instrumentation mInstrumentation;
44     private final String mPackageName;
45     private final UiAutomation mAutomation;
46     private int mStatusBarHeight = -1;
47     private int mNavigationBarHeight = -1;
48 
TestUtils()49     TestUtils() {
50         mInstrumentation = InstrumentationRegistry.getInstrumentation();
51         mContext = mInstrumentation.getTargetContext();
52         mPackageName = mContext.getPackageName();
53         mDevice = UiDevice.getInstance(mInstrumentation);
54         mAutomation = mInstrumentation.getUiAutomation();
55     }
56 
waitForIdle()57     void waitForIdle() {
58         mDevice.waitForIdle(1000);
59     }
60 
takeScreenshot()61     Bitmap takeScreenshot() {
62         // Only take a screenshot once the screen is stable enough.
63         waitForIdle();
64 
65         Bitmap bt = mAutomation.takeScreenshot();
66 
67         // Crop-out the status bar to avoid flakiness with changing notifications / time.
68         int statusBarHeight = getStatusBarHeight();
69 
70         // Crop-out the navigation bar to avoid flakiness with button animations.
71         int navigationBarHeight = getNavigationBarHeight();
72 
73         // Crop-out the right side for the scrollbar which may or may not be visible.
74         // On wearable devices the scroll bar is a curve and occupies 20% of the right side.
75         int xToCut = isOnWatchUiMode() ? bt.getWidth() / 5 : bt.getWidth() / 20;
76         int yToCut = statusBarHeight;
77 
78         if (isLandscape()) {
79             xToCut += navigationBarHeight;
80         } else {
81             yToCut += navigationBarHeight;
82         }
83 
84         bt = Bitmap.createBitmap(
85             bt, 0, statusBarHeight, bt.getWidth() - xToCut, bt.getHeight() - yToCut);
86 
87         return bt;
88     }
89 
tapOnViewWithText(String text)90     void tapOnViewWithText(String text) {
91         UiObject2 object2 = getTextObject(text);
92         if (object2 != null) {
93             object2.click();
94         } else {
95             scrollToAndGetTextObject(text);
96             getTextObject(text).click();
97         }
98         waitForIdle();
99     }
100 
isTextShown(String text)101     boolean isTextShown(String text) {
102         if (getTextObject(text) != null) {
103             return true;
104         }
105 
106         return scrollToAndGetTextObject(text);
107     }
108 
isTextHidden(String text)109     boolean isTextHidden(String text) {
110         return getTextObject(text) == null;
111     }
112 
isTextFocused(String text)113     boolean isTextFocused(String text) {
114         UiObject2 object = getTextObject(text);
115         return object != null && object.isFocused();
116     }
117 
isOnWatchUiMode()118     boolean isOnWatchUiMode() {
119         UiModeManager uiModeManager = mContext.getSystemService(UiModeManager.class);
120         return uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_WATCH;
121     }
122 
getStatusBarHeight()123     private int getStatusBarHeight() {
124         // Cache the result to keep it fast.
125         if (mStatusBarHeight >= 0) {
126             return mStatusBarHeight;
127         }
128 
129         int resourceId = mInstrumentation.getTargetContext().getResources()
130                 .getIdentifier("status_bar_height", "dimen", "android");
131         if (resourceId > 0) {
132             mStatusBarHeight = mInstrumentation.getTargetContext().getResources()
133                     .getDimensionPixelSize(resourceId);
134         } else {
135             mStatusBarHeight = 0;
136         }
137         return mStatusBarHeight;
138     }
139 
getNavigationBarHeight()140     private int getNavigationBarHeight() {
141         // Cache the result to keep it fast.
142         if (mNavigationBarHeight >= 0) {
143             return mNavigationBarHeight;
144         }
145 
146         int resourceId = mInstrumentation.getTargetContext().getResources()
147                 .getIdentifier("navigation_bar_height", "dimen", "android");
148         if (resourceId > 0) {
149             mNavigationBarHeight = mInstrumentation.getTargetContext().getResources()
150                     .getDimensionPixelSize(resourceId);
151         } else {
152             mNavigationBarHeight = 0;
153         }
154         return mNavigationBarHeight;
155     }
156 
isLandscape()157     private boolean isLandscape() {
158         return mInstrumentation.getTargetContext().getResources().getConfiguration().orientation
159             == Configuration.ORIENTATION_LANDSCAPE;
160     }
161 
getTextObject(String text)162     private UiObject2 getTextObject(String text) {
163         // Wait for up to 1 second to find the object. Returns null if the object cannot be found.
164         return mDevice.wait(Until.findObject(By.text(text).pkg(mPackageName)), 1000);
165     }
166 
scrollToAndGetTextObject(String text)167     private boolean scrollToAndGetTextObject(String text) {
168         UiScrollable scroller = new UiScrollable(new UiSelector().scrollable(true));
169         try {
170             // Swipe far away from the edges to avoid triggering navigation gestures
171             scroller.setSwipeDeadZonePercentage(0.25);
172             return scroller.scrollTextIntoView(text);
173         } catch (UiObjectNotFoundException e) {
174             throw new AssertionError("View with text '" + text + "' was not found!", e);
175         }
176     }
177 }
178