xref: /aosp_15_r20/platform_testing/libraries/flicker/utils/src/android/tools/traces/wm/DisplayContent.kt (revision dd0948b35e70be4c0246aabd6c72554a5eb8b22a)
1 /*
<lambda>null2  * Copyright (C) 2023 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 android.tools.traces.wm
18 
19 import android.graphics.Rect
20 import android.tools.PlatformConsts
21 import android.tools.Rotation
22 import android.tools.traces.component.IComponentMatcher
23 import android.tools.traces.wm.Utils.collectDescendants
24 import kotlin.math.min
25 
26 /**
27  * Represents a display content in the window manager hierarchy
28  *
29  * This is a generic object that is reused by both Flicker and Winscope and cannot access internal
30  * Java/Android functionality
31  *
32  * @property displayRect The logical display rect.
33  * @property stableBounds The logical display bounds excluding the navigation and status bar areas.
34  */
35 class DisplayContent(
36     val displayId: Int,
37     val focusedRootTaskId: Int,
38     val resumedActivity: String,
39     val singleTaskInstance: Boolean,
40     val defaultPinnedStackBounds: Rect,
41     val pinnedStackMovementBounds: Rect,
42     val displayRect: Rect,
43     val appRect: Rect,
44     val dpi: Int,
45     val flags: Int,
46     val stableBounds: Rect,
47     val surfaceSize: Int,
48     val focusedApp: String,
49     val lastTransition: String,
50     val appTransitionState: String,
51     val rotation: Rotation,
52     val lastOrientation: Int,
53     val cutout: DisplayCutout?,
54     val insetsSourceProviders: Array<InsetsSourceProvider>,
55     private val windowContainer: WindowContainer,
56 ) : WindowContainer by windowContainer {
57     override val name: String = displayId.toString()
58     override val isVisible: Boolean = false
59 
60     val isTablet: Boolean
61         get() {
62             val smallestWidth =
63                 dpiFromPx(min(displayRect.width().toFloat(), displayRect.height().toFloat()), dpi)
64             return smallestWidth >= PlatformConsts.TABLET_MIN_DPS
65         }
66 
67     val rootTasks: Collection<Task>
68         get() {
69             val tasks = collectDescendants<Task> { it.isRootTask }.toMutableList()
70             // TODO(b/149338177): figure out how CTS tests deal with organizer. For now,
71             //                    don't treat them as regular stacks
72             val rootOrganizedTasks = mutableListOf<Task>()
73             val reversedTaskList = tasks.reversed()
74             reversedTaskList.forEach { task ->
75                 // Skip tasks created by an organizer
76                 if (task.createdByOrganizer) {
77                     tasks.remove(task)
78                     rootOrganizedTasks.add(task)
79                 }
80             }
81             // Add root tasks controlled by an organizer
82             rootOrganizedTasks.reversed().forEach { task ->
83                 tasks.addAll(task.children.reversed().map { it as Task })
84             }
85 
86             return tasks
87         }
88 
89     /**
90      * @param componentMatcher Components to search
91      * @return if [componentMatcher] matches any activity
92      */
93     fun containsActivity(componentMatcher: IComponentMatcher): Boolean =
94         rootTasks.any { it.containsActivity(componentMatcher) }
95 
96     /**
97      * @param componentMatcher Components to search
98      * @return THe [DisplayArea] matching [componentMatcher], or null if none matches
99      */
100     fun getTaskDisplayArea(componentMatcher: IComponentMatcher): DisplayArea? {
101         val taskDisplayAreas =
102             this.collectDescendants<DisplayArea> { it.isTaskDisplayArea }
103                 .filter { it.containsActivity(componentMatcher) }
104 
105         if (taskDisplayAreas.size > 1) {
106             throw IllegalArgumentException(
107                 "There must be exactly one activity among all TaskDisplayAreas."
108             )
109         }
110 
111         return taskDisplayAreas.firstOrNull()
112     }
113 
114     override fun toString(): String {
115         return "${this::class.simpleName} #$displayId: name=$title mDisplayRect=$displayRect " +
116             "mAppRect=$appRect mFlags=$flags"
117     }
118 
119     override fun equals(other: Any?): Boolean {
120         if (this === other) return true
121         if (other !is DisplayContent) return false
122         if (!super.equals(other)) return false
123 
124         if (displayId != other.displayId) return false
125         if (focusedRootTaskId != other.focusedRootTaskId) return false
126         if (resumedActivity != other.resumedActivity) return false
127         if (defaultPinnedStackBounds != other.defaultPinnedStackBounds) return false
128         if (pinnedStackMovementBounds != other.pinnedStackMovementBounds) return false
129         if (stableBounds != other.stableBounds) return false
130         if (displayRect != other.displayRect) return false
131         if (appRect != other.appRect) return false
132         if (dpi != other.dpi) return false
133         if (flags != other.flags) return false
134         if (focusedApp != other.focusedApp) return false
135         if (lastTransition != other.lastTransition) return false
136         if (appTransitionState != other.appTransitionState) return false
137         if (rotation != other.rotation) return false
138         if (lastOrientation != other.lastOrientation) return false
139         if (cutout != other.cutout) return false
140         if (name != other.name) return false
141         if (singleTaskInstance != other.singleTaskInstance) return false
142         if (surfaceSize != other.surfaceSize) return false
143         if (windowContainer != other.windowContainer) return false
144 
145         return true
146     }
147 
148     override fun hashCode(): Int {
149         var result = super.hashCode()
150         result = 31 * result + displayId
151         result = 31 * result + focusedRootTaskId
152         result = 31 * result + resumedActivity.hashCode()
153         result = 31 * result + singleTaskInstance.hashCode()
154         result = 31 * result + defaultPinnedStackBounds.hashCode()
155         result = 31 * result + pinnedStackMovementBounds.hashCode()
156         result = 31 * result + displayRect.hashCode()
157         result = 31 * result + appRect.hashCode()
158         result = 31 * result + dpi
159         result = 31 * result + flags
160         result = 31 * result + stableBounds.hashCode()
161         result = 31 * result + surfaceSize
162         result = 31 * result + focusedApp.hashCode()
163         result = 31 * result + lastTransition.hashCode()
164         result = 31 * result + appTransitionState.hashCode()
165         result = 31 * result + rotation.value
166         result = 31 * result + lastOrientation
167         result = 31 * result + cutout.hashCode()
168         result = 31 * result + name.hashCode()
169         result = 31 * result + isVisible.hashCode()
170         result = 31 * result + windowContainer.hashCode()
171         return result
172     }
173 
174     companion object {
175         /** From [android.util.DisplayMetrics] */
176         const val DENSITY_DEFAULT = 160f
177 
178         /** From [com.android.systemui.shared.recents.utilities.Utilities] */
179         const val TABLET_MIN_DPS = 600f
180 
181         @JvmStatic
182         fun dpiFromPx(size: Float, densityDpi: Int): Float {
183             val densityRatio: Float = densityDpi.toFloat() / DENSITY_DEFAULT
184             return size / densityRatio
185         }
186     }
187 }
188