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