• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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