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