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