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