1 /*
2 * Copyright (C) 2020 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 com.android.server.wm.flicker.helpers
18
19 import android.content.Context
20 import android.graphics.Point
21 import android.graphics.Rect
22 import android.view.Surface
23 import android.view.WindowManager
24 import androidx.test.platform.app.InstrumentationRegistry
25 import com.android.server.wm.traces.common.layers.Display
26 import com.android.server.wm.traces.common.region.Region
27
isRotatednull28 fun Int.isRotated() = this == Surface.ROTATION_90 || this == Surface.ROTATION_270
29
30 object WindowUtils {
31 /**
32 * Helper functions to retrieve system window sizes and positions.
33 */
34 private val context
35 get() = InstrumentationRegistry.getInstrumentation().context
36
37 private val resources
38 get() = context.getResources()
39
40 /**
41 * Get the display bounds
42 */
43 val displayBounds: Rect
44 get() {
45 val display = Point()
46 val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
47 wm.defaultDisplay.getRealSize(display)
48 return Rect(0, 0, display.x, display.y)
49 }
50
51 /**
52 * Gets the current display rotation
53 */
54 val displayRotation: Int
55 get() {
56 val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
57 return wm.defaultDisplay.rotation
58 }
59
60 /**
61 * Get the display bounds when the device is at a specific rotation
62 *
63 * @param requestedRotation Device rotation
64 */
65 fun getDisplayBounds(requestedRotation: Int): Region {
66 val displayIsRotated = displayRotation.isRotated()
67 val requestedDisplayIsRotated = requestedRotation.isRotated()
68
69 // if the current orientation changes with the requested rotation,
70 // flip height and width of display bounds.
71 return if (displayIsRotated != requestedDisplayIsRotated) {
72 Region.from(0, 0, displayBounds.height(), displayBounds.width())
73 } else {
74 Region.from(0, 0, displayBounds.width(), displayBounds.height())
75 }
76 }
77
78 /**
79 * Gets the expected status bar position for a specific display
80 *
81 * @param display the main display
82 */
83 fun getStatusBarPosition(display: Display): Region {
84 val resourceName = if (!display.transform.getRotation().isRotated()) {
85 "status_bar_height_portrait"
86 } else {
87 "status_bar_height_landscape"
88 }
89 val resourceId = resources.getIdentifier(resourceName, "dimen", "android")
90 val height = resources.getDimensionPixelSize(resourceId)
91 return Region.from(0, 0, display.layerStackSpace.width, height)
92 }
93
94 /**
95 * Gets the expected navigation bar position for a specific display
96 *
97 * @param display the main display
98 */
99 fun getNavigationBarPosition(display: Display): Region {
100 return getNavigationBarPosition(display, isGesturalNavigationEnabled)
101 }
102
103 /**
104 * Gets the expected navigation bar position for a specific display
105 *
106 * @param display the main display
107 * @param isGesturalNavigation whether gestural navigation is enabled
108 */
109 fun getNavigationBarPosition(display: Display, isGesturalNavigation: Boolean): Region {
110 val navBarWidth = getDimensionPixelSize("navigation_bar_width")
111 val navBarHeight = navigationBarFrameHeight
112 val displayHeight = display.layerStackSpace.height
113 val displayWidth = display.layerStackSpace.width
114 val requestedRotation = display.transform.getRotation()
115
116 return when {
117 // nav bar is at the bottom of the screen
118 requestedRotation in listOf(Surface.ROTATION_0, Surface.ROTATION_180) ||
119 isGesturalNavigation ->
120 Region.from(0, displayHeight - navBarHeight, displayWidth, displayHeight)
121 // nav bar is at the right side
122 requestedRotation == Surface.ROTATION_90 ->
123 Region.from(displayWidth - navBarWidth, 0, displayWidth, displayHeight)
124 // nav bar is at the left side
125 requestedRotation == Surface.ROTATION_270 ->
126 Region.from(0, 0, navBarWidth, displayHeight)
127 else -> error("Unknown rotation $requestedRotation")
128 }
129 }
130
131 /**
132 * Estimate the navigation bar position at a specific rotation
133 *
134 * @param requestedRotation Device rotation
135 */
136 fun estimateNavigationBarPosition(requestedRotation: Int): Region {
137 val displayBounds = displayBounds
138 val displayWidth: Int
139 val displayHeight: Int
140 if (!requestedRotation.isRotated()) {
141 displayWidth = displayBounds.width()
142 displayHeight = displayBounds.height()
143 } else {
144 // swap display dimensions in landscape or seascape mode
145 displayWidth = displayBounds.height()
146 displayHeight = displayBounds.width()
147 }
148 val navBarWidth = getDimensionPixelSize("navigation_bar_width")
149 val navBarHeight = navigationBarFrameHeight
150
151 return when {
152 // nav bar is at the bottom of the screen
153 requestedRotation in listOf(Surface.ROTATION_0, Surface.ROTATION_180) ||
154 isGesturalNavigationEnabled ->
155 Region.from(0, displayHeight - navBarHeight, displayWidth, displayHeight)
156 // nav bar is at the right side
157 requestedRotation == Surface.ROTATION_90 ->
158 Region.from(displayWidth - navBarWidth, 0, displayWidth, displayHeight)
159 // nav bar is at the left side
160 requestedRotation == Surface.ROTATION_270 ->
161 Region.from(0, 0, navBarWidth, displayHeight)
162 else -> error("Unknown rotation $requestedRotation")
163 }
164 }
165
166 /**
167 * Checks if the device uses gestural navigation
168 */
169 val isGesturalNavigationEnabled: Boolean
170 get() {
171 val resourceId = resources
172 .getIdentifier("config_navBarInteractionMode", "integer", "android")
173 return resources.getInteger(resourceId) == 2 /* NAV_BAR_MODE_GESTURAL */
174 }
175
176 fun getDimensionPixelSize(resourceName: String): Int {
177 val resourceId = resources.getIdentifier(resourceName, "dimen", "android")
178 return resources.getDimensionPixelSize(resourceId)
179 }
180
181 /**
182 * Gets the navigation bar frame height
183 */
184 val navigationBarFrameHeight: Int
185 get() {
186 return getDimensionPixelSize("navigation_bar_frame_height")
187 }
188
189 /**
190 * Split screen divider inset height
191 */
192 val dockedStackDividerInset: Int
193 get() {
194 val resourceId = resources
195 .getIdentifier("docked_stack_divider_insets", "dimen", "android")
196 return resources.getDimensionPixelSize(resourceId)
197 }
198 }
199