1/* 2 * 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 17import {assertDefined, assertTrue} from 'common/assert_utils'; 18import {PersistentStoreProxy} from 'common/persistent_store_proxy'; 19import {Store} from 'common/store'; 20import { 21 TabbedViewSwitchRequest, 22 TracePositionUpdate, 23} from 'messaging/winscope_event'; 24import {CustomQueryType} from 'trace/custom_query'; 25import {Trace} from 'trace/trace'; 26import {Traces} from 'trace/traces'; 27import {TraceEntryFinder} from 'trace/trace_entry_finder'; 28import {TraceType} from 'trace/trace_type'; 29import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node'; 30import {PropertyTreeNode} from 'trace/tree_node/property_tree_node'; 31import { 32 AbstractHierarchyViewerPresenter, 33 NotifyHierarchyViewCallbackType, 34} from 'viewers/common/abstract_hierarchy_viewer_presenter'; 35import {VISIBLE_CHIP} from 'viewers/common/chip'; 36import {VcCuratedProperties} from 'viewers/common/curated_properties'; 37import {DisplayIdentifier} from 'viewers/common/display_identifier'; 38import {HierarchyPresenter} from 'viewers/common/hierarchy_presenter'; 39import {PropertiesPresenter} from 'viewers/common/properties_presenter'; 40import {RectsPresenter} from 'viewers/common/rects_presenter'; 41import {TextFilter} from 'viewers/common/text_filter'; 42import {UiHierarchyTreeNode} from 'viewers/common/ui_hierarchy_tree_node'; 43import {UI_RECT_FACTORY} from 'viewers/common/ui_rect_factory'; 44import {UserOptions} from 'viewers/common/user_options'; 45import {UiRect} from 'viewers/components/rects/ui_rect'; 46import {UiData} from './ui_data'; 47 48export class Presenter extends AbstractHierarchyViewerPresenter<UiData> { 49 static readonly DENYLIST_PROPERTY_NAMES = ['children', 'isComputedVisible']; 50 51 private windowNames: string[] = []; 52 protected override hierarchyPresenter = new HierarchyPresenter( 53 PersistentStoreProxy.new<UserOptions>( 54 'VcHierarchyOptions', 55 { 56 showDiff: { 57 name: 'Show diff', 58 enabled: false, 59 isUnavailable: false, 60 }, 61 showOnlyVisible: { 62 name: 'Show only', 63 chip: VISIBLE_CHIP, 64 enabled: false, 65 }, 66 simplifyNames: { 67 name: 'Simplify names', 68 enabled: true, 69 }, 70 }, 71 this.storage, 72 ), 73 new TextFilter(), 74 Presenter.DENYLIST_PROPERTY_NAMES, 75 false, 76 true, 77 ); 78 protected override rectsPresenter = new RectsPresenter( 79 PersistentStoreProxy.new<UserOptions>( 80 'VcRectsOptions', 81 { 82 ignoreRectShowState: { 83 name: 'Ignore', 84 icon: 'visibility', 85 enabled: false, 86 }, 87 showOnlyVisible: { 88 name: 'Show only', 89 chip: VISIBLE_CHIP, 90 enabled: false, 91 }, 92 }, 93 this.storage, 94 ), 95 (tree: HierarchyTreeNode, trace: Trace<HierarchyTreeNode>) => 96 UI_RECT_FACTORY.makeVcUiRects( 97 tree, 98 this.getIdFromViewCaptureTrace(trace), 99 ), 100 undefined, 101 ); 102 protected override propertiesPresenter = new PropertiesPresenter( 103 PersistentStoreProxy.new<UserOptions>( 104 'VcPropertyOptions', 105 { 106 showDiff: { 107 name: 'Show diff', 108 enabled: false, 109 isUnavailable: false, 110 }, 111 showDefaults: { 112 name: 'Show defaults', 113 enabled: false, 114 tooltip: `If checked, shows the value of all properties. 115Otherwise, hides all properties whose value is 116the default for its data type.`, 117 }, 118 }, 119 this.storage, 120 ), 121 new TextFilter(), 122 Presenter.DENYLIST_PROPERTY_NAMES, 123 ); 124 protected override readonly multiTraceType = TraceType.VIEW_CAPTURE; 125 126 private readonly surfaceFlingerTrace: Trace<HierarchyTreeNode> | undefined; 127 private readonly viewCaptureTraces: Array<Trace<HierarchyTreeNode>>; 128 129 private viewCapturePackageNames: string[] = []; 130 private sfRects: UiRect[] | undefined; 131 private curatedProperties: VcCuratedProperties | undefined; 132 133 constructor( 134 traces: Traces, 135 storage: Readonly<Store>, 136 notifyViewCallback: NotifyHierarchyViewCallbackType<UiData>, 137 ) { 138 super(undefined, traces, storage, notifyViewCallback, new UiData()); 139 this.viewCaptureTraces = traces.getTraces(TraceType.VIEW_CAPTURE); 140 this.surfaceFlingerTrace = traces.getTrace(TraceType.SURFACE_FLINGER); 141 } 142 143 async onMiniRectsDoubleClick() { 144 if (!this.surfaceFlingerTrace) { 145 return; 146 } 147 await this.emitWinscopeEvent( 148 new TabbedViewSwitchRequest(this.surfaceFlingerTrace), 149 ); 150 } 151 152 getTraces(): Array<Trace<HierarchyTreeNode>> { 153 return this.viewCaptureTraces; 154 } 155 156 override async onHighlightedNodeChange(node: UiHierarchyTreeNode) { 157 await this.applyHighlightedNodeChange(node); 158 this.updateCuratedProperties(); 159 this.refreshUIData(); 160 } 161 162 override async onHighlightedIdChange(newId: string) { 163 await this.applyHighlightedIdChange(newId); 164 this.updateCuratedProperties(); 165 this.refreshUIData(); 166 } 167 168 protected override getOverrideDisplayName(): undefined { 169 return undefined; 170 } 171 172 protected override keepCalculated(node: UiHierarchyTreeNode): boolean { 173 return node.isRoot(); 174 } 175 176 protected override async initializeIfNeeded() { 177 await this.initializePackageNamesIfNeeded(); 178 await this.initializeWindowsIfNeeded(); 179 } 180 181 protected override async processDataAfterPositionUpdate( 182 event: TracePositionUpdate, 183 ): Promise<void> { 184 if (this.uiData && this.surfaceFlingerTrace) { 185 const surfaceFlingerEntry = 186 (await TraceEntryFinder.findCorrespondingEntry( 187 this.surfaceFlingerTrace, 188 event.position, 189 )?.getValue()) as HierarchyTreeNode; 190 if (surfaceFlingerEntry) { 191 this.sfRects = UI_RECT_FACTORY.makeUiRects( 192 surfaceFlingerEntry, 193 this.viewCapturePackageNames, 194 ); 195 } 196 } 197 this.updateCuratedProperties(); 198 } 199 200 protected override refreshUIData() { 201 this.uiData.sfRects = this.sfRects; 202 this.uiData.curatedProperties = this.curatedProperties; 203 this.refreshHierarchyViewerUiData(); 204 } 205 206 private async initializePackageNamesIfNeeded() { 207 if (this.viewCapturePackageNames.length > 0) { 208 return; 209 } 210 211 const promisesPackageName = this.viewCaptureTraces.map(async (trace) => { 212 const packageAndWindow = await trace.customQuery( 213 CustomQueryType.VIEW_CAPTURE_METADATA, 214 ); 215 return packageAndWindow.packageName; 216 }); 217 218 this.viewCapturePackageNames = await Promise.all(promisesPackageName); 219 } 220 221 private async initializeWindowsIfNeeded() { 222 if (this.rectsPresenter.getDisplays().length > 0) { 223 return; 224 } 225 226 const shortenAndCapitalizeWindowName = (name: string) => { 227 const lastDot = name.lastIndexOf('.'); 228 if (lastDot !== -1) { 229 name = name.substring(lastDot + 1); 230 } 231 if (name.length > 0) { 232 name = name[0].toUpperCase() + name.slice(1); 233 } 234 return name; 235 }; 236 237 const promisesWindowName = this.viewCaptureTraces.map(async (trace) => { 238 const packageAndWindow = await trace.customQuery( 239 CustomQueryType.VIEW_CAPTURE_METADATA, 240 ); 241 return shortenAndCapitalizeWindowName(packageAndWindow.windowName); 242 }); 243 this.windowNames = await Promise.all(promisesWindowName); 244 this.rectsPresenter.setDisplays(this.getWindows(this.windowNames)); 245 } 246 247 private getWindows(windowNames: string[]): DisplayIdentifier[] { 248 return this.viewCaptureTraces.map((trace, i) => { 249 const traceId = this.getIdFromViewCaptureTrace(trace); 250 return { 251 displayId: traceId, 252 groupId: traceId, 253 name: windowNames[i], 254 isActive: true, 255 }; 256 }); 257 } 258 259 private updateCuratedProperties() { 260 const propertiesTree = this.propertiesPresenter.getPropertiesTree(); 261 if (propertiesTree) { 262 this.curatedProperties = this.getCuratedProperties(propertiesTree); 263 } else { 264 this.curatedProperties = undefined; 265 } 266 } 267 268 private getCuratedProperties(tree: PropertyTreeNode): VcCuratedProperties { 269 const curated: VcCuratedProperties = { 270 className: tree.name, 271 hashcode: assertDefined(tree.getChildByName('hashcode')).formattedValue(), 272 left: assertDefined(tree.getChildByName('left')).formattedValue(), 273 top: assertDefined(tree.getChildByName('top')).formattedValue(), 274 elevation: assertDefined( 275 tree.getChildByName('elevation'), 276 ).formattedValue(), 277 height: assertDefined(tree.getChildByName('height')).formattedValue(), 278 width: assertDefined(tree.getChildByName('width')).formattedValue(), 279 translationX: assertDefined( 280 tree.getChildByName('translationX'), 281 ).formattedValue(), 282 translationY: assertDefined( 283 tree.getChildByName('translationY'), 284 ).formattedValue(), 285 scrollX: assertDefined(tree.getChildByName('scrollX')).formattedValue(), 286 scrollY: assertDefined(tree.getChildByName('scrollY')).formattedValue(), 287 scaleX: assertDefined(tree.getChildByName('scaleX')).formattedValue(), 288 scaleY: assertDefined(tree.getChildByName('scaleY')).formattedValue(), 289 visibility: assertDefined( 290 tree.getChildByName('visibility'), 291 ).formattedValue(), 292 alpha: assertDefined(tree.getChildByName('alpha')).formattedValue(), 293 willNotDraw: assertDefined( 294 tree.getChildByName('willNotDraw'), 295 ).formattedValue(), 296 clipChildren: assertDefined( 297 tree.getChildByName('clipChildren'), 298 ).formattedValue(), 299 }; 300 return curated; 301 } 302 303 private getIdFromViewCaptureTrace(trace: Trace<HierarchyTreeNode>): number { 304 const index = this.viewCaptureTraces.indexOf(trace); 305 assertTrue(index !== -1); 306 return index; 307 } 308} 309