• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11  * express or implied. See the License for the specific language governing permissions and
12  * limitations under the License.
13  */
14 
15 package android.accessibilityservice.cts.utils;
16 
17 import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.findWindowByTitleAndDisplay;
18 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
19 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
20 import static android.view.Display.DEFAULT_DISPLAY;
21 
22 import android.app.Activity;
23 import android.app.UiAutomation;
24 import android.content.Context;
25 import android.graphics.PixelFormat;
26 import android.graphics.Rect;
27 import android.hardware.display.DisplayManager;
28 import android.hardware.display.VirtualDisplay;
29 import android.media.ImageReader;
30 import android.os.Looper;
31 import android.os.SystemClock;
32 import android.util.DisplayMetrics;
33 import android.view.Display;
34 import android.view.InputDevice;
35 import android.view.MotionEvent;
36 import android.view.WindowInsets;
37 
38 import com.android.compatibility.common.util.TestUtils;
39 
40 /**
41  * Utilities needed when interacting with the display
42  */
43 public class DisplayUtils {
44     private static final int DISPLAY_ADDED_TIMEOUT_MS = 5000;
45     // Tolerance that allows for rounding differences in how various parts of
46     // Android calculate on-screen bounds given non-integer screen scaling or
47     // dp/pixel density.
48     private static final int BOUNDS_IN_SCREEN_TOLERANCE_PX = 1;
49 
getStatusBarHeight(Activity activity)50     public static int getStatusBarHeight(Activity activity) {
51         return activity.getWindow().getDecorView().getRootWindowInsets()
52                 .getInsets(WindowInsets.Type.statusBars()).top;
53     }
54 
55     /**
56      * Checks if the bounds origin match the provided point, to a tolerance of
57      * {@link #BOUNDS_IN_SCREEN_TOLERANCE_PX} pixels.
58      */
fuzzyBoundsInScreenSameOrigin(int[] origin, Rect bounds)59     public static boolean fuzzyBoundsInScreenSameOrigin(int[] origin, Rect bounds) {
60         return Math.abs((origin[0]) - bounds.left) <= BOUNDS_IN_SCREEN_TOLERANCE_PX
61                 && Math.abs((origin[1]) - bounds.top) <= BOUNDS_IN_SCREEN_TOLERANCE_PX;
62     }
63 
64     /**
65      * Checks if the bounds origins match each other, to a tolerance of
66      * {@link #BOUNDS_IN_SCREEN_TOLERANCE_PX} pixels.
67      */
fuzzyBoundsInScreenSameOrigin(Rect boundsA, Rect boundsB)68     public static boolean fuzzyBoundsInScreenSameOrigin(Rect boundsA, Rect boundsB) {
69         return Math.abs((boundsA.left) - boundsB.left) <= BOUNDS_IN_SCREEN_TOLERANCE_PX
70                 && Math.abs((boundsA.top) - boundsB.top) <= BOUNDS_IN_SCREEN_TOLERANCE_PX;
71     }
72 
73     /**
74      * Checks if a larger rect contains another, to a tolerance of
75      * {@link #BOUNDS_IN_SCREEN_TOLERANCE_PX} pixels.
76      */
fuzzyBoundsInScreenContains(Rect larger, Rect smaller)77     public static boolean fuzzyBoundsInScreenContains(Rect larger, Rect smaller) {
78         final Rect largerExpanded = new Rect(larger);
79         largerExpanded.inset(-BOUNDS_IN_SCREEN_TOLERANCE_PX, -BOUNDS_IN_SCREEN_TOLERANCE_PX);
80         return largerExpanded.contains(smaller);
81     }
82 
83     public static class VirtualDisplaySession implements AutoCloseable {
84         private VirtualDisplay mVirtualDisplay;
85         private ImageReader mReader;
86 
createDisplay(Context context, int width, int height, int density, boolean isPrivate)87         public Display createDisplay(Context context, int width, int height, int density,
88                 boolean isPrivate) {
89             if (mReader != null) {
90                 throw new IllegalStateException(
91                         "Only one display can be created during this session.");
92             }
93             mReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888,
94                     1 /* maxImages */);
95             int flags = isPrivate ? 0
96                     :(VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | VIRTUAL_DISPLAY_FLAG_PUBLIC);
97             mVirtualDisplay = context.getSystemService(DisplayManager.class).createVirtualDisplay(
98                     "A11yDisplay", width, height, density, mReader.getSurface(), flags);
99             return mVirtualDisplay.getDisplay();
100         }
101 
102         @Override
close()103         public void close() {
104             if (mVirtualDisplay != null) {
105                 mVirtualDisplay.release();
106             }
107             if (mReader != null) {
108                 mReader.close();
109             }
110         }
111 
112         /**
113          * Creates a virtual display having same size with default display and waits until it's
114          * in display list. The density of the virtual display is based on
115          * {@link DisplayMetrics#xdpi} so that the threshold of gesture detection is same as
116          * the default display's.
117          *
118          * @param context
119          * @param isPrivate if this display is a private display.
120          * @return virtual display.
121          *
122          * @throws IllegalStateException if called from main thread.
123          */
createDisplayWithDefaultDisplayMetricsAndWait(Context context, boolean isPrivate)124         public Display createDisplayWithDefaultDisplayMetricsAndWait(Context context,
125                 boolean isPrivate) {
126             if (Looper.myLooper() == Looper.getMainLooper()) {
127                 throw new IllegalStateException("Should not call from main thread");
128             }
129 
130             final Object waitObject = new Object();
131             final DisplayManager.DisplayListener listener = new DisplayManager.DisplayListener() {
132                 @Override
133                 public void onDisplayAdded(int i) {
134                     synchronized (waitObject) {
135                         waitObject.notifyAll();
136                     }
137                 }
138 
139                 @Override
140                 public void onDisplayRemoved(int i) {
141                 }
142 
143                 @Override
144                 public void onDisplayChanged(int i) {
145                 }
146             };
147             final DisplayManager displayManager = (DisplayManager) context.getSystemService(
148                     Context.DISPLAY_SERVICE);
149             displayManager.registerDisplayListener(listener, null);
150 
151             final DisplayMetrics metrics = new DisplayMetrics();
152             displayManager.getDisplay(DEFAULT_DISPLAY).getRealMetrics(metrics);
153             final Display display = createDisplay(context, metrics.widthPixels,
154                     metrics.heightPixels, (int) metrics.xdpi, isPrivate);
155 
156             try {
157                 TestUtils.waitOn(waitObject,
158                         () -> displayManager.getDisplay(display.getDisplayId()) != null,
159                         DISPLAY_ADDED_TIMEOUT_MS,
160                         String.format("wait for virtual display %d adding", display.getDisplayId()));
161             } finally {
162                 displayManager.unregisterDisplayListener(listener);
163             }
164             return display;
165         }
166     }
167 
touchDisplay(UiAutomation uiAutomation, int displayId, CharSequence activityTitle)168     public static void touchDisplay(UiAutomation uiAutomation, int displayId,
169             CharSequence activityTitle) {
170         final Rect areaOfActivityWindowOnDisplay = new Rect();
171         findWindowByTitleAndDisplay(uiAutomation, activityTitle, displayId)
172                 .getBoundsInScreen(areaOfActivityWindowOnDisplay);
173 
174         final int xOnScreen =
175                 areaOfActivityWindowOnDisplay.centerX();
176         final int yOnScreen =
177                 areaOfActivityWindowOnDisplay.centerY();
178         final long downEventTime = SystemClock.uptimeMillis();
179         final MotionEvent downEvent = MotionEvent.obtain(downEventTime,
180                 downEventTime, MotionEvent.ACTION_DOWN, xOnScreen, yOnScreen, 0);
181         downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
182         downEvent.setDisplayId(displayId);
183         uiAutomation.injectInputEvent(downEvent, true);
184 
185         final long upEventTime = downEventTime + 10;
186         final MotionEvent upEvent = MotionEvent.obtain(downEventTime, upEventTime,
187                 MotionEvent.ACTION_UP, xOnScreen, yOnScreen, 0);
188         upEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
189         upEvent.setDisplayId(displayId);
190         uiAutomation.injectInputEvent(upEvent, true);
191     }
192 }
193