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 * @property displayRect The logical display rect. 33 * @property stableBounds The logical display bounds excluding the navigation and status bar areas. 34 */ 35 class DisplayContent( 36 val displayId: Int, 37 val focusedRootTaskId: Int, 38 val resumedActivity: String, 39 val singleTaskInstance: Boolean, 40 val defaultPinnedStackBounds: Rect, 41 val pinnedStackMovementBounds: Rect, 42 val displayRect: Rect, 43 val appRect: Rect, 44 val dpi: Int, 45 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 val insetsSourceProviders: Array<InsetsSourceProvider>, 55 private val windowContainer: WindowContainer, 56 ) : WindowContainer by windowContainer { 57 override val name: String = displayId.toString() 58 override val isVisible: Boolean = false 59 60 val isTablet: Boolean 61 get() { 62 val smallestWidth = 63 dpiFromPx(min(displayRect.width().toFloat(), displayRect.height().toFloat()), dpi) 64 return smallestWidth >= PlatformConsts.TABLET_MIN_DPS 65 } 66 67 val rootTasks: Collection<Task> 68 get() { 69 val tasks = collectDescendants<Task> { it.isRootTask }.toMutableList() 70 // TODO(b/149338177): figure out how CTS tests deal with organizer. For now, 71 // don't treat them as regular stacks 72 val rootOrganizedTasks = mutableListOf<Task>() 73 val reversedTaskList = tasks.reversed() 74 reversedTaskList.forEach { task -> 75 // Skip tasks created by an organizer 76 if (task.createdByOrganizer) { 77 tasks.remove(task) 78 rootOrganizedTasks.add(task) 79 } 80 } 81 // Add root tasks controlled by an organizer 82 rootOrganizedTasks.reversed().forEach { task -> 83 tasks.addAll(task.children.reversed().map { it as Task }) 84 } 85 86 return tasks 87 } 88 89 /** 90 * @param componentMatcher Components to search 91 * @return if [componentMatcher] matches any activity 92 */ 93 fun containsActivity(componentMatcher: IComponentMatcher): Boolean = 94 rootTasks.any { it.containsActivity(componentMatcher) } 95 96 /** 97 * @param componentMatcher Components to search 98 * @return THe [DisplayArea] matching [componentMatcher], or null if none matches 99 */ 100 fun getTaskDisplayArea(componentMatcher: IComponentMatcher): DisplayArea? { 101 val taskDisplayAreas = 102 this.collectDescendants<DisplayArea> { it.isTaskDisplayArea } 103 .filter { it.containsActivity(componentMatcher) } 104 105 if (taskDisplayAreas.size > 1) { 106 throw IllegalArgumentException( 107 "There must be exactly one activity among all TaskDisplayAreas." 108 ) 109 } 110 111 return taskDisplayAreas.firstOrNull() 112 } 113 114 override fun toString(): String { 115 return "${this::class.simpleName} #$displayId: name=$title mDisplayRect=$displayRect " + 116 "mAppRect=$appRect mFlags=$flags" 117 } 118 119 override fun equals(other: Any?): Boolean { 120 if (this === other) return true 121 if (other !is DisplayContent) return false 122 if (!super.equals(other)) return false 123 124 if (displayId != other.displayId) return false 125 if (focusedRootTaskId != other.focusedRootTaskId) return false 126 if (resumedActivity != other.resumedActivity) return false 127 if (defaultPinnedStackBounds != other.defaultPinnedStackBounds) return false 128 if (pinnedStackMovementBounds != other.pinnedStackMovementBounds) return false 129 if (stableBounds != other.stableBounds) return false 130 if (displayRect != other.displayRect) return false 131 if (appRect != other.appRect) return false 132 if (dpi != other.dpi) return false 133 if (flags != other.flags) return false 134 if (focusedApp != other.focusedApp) return false 135 if (lastTransition != other.lastTransition) return false 136 if (appTransitionState != other.appTransitionState) return false 137 if (rotation != other.rotation) return false 138 if (lastOrientation != other.lastOrientation) return false 139 if (cutout != other.cutout) return false 140 if (name != other.name) return false 141 if (singleTaskInstance != other.singleTaskInstance) return false 142 if (surfaceSize != other.surfaceSize) 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 + displayId 151 result = 31 * result + focusedRootTaskId 152 result = 31 * result + resumedActivity.hashCode() 153 result = 31 * result + singleTaskInstance.hashCode() 154 result = 31 * result + defaultPinnedStackBounds.hashCode() 155 result = 31 * result + pinnedStackMovementBounds.hashCode() 156 result = 31 * result + displayRect.hashCode() 157 result = 31 * result + appRect.hashCode() 158 result = 31 * result + dpi 159 result = 31 * result + flags 160 result = 31 * result + stableBounds.hashCode() 161 result = 31 * result + surfaceSize 162 result = 31 * result + focusedApp.hashCode() 163 result = 31 * result + lastTransition.hashCode() 164 result = 31 * result + appTransitionState.hashCode() 165 result = 31 * result + rotation.value 166 result = 31 * result + lastOrientation 167 result = 31 * result + cutout.hashCode() 168 result = 31 * result + name.hashCode() 169 result = 31 * result + isVisible.hashCode() 170 result = 31 * result + windowContainer.hashCode() 171 return result 172 } 173 174 companion object { 175 /** From [android.util.DisplayMetrics] */ 176 const val DENSITY_DEFAULT = 160f 177 178 /** From [com.android.systemui.shared.recents.utilities.Utilities] */ 179 const val TABLET_MIN_DPS = 600f 180 181 @JvmStatic 182 fun dpiFromPx(size: Float, densityDpi: Int): Float { 183 val densityRatio: Float = densityDpi.toFloat() / DENSITY_DEFAULT 184 return size / densityRatio 185 } 186 } 187 } 188