xref: /aosp_15_r20/platform_testing/libraries/flicker/utils/src/android/tools/traces/wm/Task.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.traces.component.IComponentMatcher
21 
22 /**
23  * Represents a task in the window manager hierarchy
24  *
25  * This is a generic object that is reused by both Flicker and Winscope and cannot access internal
26  * Java/Android functionality
27  */
28 class Task(
29     override val activityType: Int,
30     override val isFullscreen: Boolean,
31     override val bounds: Rect,
32     val taskId: Int,
33     val rootTaskId: Int,
34     val displayId: Int,
35     val lastNonFullscreenBounds: Rect,
36     val realActivity: String,
37     val origActivity: String,
38     val resizeMode: Int,
39     private val _resumedActivity: String,
40     var animatingBounds: Boolean,
41     val surfaceWidth: Int,
42     val surfaceHeight: Int,
43     val createdByOrganizer: Boolean,
44     val minWidth: Int,
45     val minHeight: Int,
46     private val windowContainer: WindowContainer,
47 ) : WindowContainer by windowContainer {
48     override val isVisible: Boolean = false
49     override val name: String = taskId.toString()
50     override val isEmpty: Boolean
51         get() = tasks.isEmpty() && activities.isEmpty()
52 
53     override val stableId: String
54         get() = "${this::class.simpleName} $token $title $taskId"
55 
56     val isRootTask: Boolean
57         get() = taskId == rootTaskId
58 
59     val tasks: Collection<Task>
60         get() = this.children.reversed().filterIsInstance<Task>()
61 
62     val taskFragments: Collection<TaskFragment>
63         get() = this.children.reversed().filterIsInstance<TaskFragment>()
64 
65     val activities: Collection<Activity>
66         get() = this.children.reversed().filterIsInstance<Activity>()
67 
68     /** The top task in the stack. */
69     // NOTE: Unlike the WindowManager internals, we dump the state from top to bottom,
70     //       so the indices are inverted
71     val topTask: Task?
72         get() = tasks.firstOrNull()
73 
74     val resumedActivities: Collection<String>
75         get() {
76             val result = mutableListOf<String>()
77             if (this._resumedActivity.isNotEmpty()) {
78                 result.add(this._resumedActivity)
79             }
80             val activitiesInChildren =
81                 this.tasks.flatMap { it.resumedActivities }.filter { it.isNotEmpty() }
82             result.addAll(activitiesInChildren)
83             return result
84         }
85 
86     /** @return The first [Task] matching [predicate], or null otherwise */
87     fun getTask(predicate: (Task) -> Boolean) =
88         tasks.firstOrNull { predicate(it) } ?: if (predicate(this)) this else null
89 
90     /** @return the first [Activity] matching [predicate], or null otherwise */
91     internal fun getActivity(predicate: (Activity) -> Boolean): Activity? {
92         var activity: Activity? = activities.firstOrNull { predicate(it) }
93         if (activity != null) {
94             return activity
95         }
96         for (task in tasks) {
97             activity = task.getActivity(predicate)
98             if (activity != null) {
99                 return activity
100             }
101         }
102         for (taskFragment in taskFragments) {
103             activity = taskFragment.getActivity(predicate)
104             if (activity != null) {
105                 return activity
106             }
107         }
108         return null
109     }
110 
111     /**
112      * @param componentMatcher Components to search
113      * @return the first [Activity] matching [componentMatcher], or null otherwise
114      */
115     fun getActivity(componentMatcher: IComponentMatcher): Activity? = getActivity { activity ->
116         componentMatcher.activityMatchesAnyOf(activity)
117     }
118 
119     /**
120      * @param componentMatcher Components to search
121      * @return if any activity matches [componentMatcher]
122      */
123     fun containsActivity(componentMatcher: IComponentMatcher) =
124         getActivity(componentMatcher) != null
125 
126     override fun toString(): String {
127         return "${this::class.simpleName}: {$token $title} id=$taskId bounds=$bounds"
128     }
129 
130     override fun equals(other: Any?): Boolean {
131         if (this === other) return true
132         if (other !is Task) return false
133 
134         if (activityType != other.activityType) return false
135         if (isFullscreen != other.isFullscreen) return false
136         if (bounds != other.bounds) return false
137         if (taskId != other.taskId) return false
138         if (rootTaskId != other.rootTaskId) return false
139         if (displayId != other.displayId) return false
140         if (realActivity != other.realActivity) return false
141         if (resizeMode != other.resizeMode) return false
142         if (minWidth != other.minWidth) return false
143         if (minHeight != other.minHeight) return false
144         if (name != other.name) return false
145         if (orientation != other.orientation) return false
146         if (title != other.title) return false
147         if (token != other.token) return false
148         if (windowContainer != other.windowContainer) return false
149 
150         return true
151     }
152 
153     override fun hashCode(): Int {
154         var result = super.hashCode()
155         result = 31 * result + activityType
156         result = 31 * result + isFullscreen.hashCode()
157         result = 31 * result + bounds.hashCode()
158         result = 31 * result + taskId
159         result = 31 * result + rootTaskId
160         result = 31 * result + displayId
161         result = 31 * result + lastNonFullscreenBounds.hashCode()
162         result = 31 * result + realActivity.hashCode()
163         result = 31 * result + origActivity.hashCode()
164         result = 31 * result + resizeMode
165         result = 31 * result + _resumedActivity.hashCode()
166         result = 31 * result + animatingBounds.hashCode()
167         result = 31 * result + surfaceWidth
168         result = 31 * result + surfaceHeight
169         result = 31 * result + createdByOrganizer.hashCode()
170         result = 31 * result + minWidth
171         result = 31 * result + minHeight
172         result = 31 * result + isVisible.hashCode()
173         result = 31 * result + name.hashCode()
174         result = 31 * result + windowContainer.hashCode()
175         return result
176     }
177 }
178