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 getNavBarHeight(Activity activity)55 public static int getNavBarHeight(Activity activity) { 56 return activity.getWindow().getDecorView().getRootWindowInsets() 57 .getInsets(WindowInsets.Type.statusBars()).bottom; 58 } 59 60 /** 61 * Checks if the bounds origin match the provided point, to a tolerance of 62 * {@link #BOUNDS_IN_SCREEN_TOLERANCE_PX} pixels. 63 */ fuzzyBoundsInScreenSameOrigin(int[] origin, Rect bounds)64 public static boolean fuzzyBoundsInScreenSameOrigin(int[] origin, Rect bounds) { 65 return Math.abs((origin[0]) - bounds.left) <= BOUNDS_IN_SCREEN_TOLERANCE_PX 66 && Math.abs((origin[1]) - bounds.top) <= BOUNDS_IN_SCREEN_TOLERANCE_PX; 67 } 68 69 /** 70 * Checks if the bounds origins match each other, to a tolerance of 71 * {@link #BOUNDS_IN_SCREEN_TOLERANCE_PX} pixels. 72 */ fuzzyBoundsInScreenSameOrigin(Rect boundsA, Rect boundsB)73 public static boolean fuzzyBoundsInScreenSameOrigin(Rect boundsA, Rect boundsB) { 74 return Math.abs((boundsA.left) - boundsB.left) <= BOUNDS_IN_SCREEN_TOLERANCE_PX 75 && Math.abs((boundsA.top) - boundsB.top) <= BOUNDS_IN_SCREEN_TOLERANCE_PX; 76 } 77 78 /** 79 * Checks if a larger rect contains another, to a tolerance of 80 * {@link #BOUNDS_IN_SCREEN_TOLERANCE_PX} pixels. 81 */ fuzzyBoundsInScreenContains(Rect larger, Rect smaller)82 public static boolean fuzzyBoundsInScreenContains(Rect larger, Rect smaller) { 83 final Rect largerExpanded = new Rect(larger); 84 largerExpanded.inset(-BOUNDS_IN_SCREEN_TOLERANCE_PX, -BOUNDS_IN_SCREEN_TOLERANCE_PX); 85 return largerExpanded.contains(smaller); 86 } 87 88 public static class VirtualDisplaySession implements AutoCloseable { 89 private VirtualDisplay mVirtualDisplay; 90 private ImageReader mReader; 91 createDisplay(Context context, int width, int height, int density, boolean isPrivate)92 public Display createDisplay(Context context, int width, int height, int density, 93 boolean isPrivate) { 94 if (mReader != null) { 95 throw new IllegalStateException( 96 "Only one display can be created during this session."); 97 } 98 mReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 99 1 /* maxImages */); 100 int flags = isPrivate ? 0 101 :(VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | VIRTUAL_DISPLAY_FLAG_PUBLIC); 102 mVirtualDisplay = context.getSystemService(DisplayManager.class).createVirtualDisplay( 103 "A11yDisplay", width, height, density, mReader.getSurface(), flags); 104 return mVirtualDisplay.getDisplay(); 105 } 106 107 @Override close()108 public void close() { 109 if (mVirtualDisplay != null) { 110 mVirtualDisplay.release(); 111 } 112 if (mReader != null) { 113 mReader.close(); 114 } 115 } 116 117 /** 118 * Creates a virtual display having same size with default display and waits until it's 119 * in display list. The density of the virtual display is based on 120 * {@link DisplayMetrics#xdpi} so that the threshold of gesture detection is same as 121 * the default display's. 122 * 123 * @param context 124 * @param isPrivate if this display is a private display. 125 * @return virtual display. 126 * 127 * @throws IllegalStateException if called from main thread. 128 */ createDisplayWithDefaultDisplayMetricsAndWait(Context context, boolean isPrivate)129 public Display createDisplayWithDefaultDisplayMetricsAndWait(Context context, 130 boolean isPrivate) { 131 if (Looper.myLooper() == Looper.getMainLooper()) { 132 throw new IllegalStateException("Should not call from main thread"); 133 } 134 135 final Object waitObject = new Object(); 136 final DisplayManager.DisplayListener listener = new DisplayManager.DisplayListener() { 137 @Override 138 public void onDisplayAdded(int i) { 139 synchronized (waitObject) { 140 waitObject.notifyAll(); 141 } 142 } 143 144 @Override 145 public void onDisplayRemoved(int i) { 146 } 147 148 @Override 149 public void onDisplayChanged(int i) { 150 } 151 }; 152 final DisplayManager displayManager = (DisplayManager) context.getSystemService( 153 Context.DISPLAY_SERVICE); 154 displayManager.registerDisplayListener(listener, null); 155 156 final DisplayMetrics metrics = new DisplayMetrics(); 157 displayManager.getDisplay(DEFAULT_DISPLAY).getRealMetrics(metrics); 158 final Display display = createDisplay(context, metrics.widthPixels, 159 metrics.heightPixels, (int) metrics.xdpi, isPrivate); 160 161 try { 162 TestUtils.waitOn(waitObject, 163 () -> displayManager.getDisplay(display.getDisplayId()) != null, 164 DISPLAY_ADDED_TIMEOUT_MS, 165 String.format("wait for virtual display %d adding", display.getDisplayId())); 166 } finally { 167 displayManager.unregisterDisplayListener(listener); 168 } 169 return display; 170 } 171 } 172 touchDisplay(UiAutomation uiAutomation, int displayId, CharSequence activityTitle)173 public static void touchDisplay(UiAutomation uiAutomation, int displayId, 174 CharSequence activityTitle) { 175 final Rect areaOfActivityWindowOnDisplay = new Rect(); 176 findWindowByTitleAndDisplay(uiAutomation, activityTitle, displayId) 177 .getBoundsInScreen(areaOfActivityWindowOnDisplay); 178 179 final int xOnScreen = 180 areaOfActivityWindowOnDisplay.centerX(); 181 final int yOnScreen = 182 areaOfActivityWindowOnDisplay.centerY(); 183 final long downEventTime = SystemClock.uptimeMillis(); 184 final MotionEvent downEvent = MotionEvent.obtain(downEventTime, 185 downEventTime, MotionEvent.ACTION_DOWN, xOnScreen, yOnScreen, 0); 186 downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN); 187 downEvent.setDisplayId(displayId); 188 uiAutomation.injectInputEvent(downEvent, true); 189 190 final long upEventTime = downEventTime + 10; 191 final MotionEvent upEvent = MotionEvent.obtain(downEventTime, upEventTime, 192 MotionEvent.ACTION_UP, xOnScreen, yOnScreen, 0); 193 upEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN); 194 upEvent.setDisplayId(displayId); 195 uiAutomation.injectInputEvent(upEvent, true); 196 } 197 } 198