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     override val stableId: String
53         get() = "${this::class.simpleName} $token $title $taskId"
54 
55     val isRootTask: Boolean
56         get() = taskId == rootTaskId
57     val tasks: Collection<Task>
58         get() = this.children.reversed().filterIsInstance<Task>()
59     val taskFragments: Collection<TaskFragment>
60         get() = this.children.reversed().filterIsInstance<TaskFragment>()
61     val activities: Collection<Activity>
62         get() = this.children.reversed().filterIsInstance<Activity>()
63 
64     /** The top task in the stack. */
65     // NOTE: Unlike the WindowManager internals, we dump the state from top to bottom,
66     //       so the indices are inverted
67     val topTask: Task?
68         get() = tasks.firstOrNull()
69     val resumedActivities: Collection<String>
70         get() {
71             val result = mutableListOf<String>()
72             if (this._resumedActivity.isNotEmpty()) {
73                 result.add(this._resumedActivity)
74             }
75             val activitiesInChildren =
76                 this.tasks.flatMap { it.resumedActivities }.filter { it.isNotEmpty() }
77             result.addAll(activitiesInChildren)
78             return result
79         }
80 
81     /** @return The first [Task] matching [predicate], or null otherwise */
82     fun getTask(predicate: (Task) -> Boolean) =
83         tasks.firstOrNull { predicate(it) } ?: if (predicate(this)) this else null
84 
85     /** @return the first [Activity] matching [predicate], or null otherwise */
86     internal fun getActivity(predicate: (Activity) -> Boolean): Activity? {
87         var activity: Activity? = activities.firstOrNull { predicate(it) }
88         if (activity != null) {
89             return activity
90         }
91         for (task in tasks) {
92             activity = task.getActivity(predicate)
93             if (activity != null) {
94                 return activity
95             }
96         }
97         for (taskFragment in taskFragments) {
98             activity = taskFragment.getActivity(predicate)
99             if (activity != null) {
100                 return activity
101             }
102         }
103         return null
104     }
105 
106     /**
107      * @param componentMatcher Components to search
108      * @return the first [Activity] matching [componentMatcher], or null otherwise
109      */
110     fun getActivity(componentMatcher: IComponentMatcher): Activity? = getActivity { activity ->
111         componentMatcher.activityMatchesAnyOf(activity)
112     }
113 
114     /**
115      * @param componentMatcher Components to search
116      * @return if any activity matches [componentMatcher]
117      */
118     fun containsActivity(componentMatcher: IComponentMatcher) =
119         getActivity(componentMatcher) != null
120 
121     override fun toString(): String {
122         return "${this::class.simpleName}: {$token $title} id=$taskId bounds=$bounds"
123     }
124 
125     override fun equals(other: Any?): Boolean {
126         if (this === other) return true
127         if (other !is Task) return false
128 
129         if (activityType != other.activityType) return false
130         if (isFullscreen != other.isFullscreen) return false
131         if (bounds != other.bounds) return false
132         if (taskId != other.taskId) return false
133         if (rootTaskId != other.rootTaskId) return false
134         if (displayId != other.displayId) return false
135         if (realActivity != other.realActivity) return false
136         if (resizeMode != other.resizeMode) return false
137         if (minWidth != other.minWidth) return false
138         if (minHeight != other.minHeight) return false
139         if (name != other.name) return false
140         if (orientation != other.orientation) return false
141         if (title != other.title) return false
142         if (token != other.token) 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 + activityType
151         result = 31 * result + isFullscreen.hashCode()
152         result = 31 * result + bounds.hashCode()
153         result = 31 * result + taskId
154         result = 31 * result + rootTaskId
155         result = 31 * result + displayId
156         result = 31 * result + lastNonFullscreenBounds.hashCode()
157         result = 31 * result + realActivity.hashCode()
158         result = 31 * result + origActivity.hashCode()
159         result = 31 * result + resizeMode
160         result = 31 * result + _resumedActivity.hashCode()
161         result = 31 * result + animatingBounds.hashCode()
162         result = 31 * result + surfaceWidth
163         result = 31 * result + surfaceHeight
164         result = 31 * result + createdByOrganizer.hashCode()
165         result = 31 * result + minWidth
166         result = 31 * result + minHeight
167         result = 31 * result + isVisible.hashCode()
168         result = 31 * result + name.hashCode()
169         result = 31 * result + windowContainer.hashCode()
170         return result
171     }
172 }
173