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