• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
16import {FilterType, TreeUtils} from 'common/tree_utils';
17import {WindowContainer} from 'trace/flickerlib/common';
18import {Layer} from 'trace/flickerlib/layers/Layer';
19import {LayerTraceEntry} from 'trace/flickerlib/layers/LayerTraceEntry';
20import {Activity} from 'trace/flickerlib/windows/Activity';
21import {WindowManagerState} from 'trace/flickerlib/windows/WindowManagerState';
22import {WindowState} from 'trace/flickerlib/windows/WindowState';
23
24class ProcessedWindowManagerState {
25  constructor(
26    public name: string,
27    public stableId: string,
28    public focusedApp: string,
29    public focusedWindow: WindowState,
30    public focusedActivity: Activity,
31    public isInputMethodWindowVisible: boolean,
32    public protoImeControlTarget: any,
33    public protoImeInputTarget: any,
34    public protoImeLayeringTarget: any,
35    public protoImeInsetsSourceProvider: any,
36    public proto: any
37  ) {}
38}
39
40class ImeLayers {
41  constructor(
42    public name: string,
43    public imeContainer: Layer,
44    public inputMethodSurface: Layer,
45    public focusedWindow: Layer | undefined,
46    public taskOfImeContainer: Layer | undefined,
47    public taskOfImeSnapshot: Layer | undefined
48  ) {}
49}
50
51class ImeUtils {
52  static processWindowManagerTraceEntry(entry: WindowManagerState): ProcessedWindowManagerState {
53    const displayContent = entry.root.children[0];
54
55    return new ProcessedWindowManagerState(
56      entry.name,
57      entry.stableId,
58      entry.focusedApp,
59      entry.focusedWindow,
60      entry.focusedActivity,
61      ImeUtils.isInputMethodVisible(displayContent),
62      ImeUtils.getImeControlTargetProperty(displayContent.proto),
63      ImeUtils.getImeInputTargetProperty(displayContent.proto),
64      ImeUtils.getImeLayeringTargetProperty(displayContent.proto),
65      displayContent.proto.imeInsetsSourceProvider,
66      entry.proto
67    );
68  }
69
70  static getImeLayers(
71    entry: LayerTraceEntry,
72    processedWindowManagerState: ProcessedWindowManagerState
73  ): ImeLayers | undefined {
74    const isImeContainer = TreeUtils.makeNodeFilter('ImeContainer');
75    const imeContainer = TreeUtils.findDescendantNode(entry, isImeContainer);
76    if (!imeContainer) {
77      return undefined;
78    }
79
80    const isInputMethodSurface = TreeUtils.makeNodeFilter('InputMethod');
81    const inputMethodSurface = TreeUtils.findDescendantNode(imeContainer, isInputMethodSurface);
82
83    let focusedWindowLayer: Layer = undefined;
84    const focusedWindowToken = processedWindowManagerState.focusedWindow?.token;
85    if (focusedWindowToken) {
86      const isFocusedWindow = TreeUtils.makeNodeFilter(focusedWindowToken);
87      focusedWindowLayer = TreeUtils.findDescendantNode(entry, isFocusedWindow);
88    }
89
90    // we want to see both ImeContainer and IME-snapshot if there are
91    // cases where both exist
92    const taskLayerOfImeContainer = ImeUtils.findAncestorTaskLayerOfImeLayer(
93      entry,
94      TreeUtils.makeNodeFilter('ImeContainer')
95    );
96
97    const taskLayerOfImeSnapshot = ImeUtils.findAncestorTaskLayerOfImeLayer(
98      entry,
99      TreeUtils.makeNodeFilter('IME-snapshot')
100    );
101
102    return new ImeLayers(
103      entry.name,
104      imeContainer,
105      inputMethodSurface,
106      focusedWindowLayer,
107      taskLayerOfImeContainer,
108      taskLayerOfImeSnapshot
109    );
110  }
111
112  static transformInputConnectionCall(entry: any) {
113    const obj = Object.assign({}, entry);
114    if (obj.inputConnectionCall) {
115      Object.getOwnPropertyNames(obj.inputConnectionCall).forEach((name) => {
116        const value = Object.getOwnPropertyDescriptor(obj.inputConnectionCall, name);
117        if (!value?.value) delete obj.inputConnectionCall[name];
118      });
119    }
120    return obj;
121  }
122
123  private static findAncestorTaskLayerOfImeLayer(
124    entry: LayerTraceEntry,
125    isTargetImeLayer: FilterType
126  ): Layer {
127    const imeLayer = TreeUtils.findDescendantNode(entry, isTargetImeLayer);
128    if (!imeLayer) {
129      return undefined;
130    }
131
132    const isTaskLayer = TreeUtils.makeNodeFilter('Task|ImePlaceholder');
133    const taskLayer = TreeUtils.findAncestorNode(imeLayer, isTaskLayer) as Layer;
134    if (!taskLayer) {
135      return undefined;
136    }
137
138    taskLayer.kind = 'SF subtree - ' + taskLayer.id;
139    return taskLayer;
140  }
141
142  private static getImeControlTargetProperty(displayContentProto: any): any {
143    const POSSIBLE_NAMES = ['inputMethodControlTarget', 'imeControlTarget'];
144    return ImeUtils.findAnyPropertyWithMatchingName(displayContentProto, POSSIBLE_NAMES);
145  }
146
147  private static getImeInputTargetProperty(displayContentProto: any): any {
148    const POSSIBLE_NAMES = ['inputMethodInputTarget', 'imeInputTarget'];
149    return ImeUtils.findAnyPropertyWithMatchingName(displayContentProto, POSSIBLE_NAMES);
150  }
151
152  private static getImeLayeringTargetProperty(displayContentProto: any): any {
153    const POSSIBLE_NAMES = ['inputMethodTarget', 'imeLayeringTarget'];
154    return ImeUtils.findAnyPropertyWithMatchingName(displayContentProto, POSSIBLE_NAMES);
155  }
156
157  private static findAnyPropertyWithMatchingName(object: any, possible_names: string[]): any {
158    const key = Object.keys(object).find((key) => possible_names.includes(key));
159    return key ? object[key] : undefined;
160  }
161
162  private static isInputMethodVisible(displayContent: WindowContainer): boolean {
163    const isInputMethod = TreeUtils.makeNodeFilter('InputMethod');
164    const inputMethodWindowOrLayer = TreeUtils.findDescendantNode(
165      displayContent,
166      isInputMethod
167    ) as WindowContainer;
168    return inputMethodWindowOrLayer?.isVisible === true;
169  }
170}
171
172export {ImeUtils, ProcessedWindowManagerState, ImeLayers};
173