xref: /aosp_15_r20/development/tools/winscope/src/viewers/common/abstract_presenter_input_method.ts (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
1/*
2 * Copyright (C) 2022 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} from 'common/assert_utils';
18import {PersistentStoreProxy} from 'common/persistent_store_proxy';
19import {Store} from 'common/store';
20import {Timestamp} from 'common/time';
21import {Trace, TraceEntry} from 'trace/trace';
22import {Traces} from 'trace/traces';
23import {ImeTraceType, TraceType} from 'trace/trace_type';
24import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node';
25import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
26import {TreeNode} from 'trace/tree_node/tree_node';
27import {ImeAdditionalProperties} from 'viewers/common/ime_additional_properties';
28import {ImeUiData} from 'viewers/common/ime_ui_data';
29import {
30  ImeLayers,
31  ImeUtils,
32  ProcessedWindowManagerState,
33} from 'viewers/common/ime_utils';
34import {TableProperties} from 'viewers/common/table_properties';
35import {TextFilter} from 'viewers/common/text_filter';
36import {UserOptions} from 'viewers/common/user_options';
37import {
38  AbstractHierarchyViewerPresenter,
39  NotifyHierarchyViewCallbackType,
40} from './abstract_hierarchy_viewer_presenter';
41import {VISIBLE_CHIP} from './chip';
42import {HierarchyPresenter} from './hierarchy_presenter';
43import {UpdateSfSubtreeDisplayNames} from './operations/update_sf_subtree_display_names';
44import {PropertiesPresenter} from './properties_presenter';
45import {UiHierarchyTreeNode} from './ui_hierarchy_tree_node';
46import {UiTreeUtils} from './ui_tree_utils';
47
48export abstract class AbstractPresenterInputMethod extends AbstractHierarchyViewerPresenter<ImeUiData> {
49  protected getHierarchyTreeNameStrategy = (
50    entry: TraceEntry<HierarchyTreeNode>,
51    tree: HierarchyTreeNode,
52  ) => {
53    const where = tree.getEagerPropertyByName('where')?.formattedValue();
54    return this.getEntryFormattedTimestamp(entry) + ' - ' + where;
55  };
56  protected override hierarchyPresenter = new HierarchyPresenter(
57    PersistentStoreProxy.new<UserOptions>(
58      'ImeHierarchyOptions',
59      {
60        simplifyNames: {
61          name: 'Simplify names',
62          enabled: true,
63        },
64        showOnlyVisible: {
65          name: 'Show only',
66          chip: VISIBLE_CHIP,
67          enabled: false,
68        },
69        flat: {
70          name: 'Flat',
71          enabled: false,
72        },
73      },
74      this.storage,
75    ),
76    new TextFilter(),
77    [],
78    true,
79    false,
80    this.getHierarchyTreeNameStrategy,
81    [[TraceType.SURFACE_FLINGER, [new UpdateSfSubtreeDisplayNames()]]],
82  );
83  protected override propertiesPresenter = new PropertiesPresenter(
84    PersistentStoreProxy.new<UserOptions>(
85      'ImePropertiesOptions',
86      {
87        showDefaults: {
88          name: 'Show defaults',
89          enabled: false,
90          tooltip: `If checked, shows the value of all properties.
91Otherwise, hides all properties whose value is
92the default for its data type.`,
93        },
94      },
95      this.storage,
96    ),
97    new TextFilter(),
98    [],
99  );
100  protected override multiTraceType = undefined;
101
102  protected readonly imeTrace: Trace<HierarchyTreeNode>;
103  private readonly wmTrace?: Trace<HierarchyTreeNode>;
104  private readonly sfTrace?: Trace<HierarchyTreeNode>;
105
106  private hierarchyTableProperties: TableProperties | undefined;
107  private additionalProperties: ImeAdditionalProperties | undefined;
108
109  constructor(
110    trace: Trace<HierarchyTreeNode>,
111    traces: Traces,
112    storage: Store,
113    notifyViewCallback: NotifyHierarchyViewCallbackType<ImeUiData>,
114  ) {
115    super(
116      trace,
117      traces,
118      storage,
119      notifyViewCallback,
120      new ImeUiData(trace.type as ImeTraceType),
121    );
122    this.imeTrace = trace;
123    this.sfTrace = traces.getTrace(TraceType.SURFACE_FLINGER);
124    this.wmTrace = traces.getTrace(TraceType.WINDOW_MANAGER);
125  }
126
127  async onHighlightedNodeChange(node: UiHierarchyTreeNode) {
128    this.clearOverridePropertiesTreeSelection();
129    await this.applyHighlightedNodeChange(node);
130    this.refreshUIData();
131  }
132
133  async onHighlightedIdChange(newId: string) {
134    const selectedHierarchyTree = this.hierarchyPresenter.getSelectedTree();
135    if (!selectedHierarchyTree || selectedHierarchyTree[1].id !== newId) {
136      this.clearOverridePropertiesTreeSelection();
137    }
138    await this.applyHighlightedIdChange(newId);
139    this.refreshUIData();
140  }
141
142  async onAdditionalPropertySelected(selectedItem: {
143    name: string;
144    treeNode: TreeNode;
145  }) {
146    this.updateHighlightedItem(selectedItem.treeNode.id);
147    if (selectedItem.treeNode instanceof HierarchyTreeNode) {
148      this.clearOverridePropertiesTreeSelection();
149      this.hierarchyPresenter.setSelectedTree([
150        assertDefined(this.wmTrace),
151        selectedItem.treeNode,
152      ]);
153    } else if (selectedItem.treeNode instanceof PropertyTreeNode) {
154      this.hierarchyPresenter.setSelectedTree(undefined);
155      this.overridePropertiesTree = selectedItem.treeNode;
156    }
157
158    this.overridePropertiesTreeName = selectedItem.name;
159    await this.updatePropertiesTree();
160    this.refreshUIData();
161  }
162
163  protected async getAdditionalProperties(
164    wmEntry: HierarchyTreeNode | undefined,
165    sfEntry: HierarchyTreeNode | undefined,
166    wmEntryTimestamp: Timestamp | undefined,
167    sfEntryTimestamp: Timestamp | undefined,
168  ): Promise<ImeAdditionalProperties> {
169    let wmProperties: ProcessedWindowManagerState | undefined;
170    let sfProperties: ImeLayers | undefined;
171
172    if (wmEntry) {
173      wmProperties = ImeUtils.processWindowManagerTraceEntry(
174        wmEntry,
175        wmEntryTimestamp,
176      );
177
178      if (sfEntry) {
179        sfProperties = ImeUtils.getImeLayers(
180          sfEntry,
181          wmProperties,
182          sfEntryTimestamp,
183        );
184
185        if (sfProperties) {
186          await this.makeSfSubtrees(sfProperties);
187        }
188      }
189    }
190
191    return new ImeAdditionalProperties(wmProperties, sfProperties);
192  }
193
194  protected override keepCalculated(tree: HierarchyTreeNode): boolean {
195    return false;
196  }
197
198  protected override getOverrideDisplayName(
199    selected: [Trace<HierarchyTreeNode>, HierarchyTreeNode],
200  ): string | undefined {
201    return this.overridePropertiesTreeName;
202  }
203
204  protected override async initializeIfNeeded(): Promise<void> {
205    this.clearOverridePropertiesTreeSelection();
206  }
207
208  protected override async processDataAfterPositionUpdate(): Promise<void> {
209    const imeEntry = this.hierarchyPresenter.getCurrentEntryForTrace(
210      this.imeTrace,
211    );
212    const [sfEntry, wmEntry] = this.findSfWmTraceEntries(imeEntry);
213
214    if (imeEntry) {
215      this.additionalProperties = await this.getAdditionalProperties(
216        await wmEntry?.getValue(),
217        await sfEntry?.getValue(),
218        wmEntry?.getTimestamp(),
219        sfEntry?.getTimestamp(),
220      );
221      this.hierarchyTableProperties = this.getHierarchyTableProperties();
222
223      await this.updateOverridePropertiesTree(this.additionalProperties);
224
225      const highlightedItem = this.getHighlightedItem();
226      const selected = this.hierarchyPresenter.getSelectedTree();
227
228      if (!selected && highlightedItem !== undefined) {
229        const isHighlightedFilter = (node: HierarchyTreeNode) =>
230          UiTreeUtils.isHighlighted(node, highlightedItem);
231        let selectedTree =
232          this.additionalProperties?.sf?.taskLayerOfImeContainer?.findDfs(
233            isHighlightedFilter,
234          );
235        if (!selectedTree) {
236          selectedTree =
237            this.additionalProperties?.sf?.taskLayerOfImeSnapshot?.findDfs(
238              isHighlightedFilter,
239            );
240        }
241
242        if (selectedTree) {
243          this.hierarchyPresenter.setSelectedTree([
244            assertDefined(this.sfTrace),
245            selectedTree,
246          ]);
247          await this.updatePropertiesTree();
248        }
249      }
250    }
251  }
252
253  private async makeSfSubtrees(
254    sfProperties: ImeLayers,
255  ): Promise<UiHierarchyTreeNode[]> {
256    const sfHierarchyTrees = [];
257    const sfTrace = assertDefined(this.sfTrace);
258    if (sfProperties.taskLayerOfImeContainer) {
259      sfHierarchyTrees.push(sfProperties.taskLayerOfImeContainer);
260    }
261    if (sfProperties.taskLayerOfImeSnapshot) {
262      sfHierarchyTrees.push(sfProperties.taskLayerOfImeSnapshot);
263    }
264    if (sfHierarchyTrees.length > 0) {
265      await this.hierarchyPresenter.addCurrentHierarchyTrees(
266        [sfTrace, sfHierarchyTrees],
267        this.getHighlightedItem(),
268      );
269    }
270    const sfSubtrees = assertDefined(
271      this.hierarchyPresenter.getFormattedTreesByTrace(sfTrace),
272    );
273    sfSubtrees.forEach((subtree) =>
274      subtree.setDisplayName('SfSubtree - ' + subtree.name),
275    );
276    return sfSubtrees;
277  }
278
279  private clearOverridePropertiesTreeSelection() {
280    this.overridePropertiesTree = undefined;
281    this.overridePropertiesTreeName = undefined;
282  }
283
284  private findSfWmTraceEntries(
285    imeEntry: TraceEntry<HierarchyTreeNode> | undefined,
286  ): [
287    TraceEntry<HierarchyTreeNode> | undefined,
288    TraceEntry<HierarchyTreeNode> | undefined,
289  ] {
290    if (!imeEntry || !this.imeTrace.hasFrameInfo()) {
291      return [undefined, undefined];
292    }
293
294    const frames = imeEntry.getFramesRange();
295    if (!frames || frames.start === frames.end) {
296      return [undefined, undefined];
297    }
298
299    const frame = frames.start;
300    const sfEntry = this.sfTrace
301      ?.getFrame(frame)
302      ?.findClosestEntry(imeEntry.getTimestamp());
303    const wmEntry = this.wmTrace
304      ?.getFrame(frame)
305      ?.findClosestEntry(imeEntry.getTimestamp());
306
307    return [sfEntry, wmEntry];
308  }
309
310  private async updateOverridePropertiesTree(
311    additionalProperties: ImeAdditionalProperties,
312  ) {
313    const highlightedItem = this.getHighlightedItem();
314    if (!highlightedItem) {
315      this.clearOverridePropertiesTreeSelection();
316      return;
317    }
318    if (highlightedItem.includes('WindowManagerState')) {
319      this.overridePropertiesTree = undefined;
320      const wmHierarchyTree = additionalProperties.wm?.hierarchyTree;
321      this.hierarchyPresenter.setSelectedTree(
322        wmHierarchyTree
323          ? [assertDefined(this.wmTrace), wmHierarchyTree]
324          : undefined,
325      );
326      this.overridePropertiesTreeName = wmHierarchyTree
327        ? 'Window Manager State'
328        : undefined;
329    } else if (highlightedItem.includes('imeInsetsSourceProvider')) {
330      this.hierarchyPresenter.setSelectedTree(undefined);
331      const imeInsetsSourceProvider =
332        additionalProperties.wm?.wmStateProperties?.imeInsetsSourceProvider;
333      this.overridePropertiesTree = imeInsetsSourceProvider;
334      this.overridePropertiesTreeName = imeInsetsSourceProvider
335        ? 'Ime Insets Source Provider'
336        : undefined;
337    } else if (highlightedItem.includes('inputMethodControlTarget')) {
338      this.hierarchyPresenter.setSelectedTree(undefined);
339      const imeControlTarget =
340        additionalProperties.wm?.wmStateProperties?.imeControlTarget;
341      this.overridePropertiesTree = imeControlTarget;
342      this.overridePropertiesTreeName = imeControlTarget
343        ? 'Ime Control Target'
344        : undefined;
345    } else if (highlightedItem.includes('inputMethodInputTarget')) {
346      this.hierarchyPresenter.setSelectedTree(undefined);
347      const imeInputTarget =
348        additionalProperties.wm?.wmStateProperties?.imeInputTarget;
349      this.overridePropertiesTree = imeInputTarget;
350      this.overridePropertiesTreeName = imeInputTarget
351        ? 'Ime Input Target'
352        : undefined;
353    } else if (highlightedItem.includes('inputMethodTarget')) {
354      this.hierarchyPresenter.setSelectedTree(undefined);
355      const imeLayeringTarget =
356        additionalProperties.wm?.wmStateProperties?.imeLayeringTarget;
357      this.overridePropertiesTree = imeLayeringTarget;
358      this.overridePropertiesTreeName = imeLayeringTarget
359        ? 'Ime Layering Target'
360        : undefined;
361    }
362
363    await this.updatePropertiesTree();
364  }
365
366  protected override refreshUIData() {
367    this.uiData.hierarchyTableProperties = this.hierarchyTableProperties;
368    this.uiData.additionalProperties = this.additionalProperties;
369    this.refreshHierarchyViewerUiData();
370  }
371
372  protected abstract getHierarchyTableProperties(): TableProperties;
373}
374