IME Viewers - Table of IME properties and SF/WM.

Add extra properties to IME viewers from the IME traces and SF/WM
traces.

Bug: b/238088679
Test: npm run test:all

Change-Id: I177fc7353f963cc8b1391a88d5d8f4b2e4f387fa
This commit is contained in:
Priyanka Patel
2022-09-20 11:05:26 +00:00
parent 32c0ee72db
commit 0c8cdf9609
35 changed files with 1444 additions and 553 deletions

View File

@@ -42,6 +42,9 @@ import { PropertyGroupsComponent } from "viewers/components/property_groups.comp
import { TransformMatrixComponent } from "viewers/components/transform_matrix.component";
import { ParserErrorSnackBarComponent } from "./components/parser_error_snack_bar_component";
import { ViewerInputMethodComponent } from "viewers/components/viewer_input_method.component";
import { PropertiesTableComponent } from "viewers/components/properties_table.component";
import { ImeAdditionalPropertiesComponent } from "viewers/components/ime_additional_properties.component";
import { CoordinatesTableComponent } from "viewers/components/coordinates_table.component";
@NgModule({
declarations: [
@@ -64,7 +67,10 @@ import { ViewerInputMethodComponent } from "viewers/components/viewer_input_meth
TreeNodePropertiesDataViewComponent,
PropertyGroupsComponent,
TransformMatrixComponent,
ParserErrorSnackBarComponent
ParserErrorSnackBarComponent,
PropertiesTableComponent,
ImeAdditionalPropertiesComponent,
CoordinatesTableComponent,
],
imports: [
BrowserModule,
@@ -89,7 +95,7 @@ import { ViewerInputMethodComponent } from "viewers/components/viewer_input_meth
MatTooltipModule,
MatToolbarModule,
MatTabsModule,
MatSnackBarModule
MatSnackBarModule,
],
bootstrap: [AppComponent]
})

View File

@@ -0,0 +1,23 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ImeLayers, ProcessedWindowManagerState } from "./ime_utils";
export class ImeAdditionalProperties {
constructor(
public wm: ProcessedWindowManagerState | undefined,
public sf: ImeLayers | undefined,
) {}
}

View File

@@ -1,154 +0,0 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ImePresenter } from "./ime_presenter";
import { ImeUiData } from "./ime_ui_data";
import { UserOptions } from "viewers/common/user_options";
import { TraceType } from "common/trace/trace_type";
import { HierarchyTreeNode, PropertiesTreeNode } from "viewers/common/ui_tree_utils";
import { UnitTestUtils } from "test/unit/utils";
import { HierarchyTreeBuilder } from "test/unit/hierarchy_tree_builder";
describe("ImePresenterInputMethodClients", () => {
let presenter: ImePresenter;
let uiData: ImeUiData;
let entries: Map<TraceType, any>;
let selectedTree: HierarchyTreeNode;
beforeAll(async () => {
entries = new Map<TraceType, any>();
const entry: any = await UnitTestUtils.getInputMethodClientsEntry();
entries.set(TraceType.INPUT_METHOD_CLIENTS, [entry, null]);
selectedTree = new HierarchyTreeBuilder().setName("8m23s29ms - InputMethodManager#showSoftInput")
.setFilteredView(true).setKind("InputMethodClient entry").setId("entry").setStableId("entry")
.setChildren([
new HierarchyTreeBuilder().setKind("Client").setId("client").setFilteredView(true)
.setStableId("client").build()
]).build();
});
beforeEach(async () => {
presenter = new ImePresenter((newData: ImeUiData) => {
uiData = newData;
}, [TraceType.INPUT_METHOD_CLIENTS]);
});
it("can notify current trace entries", () => {
presenter.notifyCurrentTraceEntries(entries);
expect(uiData.highlightedItems?.length).toEqual(0);
expect(uiData.hierarchyUserOptions).toBeTruthy();
expect(uiData.propertiesUserOptions).toBeTruthy();
// does not check specific tree values as tree generation method may change
expect(Object.keys(uiData.tree!).length > 0).toBeTrue();
});
it("can handle unavailable trace entry", () => {
presenter.notifyCurrentTraceEntries(entries);
expect(Object.keys(uiData.tree!).length > 0).toBeTrue();
const emptyEntries = new Map<TraceType, any>();
presenter.notifyCurrentTraceEntries(emptyEntries);
expect(uiData.tree).toBeFalsy();
});
it("can update pinned items", () => {
expect(uiData.pinnedItems).toEqual([]);
const pinnedItem = new HierarchyTreeBuilder().setName("FirstPinnedItem")
.setStableId("TestItem 4").setLayerId(4).build();
presenter.updatePinnedItems(pinnedItem);
expect(uiData.pinnedItems).toContain(pinnedItem);
});
it("can update highlighted items", () => {
expect(uiData.highlightedItems).toEqual([]);
const id = "entry";
presenter.updateHighlightedItems(id);
expect(uiData.highlightedItems).toContain(id);
});
it("can update hierarchy tree", () => {
//change flat view to true
const userOptions: UserOptions = {
onlyVisible: {
name: "Only visible",
enabled: true
},
simplifyNames: {
name: "Simplify names",
enabled: true
},
flat: {
name: "Flat",
enabled: false
}
};
presenter.notifyCurrentTraceEntries(entries);
expect(uiData.tree?.children.length).toEqual(1);
presenter.updateHierarchyTree(userOptions);
expect(uiData.hierarchyUserOptions).toEqual(userOptions);
// non visible child filtered out
expect(uiData.tree?.children.length).toEqual(0);
});
it("can filter hierarchy tree", () => {
const userOptions: UserOptions = {
onlyVisible: {
name: "Only visible",
enabled: false
},
simplifyNames: {
name: "Simplify names",
enabled: true
},
flat: {
name: "Flat",
enabled: true
}
};
presenter.notifyCurrentTraceEntries(entries);
presenter.updateHierarchyTree(userOptions);
expect(uiData.tree?.children.length).toEqual(1);
presenter.filterHierarchyTree("Reject all");
// All children should be filtered out
expect(uiData.tree?.children.length).toEqual(0);
});
it("can set new properties tree and associated ui data", () => {
presenter.notifyCurrentTraceEntries(entries);
presenter.newPropertiesTree(selectedTree);
// does not check specific tree values as tree transformation method may change
expect(uiData.propertiesTree).toBeTruthy();
});
it("can filter properties tree", () => {
presenter.notifyCurrentTraceEntries(entries);
presenter.newPropertiesTree(selectedTree);
let nonTerminalChildren = uiData.propertiesTree?.children?.filter(
(child: PropertiesTreeNode) => typeof child.propertyKey === "string"
) ?? [];
expect(nonTerminalChildren.length).toEqual(3);
presenter.filterPropertiesTree("elapsed");
nonTerminalChildren = uiData.propertiesTree?.children?.filter(
(child: PropertiesTreeNode) => typeof child.propertyKey === "string"
) ?? [];
expect(nonTerminalChildren.length).toEqual(1);
});
});

View File

@@ -1,150 +0,0 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ImePresenter } from "./ime_presenter";
import { ImeUiData } from "./ime_ui_data";
import { UserOptions } from "viewers/common/user_options";
import { TraceType } from "common/trace/trace_type";
import { HierarchyTreeNode, PropertiesTreeNode } from "viewers/common/ui_tree_utils";
import { UnitTestUtils } from "test/unit/utils";
import { HierarchyTreeBuilder } from "test/unit/hierarchy_tree_builder";
describe("ImePresenterInputMethodManagerService", () => {
let presenter: ImePresenter;
let uiData: ImeUiData;
let entries: Map<TraceType, any>;
let selectedTree: HierarchyTreeNode;
beforeAll(async () => {
entries = new Map<TraceType, any>();
const entry: any = await UnitTestUtils.getInputMethodManagerServiceEntry();
entries.set(TraceType.INPUT_METHOD_MANAGER_SERVICE, [entry, null]);
selectedTree = new HierarchyTreeBuilder()
.setFilteredView(true).setKind("InputMethodManagerService").setId("managerservice")
.setStableId("managerservice").build();
});
beforeEach(async () => {
presenter = new ImePresenter((newData: ImeUiData) => {
uiData = newData;
}, [TraceType.INPUT_METHOD_MANAGER_SERVICE]);
});
it("can notify current trace entries", () => {
presenter.notifyCurrentTraceEntries(entries);
expect(uiData.hierarchyUserOptions).toBeTruthy();
expect(uiData.propertiesUserOptions).toBeTruthy();
// does not check specific tree values as tree generation method may change
expect(Object.keys(uiData.tree!).length > 0).toBeTrue();
});
it("can handle unavailable trace entry", () => {
presenter.notifyCurrentTraceEntries(entries);
expect(Object.keys(uiData.tree!).length > 0).toBeTrue();
const emptyEntries = new Map<TraceType, any>();
presenter.notifyCurrentTraceEntries(emptyEntries);
expect(uiData.tree).toBeFalsy();
});
it("can update pinned items", () => {
expect(uiData.pinnedItems).toEqual([]);
const pinnedItem = new HierarchyTreeBuilder().setName("FirstPinnedItem")
.setStableId("TestItem 4").setLayerId(4).build();
presenter.updatePinnedItems(pinnedItem);
expect(uiData.pinnedItems).toContain(pinnedItem);
});
it("can update highlighted items", () => {
expect(uiData.highlightedItems).toEqual([]);
const id = "entry";
presenter.updateHighlightedItems(id);
expect(uiData.highlightedItems).toContain(id);
});
it("can update hierarchy tree", () => {
//change flat view to true
const userOptions: UserOptions = {
onlyVisible: {
name: "Only visible",
enabled: true
},
simplifyNames: {
name: "Simplify names",
enabled: true
},
flat: {
name: "Flat",
enabled: false
}
};
presenter.notifyCurrentTraceEntries(entries);
expect(uiData.tree?.children.length).toEqual(1);
presenter.updateHierarchyTree(userOptions);
expect(uiData.hierarchyUserOptions).toEqual(userOptions);
// non visible child filtered out
expect(uiData.tree?.children.length).toEqual(0);
});
it("can filter hierarchy tree", () => {
const userOptions: UserOptions = {
onlyVisible: {
name: "Only visible",
enabled: false
},
simplifyNames: {
name: "Simplify names",
enabled: true
},
flat: {
name: "Flat",
enabled: true
}
};
presenter.notifyCurrentTraceEntries(entries);
presenter.updateHierarchyTree(userOptions);
expect(uiData.tree?.children.length).toEqual(1);
presenter.filterHierarchyTree("Reject all");
// All children should be filtered out
expect(uiData.tree?.children.length).toEqual(0);
});
it("can set new properties tree and associated ui data", () => {
presenter.notifyCurrentTraceEntries(entries);
presenter.newPropertiesTree(selectedTree);
// does not check specific tree values as tree transformation method may change
expect(uiData.propertiesTree).toBeTruthy();
});
it("can filter properties tree", () => {
presenter.notifyCurrentTraceEntries(entries);
presenter.newPropertiesTree(selectedTree);
let nonTerminalChildren = uiData.propertiesTree?.children?.filter(
(child: PropertiesTreeNode) => typeof child.propertyKey === "string"
) ?? [];
expect(nonTerminalChildren.length).toEqual(13);
presenter.filterPropertiesTree("cur");
nonTerminalChildren = uiData.propertiesTree?.children?.filter(
(child: PropertiesTreeNode) => typeof child.propertyKey === "string"
) ?? [];
expect(nonTerminalChildren.length).toEqual(8);
});
});

View File

@@ -1,149 +0,0 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ImePresenter } from "./ime_presenter";
import { ImeUiData } from "./ime_ui_data";
import { UserOptions } from "viewers/common/user_options";
import { TraceType } from "common/trace/trace_type";
import { HierarchyTreeNode, PropertiesTreeNode } from "viewers/common/ui_tree_utils";
import { UnitTestUtils } from "test/unit/utils";
import { HierarchyTreeBuilder } from "test/unit/hierarchy_tree_builder";
describe("ImePresenterInputMethodService", () => {
let presenter: ImePresenter;
let uiData: ImeUiData;
let entries: Map<TraceType, any>;
let selectedTree: HierarchyTreeNode;
beforeAll(async () => {
entries = new Map<TraceType, any>();
const entry: any = await UnitTestUtils.getInputMethodServiceEntry();
entries.set(TraceType.INPUT_METHOD_SERVICE, [entry, null]);
selectedTree = new HierarchyTreeBuilder().setKind("InputMethodService")
.setId("service").setFilteredView(true).setStableId("service").build();
});
beforeEach(async () => {
presenter = new ImePresenter((newData: ImeUiData) => {
uiData = newData;
}, [TraceType.INPUT_METHOD_SERVICE]);
});
it("can notify current trace entries", () => {
presenter.notifyCurrentTraceEntries(entries);
expect(uiData.hierarchyUserOptions).toBeTruthy();
expect(uiData.propertiesUserOptions).toBeTruthy();
// does not check specific tree values as tree generation method may change
expect(Object.keys(uiData.tree!).length > 0).toBeTrue();
});
it("can handle unavailable trace entry", () => {
presenter.notifyCurrentTraceEntries(entries);
expect(Object.keys(uiData.tree!).length > 0).toBeTrue();
const emptyEntries = new Map<TraceType, any>();
presenter.notifyCurrentTraceEntries(emptyEntries);
expect(uiData.tree).toBeFalsy();
});
it("can update pinned items", () => {
expect(uiData.pinnedItems).toEqual([]);
const pinnedItem = new HierarchyTreeBuilder().setName("FirstPinnedItem")
.setStableId("TestItem 4").setLayerId(4).build();
presenter.updatePinnedItems(pinnedItem);
expect(uiData.pinnedItems).toContain(pinnedItem);
});
it("can update highlighted items", () => {
expect(uiData.highlightedItems).toEqual([]);
const id = "entry";
presenter.updateHighlightedItems(id);
expect(uiData.highlightedItems).toContain(id);
});
it("can update hierarchy tree", () => {
//change flat view to true
const userOptions: UserOptions = {
onlyVisible: {
name: "Only visible",
enabled: true
},
simplifyNames: {
name: "Simplify names",
enabled: true
},
flat: {
name: "Flat",
enabled: false
}
};
presenter.notifyCurrentTraceEntries(entries);
expect(uiData.tree?.children.length).toEqual(1);
presenter.updateHierarchyTree(userOptions);
expect(uiData.hierarchyUserOptions).toEqual(userOptions);
// non visible child filtered out
expect(uiData.tree?.children.length).toEqual(0);
});
it("can filter hierarchy tree", () => {
const userOptions: UserOptions = {
onlyVisible: {
name: "Only visible",
enabled: false
},
simplifyNames: {
name: "Simplify names",
enabled: true
},
flat: {
name: "Flat",
enabled: true
}
};
presenter.notifyCurrentTraceEntries(entries);
presenter.updateHierarchyTree(userOptions);
expect(uiData.tree?.children.length).toEqual(1);
presenter.filterHierarchyTree("Reject all");
// All children should be filtered out
expect(uiData.tree?.children.length).toEqual(0);
});
it("can set new properties tree and associated ui data", () => {
presenter.notifyCurrentTraceEntries(entries);
presenter.newPropertiesTree(selectedTree);
// does not check specific tree values as tree transformation method may change
expect(uiData.propertiesTree).toBeTruthy();
});
it("can filter properties tree", () => {
presenter.notifyCurrentTraceEntries(entries);
presenter.newPropertiesTree(selectedTree);
let nonTerminalChildren = uiData.propertiesTree?.children?.filter(
(child: PropertiesTreeNode) => typeof child.propertyKey === "string"
) ?? [];
expect(nonTerminalChildren.length).toEqual(13);
presenter.filterPropertiesTree("visib");
nonTerminalChildren = uiData.propertiesTree?.children?.filter(
(child: PropertiesTreeNode) => typeof child.propertyKey === "string"
) ?? [];
expect(nonTerminalChildren.length).toEqual(3);
});
});

View File

@@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { HierarchyTreeNode, PropertiesTreeNode } from "./ui_tree_utils";
import { HierarchyTreeNode, PropertiesTreeNode } from "viewers/common/ui_tree_utils";
import { UserOptions } from "viewers/common/user_options";
import { TraceType } from "common/trace/trace_type";
import { TableProperties } from "viewers/common/table_properties";
import { ImeAdditionalProperties } from "viewers/common/ime_additional_properties";
export class ImeUiData {
dependencies: Array<TraceType>;
@@ -25,6 +27,8 @@ export class ImeUiData {
propertiesUserOptions: UserOptions = {};
tree: HierarchyTreeNode | null = null;
propertiesTree: PropertiesTreeNode | null = null;
hierarchyTableProperties: TableProperties | null = null;
additionalProperties: ImeAdditionalProperties | null = null;
constructor(dependencies?: Array<TraceType>) {
this.dependencies = dependencies ?? [];

View File

@@ -23,6 +23,7 @@ import {TreeUtils, FilterType} from "common/utils/tree_utils";
class ProcessedWindowManagerState {
constructor(
public name: string,
public stableId: string,
public focusedApp: string,
public focusedWindow: WindowState,
@@ -31,17 +32,20 @@ class ProcessedWindowManagerState {
public protoImeControlTarget: any,
public protoImeInputTarget: any,
public protoImeLayeringTarget: any,
public protoImeInsetsSourceProvider: any) {
}
public protoImeInsetsSourceProvider: any,
public proto: any
) {}
}
class ImeLayers {
constructor(
public name: string,
public imeContainer: Layer,
public inputMethodSurface: Layer,
public focusedWindow: Layer|undefined,
public taskOfImeContainer: Layer|undefined,
public taskOfImeSnapshot: Layer|undefined) {
public taskOfImeSnapshot: Layer|undefined
) {
}
}
@@ -50,6 +54,7 @@ class ImeUtils {
const displayContent = entry.root.children[0];
return new ProcessedWindowManagerState(
entry.name,
entry.stableId,
entry.focusedApp,
entry.focusedWindow,
@@ -58,7 +63,8 @@ class ImeUtils {
this.getImeControlTargetProperty(displayContent.proto),
this.getImeInputTargetProperty(displayContent.proto),
this.getImeLayeringTargetProperty(displayContent.proto),
displayContent.proto.imeInsetsSourceProvider
displayContent.proto.imeInsetsSourceProvider,
entry.proto,
);
}
@@ -91,6 +97,7 @@ class ImeUtils {
this.findAncestorTaskLayerOfImeLayer(entry, TreeUtils.makeNodeFilter("IME-snapshot"));
return new ImeLayers(
entry.name,
imeContainer,
inputMethodSurface,
focusedWindowLayer,
@@ -154,4 +161,4 @@ class ImeUtils {
}
}
export {ImeUtils};
export {ImeUtils, ProcessedWindowManagerState, ImeLayers};

View File

@@ -14,17 +14,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ImeUiData } from "./ime_ui_data";
import { ImeUiData } from "viewers/common/ime_ui_data";
import { TraceType } from "common/trace/trace_type";
import { UserOptions } from "viewers/common/user_options";
import { HierarchyTreeNode, PropertiesTreeNode } from "viewers/common/ui_tree_utils";
import { TreeGenerator } from "viewers/common/tree_generator";
import { TreeTransformer } from "viewers/common/tree_transformer";
import { TreeUtils, FilterType } from "common/utils/tree_utils";
import { TraceTreeNode } from "common/trace/trace_tree_node";
import { ImeLayers, ImeUtils, ProcessedWindowManagerState } from "viewers/common/ime_utils";
import { ImeAdditionalProperties } from "viewers/common/ime_additional_properties";
import { TableProperties } from "viewers/common/table_properties";
export type NotifyImeViewCallbackType = (uiData: ImeUiData) => void;
type NotifyImeViewCallbackType = (uiData: ImeUiData) => void;
export class ImePresenter {
export abstract class PresenterInputMethod {
constructor(
notifyViewCallback: NotifyImeViewCallbackType,
dependencies: Array<TraceType>
@@ -83,34 +87,71 @@ export class ImePresenter {
}
public newPropertiesTree(selectedItem: HierarchyTreeNode) {
this.additionalPropertyEntry = null;
this.selectedHierarchyTree = selectedItem;
this.updateSelectedTreeUiData();
}
public newAdditionalPropertiesTree(selectedItem: any) {
this.selectedHierarchyTree = new HierarchyTreeNode(selectedItem.name, "AdditionalProperty", "AdditionalProperty");
this.additionalPropertyEntry = {
name: selectedItem.name,
kind: "AdditionalProperty",
children: [],
stableId: "AdditionalProperty",
proto: selectedItem.proto,
};
this.updateSelectedTreeUiData();
}
public notifyCurrentTraceEntries(entries: Map<TraceType, [any, any]>) {
this.uiData = new ImeUiData(this.dependencies);
this.uiData.hierarchyUserOptions = this.hierarchyUserOptions;
this.uiData.propertiesUserOptions = this.propertiesUserOptions;
const imeEntries = entries.get(this.dependencies[0]);
if (imeEntries) {
this.entry = imeEntries[0];
if (this.entry) {
this.uiData.highlightedItems = this.highlightedItems;
this.uiData.tree = this.generateTree();
}
const imEntries = entries.get(this.dependencies[0]);
if (imEntries && imEntries[0]) {
this.entry = imEntries[0];
this.uiData.highlightedItems = this.highlightedItems;
const wmEntries = entries.get(TraceType.WINDOW_MANAGER);
const sfEntries = entries.get(TraceType.SURFACE_FLINGER);
this.uiData.additionalProperties = this.getAdditionalProperties(
wmEntries ? wmEntries[0] : undefined,
sfEntries ? sfEntries[0] : undefined
);
this.uiData.tree = this.generateTree();
this.uiData.hierarchyTableProperties = this.updateHierarchyTableProperties();
}
this.notifyViewCallback(this.uiData);
}
private updateSelectedTreeUiData() {
if (this.selectedHierarchyTree) {
this.uiData.propertiesTree = this.getTreeWithTransformedProperties(this.selectedHierarchyTree);
protected getAdditionalProperties(wmEntry: TraceTreeNode | undefined, sfEntry: TraceTreeNode | undefined) {
let wmProperties: ProcessedWindowManagerState | undefined;
let sfProperties: ImeLayers | undefined;
let sfSubtrees: any[];
if (wmEntry) {
wmProperties = ImeUtils.processWindowManagerTraceEntry(wmEntry);
sfProperties = ImeUtils.getImeLayers(sfEntry, wmProperties);
sfSubtrees = [sfProperties?.taskOfImeContainer, sfProperties?.taskOfImeSnapshot]
.filter((node) => node) // filter away null values
.map((node) => {
node.kind = "SF subtree - " + node.id;
return node;
});
this.entry?.children.push(...sfSubtrees);
}
this.notifyViewCallback(this.uiData);
return new ImeAdditionalProperties(
wmProperties,
sfProperties,
);
}
private generateTree() {
protected generateTree() {
if (!this.entry) {
return null;
}
@@ -126,6 +167,12 @@ export class ImePresenter {
return tree;
}
private updateSelectedTreeUiData() {
if (this.selectedHierarchyTree) {
this.uiData.propertiesTree = this.getTreeWithTransformedProperties(this.selectedHierarchyTree);
}
this.notifyViewCallback(this.uiData);
}
private updatePinnedIds(newId: string) {
if (this.pinnedIds.includes(newId)) {
this.pinnedIds = this.pinnedIds.filter(pinned => pinned != newId);
@@ -136,24 +183,27 @@ export class ImePresenter {
private getTreeWithTransformedProperties(selectedTree: HierarchyTreeNode): PropertiesTreeNode {
const transformer = new TreeTransformer(selectedTree, this.propertiesFilter)
.setOnlyProtoDump(this.additionalPropertyEntry != null)
.setIsShowDefaults(this.propertiesUserOptions["showDefaults"]?.enabled)
.setTransformerOptions({skip: selectedTree.skip})
.setProperties(this.entry);
.setProperties(this.additionalPropertyEntry ?? this.entry);
const transformedTree = transformer.transform();
return transformedTree;
}
readonly notifyViewCallback: NotifyImeViewCallbackType;
readonly dependencies: Array<TraceType>;
uiData: ImeUiData;
private hierarchyFilter: FilterType = TreeUtils.makeNodeFilter("");
private propertiesFilter: FilterType = TreeUtils.makeNodeFilter("");
private highlightedItems: Array<string> = [];
private pinnedItems: Array<HierarchyTreeNode> = [];
private pinnedIds: Array<string> = [];
private selectedHierarchyTree: HierarchyTreeNode | null = null;
private entry: any = null;
private hierarchyUserOptions: UserOptions = {
readonly notifyViewCallback: NotifyImeViewCallbackType;
protected readonly dependencies: Array<TraceType>;
protected uiData: ImeUiData;
protected highlightedItems: Array<string> = [];
protected entry: TraceTreeNode | null = null;
protected additionalPropertyEntry: TraceTreeNode | null = null;
protected hierarchyUserOptions: UserOptions = {
simplifyNames: {
name: "Simplify names",
enabled: true
@@ -167,8 +217,7 @@ export class ImePresenter {
enabled: false
}
};
private propertiesUserOptions: UserOptions = {
protected propertiesUserOptions: UserOptions = {
showDefaults: {
name: "Show defaults",
enabled: true,
@@ -179,4 +228,6 @@ export class ImePresenter {
`
},
};
protected abstract updateHierarchyTableProperties(): TableProperties;
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.d
*/
import { ImeUiData } from "viewers/common/ime_ui_data";
import { UserOptions } from "viewers/common/user_options";
import { TraceType } from "common/trace/trace_type";
import { HierarchyTreeNode, PropertiesTreeNode } from "viewers/common/ui_tree_utils";
import { HierarchyTreeBuilder } from "test/unit/hierarchy_tree_builder";
import { PresenterInputMethod } from "./presenter_input_method";
import { PresenterInputMethodClients } from "viewers/viewer_input_method_clients/presenter_input_method_clients";
import { PresenterInputMethodService } from "viewers/viewer_input_method_service/presenter_input_method_service";
import { PresenterInputMethodManagerService } from "viewers/viewer_input_method_manager_service/presenter_input_method_manager_service";
export function executePresenterInputMethodTests(
getEntry: () => any,
selected: HierarchyTreeNode,
propertiesTreeFilterString: string,
expectedChildren: [number, number],
PresenterInputMethod: typeof PresenterInputMethodClients | typeof PresenterInputMethodService | typeof PresenterInputMethodManagerService,
traceType: TraceType,
) {
describe("PresenterInputMethod", () => {
let presenter: PresenterInputMethod;
let uiData: ImeUiData;
let entries: Map<TraceType, any>;
let selectedTree: HierarchyTreeNode;
beforeAll(async () => {
const entry = await getEntry();
entries = new Map<TraceType, any>();
entries.set(traceType, [entry, null]);
selectedTree = selected;
});
beforeEach(async () => {
presenter = new PresenterInputMethod((newData: ImeUiData) => {
uiData = newData;
}, [traceType]);
});
it("can notify current trace entries", () => {
presenter.notifyCurrentTraceEntries(entries);
expect(uiData.hierarchyUserOptions).toBeTruthy();
expect(uiData.propertiesUserOptions).toBeTruthy();
// does not check specific tree values as tree generation method may change
expect(Object.keys(uiData.tree!).length > 0).toBeTrue();
});
it("can handle unavailable trace entry", () => {
presenter.notifyCurrentTraceEntries(entries);
expect(Object.keys(uiData.tree!).length > 0).toBeTrue();
const emptyEntries = new Map<TraceType, any>();
presenter.notifyCurrentTraceEntries(emptyEntries);
expect(uiData.tree).toBeFalsy();
});
it("can update pinned items", () => {
expect(uiData.pinnedItems).toEqual([]);
const pinnedItem = new HierarchyTreeBuilder().setName("FirstPinnedItem")
.setStableId("TestItem 4").setLayerId(4).build();
presenter.updatePinnedItems(pinnedItem);
expect(uiData.pinnedItems).toContain(pinnedItem);
});
it("can update highlighted items", () => {
expect(uiData.highlightedItems).toEqual([]);
const id = "entry";
presenter.updateHighlightedItems(id);
expect(uiData.highlightedItems).toContain(id);
});
it("can update hierarchy tree", () => {
//change flat view to true
const userOptions: UserOptions = {
onlyVisible: {
name: "Only visible",
enabled: true
},
simplifyNames: {
name: "Simplify names",
enabled: true
},
flat: {
name: "Flat",
enabled: false
}
};
presenter.notifyCurrentTraceEntries(entries);
expect(uiData.tree?.children.length).toEqual(1);
presenter.updateHierarchyTree(userOptions);
expect(uiData.hierarchyUserOptions).toEqual(userOptions);
// non visible child filtered out
expect(uiData.tree?.children.length).toEqual(0);
});
it("can filter hierarchy tree", () => {
const userOptions: UserOptions = {
onlyVisible: {
name: "Only visible",
enabled: false
},
simplifyNames: {
name: "Simplify names",
enabled: true
},
flat: {
name: "Flat",
enabled: true
}
};
presenter.notifyCurrentTraceEntries(entries);
presenter.updateHierarchyTree(userOptions);
expect(uiData.tree?.children.length).toEqual(1);
presenter.filterHierarchyTree("Reject all");
// All children should be filtered out
expect(uiData.tree?.children.length).toEqual(0);
});
it("can set new properties tree and associated ui data", () => {
presenter.notifyCurrentTraceEntries(entries);
presenter.newPropertiesTree(selectedTree);
// does not check specific tree values as tree transformation method may change
expect(uiData.propertiesTree).toBeTruthy();
});
it("can filter properties tree", () => {
presenter.notifyCurrentTraceEntries(entries);
presenter.newPropertiesTree(selectedTree);
let nonTerminalChildren = uiData.propertiesTree?.children?.filter(
(child: PropertiesTreeNode) => typeof child.propertyKey === "string"
) ?? [];
expect(nonTerminalChildren.length).toEqual(expectedChildren[0]);
presenter.filterPropertiesTree(propertiesTreeFilterString);
nonTerminalChildren = uiData.propertiesTree?.children?.filter(
(child: PropertiesTreeNode) => typeof child.propertyKey === "string"
) ?? [];
expect(nonTerminalChildren.length).toEqual(expectedChildren[1]);
});
});
}

View File

@@ -0,0 +1,18 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export type TableProperties = {
[key: string]: string | boolean | undefined;
}

View File

@@ -109,7 +109,7 @@ describe("TreeTransformer", () => {
const filter = TreeUtils.makeNodeFilter("");
const transformer = new TreeTransformer(selectedTree, filter)
.showOnlyProtoDump()
.setOnlyProtoDump(true)
.setProperties(entry);
const transformedTree = transformer.transform();
@@ -151,7 +151,7 @@ describe("TreeTransformer", () => {
const filter = TreeUtils.makeNodeFilter("");
const transformer = new TreeTransformer(selectedTree, filter)
.setIsShowDiff(true)
.showOnlyProtoDump()
.setOnlyProtoDump(true)
.setProperties(entry)
.setDiffProperties(null);

View File

@@ -56,8 +56,8 @@ export class TreeTransformer {
this.setTransformerOptions({});
}
public showOnlyProtoDump(): TreeTransformer {
this.onlyProtoDump = true;
public setOnlyProtoDump(onlyProto: boolean): TreeTransformer {
this.onlyProtoDump = onlyProto;
return this;
}
@@ -79,7 +79,7 @@ export class TreeTransformer {
return this;
}
public setProperties(currentEntry: TraceTreeNode): TreeTransformer {
public setProperties(currentEntry: TraceTreeNode | null): TreeTransformer {
const currFlickerItem = this.getOriginalFlickerItem(currentEntry, this.stableId);
const target = currFlickerItem ? currFlickerItem.obj ?? currFlickerItem : null;
ObjectFormatter.displayDefaults = this.isShowDefaults;
@@ -96,7 +96,7 @@ export class TreeTransformer {
return this;
}
public getOriginalFlickerItem(entry: TraceTreeNode, stableId: string): TraceTreeNode | null {
public getOriginalFlickerItem(entry: TraceTreeNode | null, stableId: string): TraceTreeNode | null {
return this.findFlickerItem(entry, stableId);
}

View File

@@ -20,5 +20,6 @@ export const ViewerEvents = {
HierarchyFilterChange: "HierarchyFilterChange",
SelectedTreeChange: "SelectedTreeChange",
PropertiesUserOptionsChange: "PropertiesUserOptionsChange",
PropertiesFilterChange: "PropertiesFilterChange"
PropertiesFilterChange: "PropertiesFilterChange",
AdditionalPropertySelected: "AdditionalPropertySelected"
};

View File

@@ -16,26 +16,14 @@
import {TraceType} from "common/trace/trace_type";
import {Viewer} from "viewers/viewer";
import {ViewerEvents} from "viewers/common/viewer_events";
import { ImePresenter } from "viewers/common/ime_presenter";
import { PresenterInputMethod } from "viewers/common/presenter_input_method";
import { ImeUiData } from "viewers/common/ime_ui_data";
class ViewerInputMethod implements Viewer {
abstract class ViewerInputMethod implements Viewer {
constructor() {
this.view = document.createElement("viewer-input-method");
this.presenter = new ImePresenter((uiData: ImeUiData) => {
// Angular does not deep watch @Input properties. Clearing inputData to null before repopulating
// automatically ensures that the UI will change via the Angular change detection cycle. Without
// resetting, Angular does not auto-detect that inputData has changed.
(this.view as any).inputData = null;
(this.view as any).inputData = uiData;
}, this.getDependencies());
this.view.addEventListener(ViewerEvents.HierarchyPinnedChange, (event) => this.presenter.updatePinnedItems(((event as CustomEvent).detail.pinnedItem)));
this.view.addEventListener(ViewerEvents.HighlightedChange, (event) => this.presenter.updateHighlightedItems(`${(event as CustomEvent).detail.id}`));
this.view.addEventListener(ViewerEvents.HierarchyUserOptionsChange, (event) => this.presenter.updateHierarchyTree((event as CustomEvent).detail.userOptions));
this.view.addEventListener(ViewerEvents.HierarchyFilterChange, (event) => this.presenter.filterHierarchyTree((event as CustomEvent).detail.filterString));
this.view.addEventListener(ViewerEvents.PropertiesUserOptionsChange, (event) => this.presenter.updatePropertiesTree((event as CustomEvent).detail.userOptions));
this.view.addEventListener(ViewerEvents.PropertiesFilterChange, (event) => this.presenter.filterPropertiesTree((event as CustomEvent).detail.filterString));
this.view.addEventListener(ViewerEvents.SelectedTreeChange, (event) => this.presenter.newPropertiesTree((event as CustomEvent).detail.selectedItem));
this.presenter = this.initialisePresenter();
this.addViewerEventListeners();
}
public notifyCurrentTraceEntries(entries: Map<TraceType, any>): void {
@@ -46,17 +34,32 @@ class ViewerInputMethod implements Viewer {
return this.view;
}
public getTitle(): string {
return "";
protected imeUiCallback = (uiData: ImeUiData) => {
// Angular does not deep watch @Input properties. Clearing inputData to null before repopulating
// automatically ensures that the UI will change via the Angular change detection cycle. Without
// resetting, Angular does not auto-detect that inputData has changed.
(this.view as any).inputData = null;
(this.view as any).inputData = uiData;
};
protected addViewerEventListeners() {
this.view.addEventListener(ViewerEvents.HierarchyPinnedChange, (event) => this.presenter.updatePinnedItems(((event as CustomEvent).detail.pinnedItem)));
this.view.addEventListener(ViewerEvents.HighlightedChange, (event) => this.presenter.updateHighlightedItems(`${(event as CustomEvent).detail.id}`));
this.view.addEventListener(ViewerEvents.HierarchyUserOptionsChange, (event) => this.presenter.updateHierarchyTree((event as CustomEvent).detail.userOptions));
this.view.addEventListener(ViewerEvents.HierarchyFilterChange, (event) => this.presenter.filterHierarchyTree((event as CustomEvent).detail.filterString));
this.view.addEventListener(ViewerEvents.PropertiesUserOptionsChange, (event) => this.presenter.updatePropertiesTree((event as CustomEvent).detail.userOptions));
this.view.addEventListener(ViewerEvents.PropertiesFilterChange, (event) => this.presenter.filterPropertiesTree((event as CustomEvent).detail.filterString));
this.view.addEventListener(ViewerEvents.SelectedTreeChange, (event) => this.presenter.newPropertiesTree((event as CustomEvent).detail.selectedItem));
this.view.addEventListener(ViewerEvents.AdditionalPropertySelected, (event) => this.presenter.newAdditionalPropertiesTree((event as CustomEvent).detail.selectedItem));
}
public getDependencies(): TraceType[] {
return ViewerInputMethod.DEPENDENCIES;
}
abstract getDependencies(): TraceType[];
abstract getTitle(): string;
protected abstract initialisePresenter(): PresenterInputMethod;
public static readonly DEPENDENCIES: TraceType[] = [];
private view: HTMLElement;
private presenter: ImePresenter;
protected view: HTMLElement;
protected presenter: PresenterInputMethod;
}
export {ViewerInputMethod};

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {ComponentFixture, TestBed} from "@angular/core/testing";
import { CoordinatesTableComponent } from "./coordinates_table.component";
describe("CoordinatesTableComponent", () => {
let fixture: ComponentFixture<CoordinatesTableComponent>;
let component: CoordinatesTableComponent;
let htmlElement: HTMLElement;
beforeAll(async () => {
await TestBed.configureTestingModule({
declarations: [
CoordinatesTableComponent
],
schemas: []
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(CoordinatesTableComponent);
component = fixture.componentInstance;
htmlElement = fixture.nativeElement;
});
it("can be created", () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, Input } from "@angular/core";
@Component({
selector: "coordinates-table",
template: `
<span class="coord-null-value" *ngIf="!hasCoordinates()">null</span>
<div class="coord-table-wrapper" *ngIf="hasCoordinates()">
<table class="table">
<tr class="header-row">
<td>Left</td>
<td>Top</td>
<td>Right</td>
<td>Bottom</td>
</tr>
<tr>
<td>{{ coordinates.left }}</td>
<td>{{ coordinates.top }}</td>
<td>{{ coordinates.right }}</td>
<td>{{ coordinates.bottom }}</td>
</tr>
</table>
</div>
`,
styles: [
`
.coord-null-value {
color: rgba(0, 0, 0, 0.75);
}
.coord-table-wrapper {
margin-left: 10px;
display: inline-flex;
padding: 3px 0px;
}
.coord-table-wrapper td, .coord-table-wrapper th {
height: auto;
border: 1px solid ar(--default-border);
}
.coord-table-wrapper .header-row td {
color: gray;
font-weight: 600;
}
`
],
})
export class CoordinatesTableComponent {
@Input() coordinates!: any;
hasCoordinates() {
return this.coordinates.left || this.coordinates.top ||
this.coordinates.right || this.coordinates.bottom;
}
}

View File

@@ -20,6 +20,7 @@ import { UiTreeUtils, HierarchyTreeNode, UiTreeNode } from "viewers/common/ui_tr
import { nodeStyles } from "viewers/components/styles/node.styles";
import { ViewerEvents } from "viewers/common/viewer_events";
import { TraceType } from "common/trace/trace_type";
import { TableProperties } from "viewers/common/table_properties";
@Component({
selector: "hierarchy-view",
@@ -45,6 +46,11 @@ import { TraceType } from "common/trace/trace_type";
(ngModelChange)="updateTree()"
>{{userOptions[option].name}}</mat-checkbox>
</div>
<properties-table
*ngIf="tableProperties"
class="properties-table"
[properties]="tableProperties"
></properties-table>
<div class="pinned-items" *ngIf="pinnedItems.length > 0">
<tree-node
*ngFor="let pinnedItem of pinnedItems"
@@ -133,6 +139,13 @@ import { TraceType } from "common/trace/trace_type";
overflow-y: auto;
}
.properties-table {
padding-top: 5px;
position: relative;
display: block;
width: 100%;
}
.pinned-items {
border: 2px solid yellow;
position: relative;
@@ -151,6 +164,7 @@ export class HierarchyComponent {
isHighlighted = UiTreeUtils.isHighlighted;
@Input() tree!: HierarchyTreeNode | null;
@Input() tableProperties?: TableProperties | null;
@Input() dependencies: Array<TraceType> = [];
@Input() highlightedItems: Array<string> = [];
@Input() pinnedItems: Array<HierarchyTreeNode> = [];
@@ -167,8 +181,9 @@ export class HierarchyComponent {
public maxHierarchyHeight() {
const headerHeight = this.elementRef.nativeElement.querySelector(".view-header").clientHeight;
const max = this.tableProperties ? 400 : 800;
return {
height: `${800 - headerHeight}px`
height: `${max - headerHeight}px`
};
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {ComponentFixture, TestBed} from "@angular/core/testing";
import { ImeAdditionalPropertiesComponent } from "./ime_additional_properties.component";
import { MatCardModule } from "@angular/material/card";
describe("ImeAdditionalPropertiesComponent", () => {
let fixture: ComponentFixture<ImeAdditionalPropertiesComponent>;
let component: ImeAdditionalPropertiesComponent;
let htmlElement: HTMLElement;
beforeAll(async () => {
await TestBed.configureTestingModule({
imports: [
MatCardModule,
],
declarations: [
ImeAdditionalPropertiesComponent
],
schemas: []
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ImeAdditionalPropertiesComponent);
component = fixture.componentInstance;
htmlElement = fixture.nativeElement;
});
it("can be created", () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,513 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, ElementRef, Inject, Input } from "@angular/core";
import { ImeAdditionalProperties } from "viewers/common/ime_additional_properties";
import { UiTreeUtils } from "viewers/common/ui_tree_utils";
import { ViewerEvents } from "viewers/common/viewer_events";
@Component({
selector: "ime-additional-properties",
template: `
<mat-card-header class="view-header">
<div class="title-filter">
<span class="additional-properties-title">WM & SF Properties</span>
</div>
</mat-card-header>
<mat-card-content class="additional-properties-content">
<div *ngIf="isAllPropertiesNull()" class="group">
There is no corresponding WM / SF additionalProperties for this IME entry
no WM / SF entry is recorded before this IME entry in time.
View later frames for WM & SF properties.
</div>
<div *ngIf="isImeManagerService">
<div class="group">
<button
class="text-button group-header"
*ngIf="wmProtoOrNull()"
[class]="{ 'selected': isHighlighted(wmProtoOrNull()) }"
(click)="onClickShowInPropertiesPanel(wmProtoOrNull(), additionalProperties.wm?.name)">
WMState
</button>
<span class="group-header" *ngIf="!wmProtoOrNull()">WMState</span>
<div class="full-width">
<span class="value" *ngIf="additionalProperties.wm">{{
additionalProperties.wm.name }}</span>
<span *ngIf="!additionalProperties.wm">There is no corresponding WMState entry.</span>
</div>
</div>
<div class="group" *ngIf="wmInsetsSourceProviderOrNull()">
<button
class="text-button group-header"
[class]="{ 'selected': isHighlighted(wmInsetsSourceProviderOrNull()) }"
(click)="onClickShowInPropertiesPanel(wmInsetsSourceProviderOrNull(), 'Ime Insets Source Provider')">
IME Insets Source Provider
</button>
<div class="full-width">
<div></div>
<span class="key">Source Frame:</span>
<coordinates-table
[coordinates]="wmInsetsSourceProviderSourceFrameOrNull()"
></coordinates-table>
<div></div>
<span class="key">Source Visible:</span>
<span class="value">{{
wmInsetsSourceProviderSourceVisibleOrNull() }}</span>
<div></div>
<span class="key">Source Visible Frame:</span>
<coordinates-table
[coordinates]="wmInsetsSourceProviderSourceVisibleFrameOrNull()"
></coordinates-table>
<div></div>
<span class="key">Position:</span>
<span class="value">{{ wmInsetsSourceProviderPositionOrNull() }}</span>
<div></div>
<span class="key">IsLeashReadyForDispatching:</span>
<span class="value">{{
wmInsetsSourceProviderIsLeashReadyOrNull() }}</span>
<div></div>
<span class="key">Controllable:</span>
<span class="value">{{
wmInsetsSourceProviderControllableOrNull() }}</span>
<div></div>
</div>
</div>
<div class="group" *ngIf="wmImeControlTargetOrNull()">
<button
class="text-button group-header"
[class]="{ 'selected': isHighlighted(wmImeControlTargetOrNull()) }"
(click)="onClickShowInPropertiesPanel(wmImeControlTargetOrNull(), 'Ime Control Target')">
IME Control Target
</button>
<div class="full-width">
<span class="key" *ngIf="wmImeControlTargetTitleOrNull()">Title:</span>
<span class="value" *ngIf="wmImeControlTargetTitleOrNull()">{{
wmImeControlTargetTitleOrNull() }}</span>
</div>
</div>
<div class="group" *ngIf="wmImeInputTargetOrNull()">
<button
class="text-button group-header"
[class]="{ 'selected': isHighlighted(wmImeInputTargetOrNull()) }"
(click)="onClickShowInPropertiesPanel(wmImeInputTargetOrNull(), 'Ime Input Target')">
IME Input Target
</button>
<div class="full-width">
<span class="key" *ngIf="wmImeInputTargetTitleOrNull()">Title:</span>
<span class="value" *ngIf="wmImeInputTargetTitleOrNull()">{{
wmImeInputTargetTitleOrNull() }}</span>
</div>
</div>
<div class="group" *ngIf="wmImeLayeringTargetOrNull()">
<button
class="text-button group-header"
[class]="{ 'selected': isHighlighted(wmImeLayeringTargetOrNull()) }"
(click)="onClickShowInPropertiesPanel(wmImeLayeringTargetOrNull(), 'Ime Layering Target')">
IME Layering Target
</button>
<div class="full-width">
<span class="key" *ngIf="wmImeLayeringTargetTitleOrNull()">Title:</span>
<span class="value" *ngIf="wmImeLayeringTargetTitleOrNull()">{{
wmImeLayeringTargetTitleOrNull() }}</span>
</div>
</div>
</div>
<div *ngIf="!isImeManagerService">
<!-- Ime Client or Ime Service -->
<div class="group">
<button
class="text-button group-header"
*ngIf="wmProtoOrNull()"
[class]="{ 'selected': isHighlighted(wmProtoOrNull()) }"
(click)="onClickShowInPropertiesPanel(wmProtoOrNull(), additionalProperties.wm?.name)">
WMState
</button>
<span class="group-header" *ngIf="!wmProtoOrNull()">WMState</span>
<div class="full-width">
<span class="value" *ngIf="additionalProperties.wm">{{
additionalProperties.wm.name }}</span>
<span *ngIf="!additionalProperties.wm">There is no corresponding WMState entry.</span>
</div>
</div>
<div class="group">
<span class="group-header">SFLayer</span>
<div class="full-width">
<span class="value" *ngIf="additionalProperties.sf">{{
additionalProperties.sf.name }}</span>
<span *ngIf="!additionalProperties.sf">There is no corresponding SFLayer entry.</span>
</div>
</div>
<div class="group" *ngIf="additionalProperties.wm">
<span class="group-header">Focus</span>
<div class="full-width">
<span class="key">Focused App:</span>
<span class="value">{{ additionalProperties.wm.focusedApp }}</span>
<div></div>
<span class="key">Focused Activity:</span>
<span class="value">{{ additionalProperties.wm.focusedActivity }}</span>
<div></div>
<span class="key">Focused Window:</span>
<span class="value">{{ additionalProperties.wm.focusedWindow }}</span>
<div></div>
<span class="key" *ngIf="additionalProperties.sf">Focused Window Color:</span>
<span class="value" *ngIf="additionalProperties.sf">{{
additionalProperties.sf.focusedWindow.color
}}</span>
<div></div>
<span class="key">Input Control Target Frame:</span>
<coordinates-table
[coordinates]="wmControlTargetFrameOrNull()"
></coordinates-table>
<div></div>
</div>
</div>
<div class="group">
<span class="group-header">Visibility</span>
<div class="full-width">
<span class="key" *ngIf="additionalProperties.wm">InputMethod Window:</span>
<span class="value" *ngIf="additionalProperties.wm">{{
additionalProperties.wm.isInputMethodWindowVisible
}}</span>
<div></div>
<span class="key" *ngIf="additionalProperties.sf">InputMethod Surface:</span>
<span class="value" *ngIf="additionalProperties.sf">{{
additionalProperties.sf.inputMethodSurface.isInputMethodSurfaceVisible }}</span>
<div></div>
</div>
</div>
<div class="group" *ngIf="additionalProperties.sf">
<button
class="text-button group-header"
[class]="{ 'selected': isHighlighted(additionalProperties.sf.imeContainer) }"
(click)="onClickShowInPropertiesPanel(additionalProperties.sf.imeContainer)">
Ime Container
</button>
<div class="full-width">
<span class="key">ZOrderRelativeOfId:</span>
<span class="value">{{
additionalProperties.sf.imeContainer.zOrderRelativeOfId
}}</span>
<div></div>
<span class="key">Z:</span>
<span class="value">{{ additionalProperties.sf.imeContainer.z }}</span>
<div></div>
</div>
</div>
<div class="group" *ngIf="additionalProperties.sf">
<button
class="text-button group-header"
[class]="{
'selected': isHighlighted(additionalProperties.sf.inputMethodSurface)
}"
(click)="onClickShowInPropertiesPanel(
additionalProperties.sf.inputMethodSurface)">
Input Method Surface
</button>
<div class="full-width">
<span class="key">ScreenBounds:</span>
<coordinates-table
[coordinates]="sfImeContainerScreenBoundsOrNull()"
></coordinates-table>
<div></div>
<span class="key">Rect:</span>
<coordinates-table
[coordinates]="sfImeContainerRectOrNull()"
></coordinates-table>
<div></div>
</div>
</div>
</div>
</mat-card-content>
`,
styles: [
`
.view-header {
width: 100%;
height: 2.5rem;
border-bottom: 1px solid var(--default-border);
}
.title-filter {
position: relative;
display: flex;
align-items: center;
width: 100%;
margin-bottom: 12px;
}
.additional-properties-title {
font-weight: medium;
font-size: 16px;
}
.additional-properties-content {
display: flex;
flex-direction: column;
height: 375px;
overflow-y: auto;
overflow-x: hidden;
}
.container {
overflow: auto;
}
.group {
padding: 0.5rem;
border-bottom: thin solid rgba(0, 0, 0, 0.12);
flex-direction: row;
display: flex;
}
.group .key {
font-weight: bold;
}
.group .value {
color: rgba(0, 0, 0, 0.75);
word-break: break-all !important;
}
.group-header {
justify-content: center;
text-align: left;
padding: 0px 5px;
width: 95px;
display: inline-block;
font-size: bigger;
color: grey;
word-break: break-word;
}
.left-column {
width: 320px;
max-width: 100%;
display: inline-block;
vertical-align: top;
overflow: auto;
padding-right: 20px;
}
.right-column {
width: 320px;
max-width: 100%;
display: inline-block;
vertical-align: top;
overflow: auto;
}
.full-width {
width: 100%;
display: inline-block;
vertical-align: top;
overflow: auto;
}
.column-header {
font-weight: medium;
font-size: smaller;
}
.element-summary {
padding-top: 1rem;
}
.element-summary .key {
font-weight: bold;
}
.element-summary .value {
color: rgba(0, 0, 0, 0.75);
}
.tree-view {
overflow: auto;
}
.text-button {
border: none;
cursor: pointer;
font-size: 14px;
font-family: roboto;
color: blue;
text-decoration: underline;
text-decoration-color: blue;
background-color: inherit;
}
.text-button:focus {
color: purple;
}
.text-button.selected {
color: purple;
}
`
],
})
export class ImeAdditionalPropertiesComponent {
@Input() additionalProperties!: ImeAdditionalProperties;
@Input() isImeManagerService?: boolean;
@Input() highlightedItems: Array<string> = [];
constructor(
@Inject(ElementRef) private elementRef: ElementRef,
) {}
public isHighlighted(item: any) {
return UiTreeUtils.isHighlighted(item, this.highlightedItems);
}
public formatProto(item: any) {
if (item?.prettyPrint) {
return item.prettyPrint();
}
}
public wmProtoOrNull() {
return this.additionalProperties.wm?.proto;
}
public wmInsetsSourceProviderOrNull() {
return this.additionalProperties.wm?.protoImeInsetsSourceProvider ?
Object.assign({ "name": "Ime Insets Source Provider" },
this.additionalProperties.wm.protoImeInsetsSourceProvider) :
null;
}
public wmControlTargetFrameOrNull() {
return this.additionalProperties.wm?.protoImeInsetsSourceProvider
?.insetsSourceProvider?.controlTarget?.windowFrames?.frame || "null";
}
public wmInsetsSourceProviderPositionOrNull() {
return this.additionalProperties.wm?.protoImeInsetsSourceProvider
?.insetsSourceProvider?.control?.position || "null";
}
public wmInsetsSourceProviderIsLeashReadyOrNull() {
return this.additionalProperties.wm?.protoImeInsetsSourceProvider
?.insetsSourceProvider?.isLeashReadyForDispatching || "null";
}
public wmInsetsSourceProviderControllableOrNull() {
return this.additionalProperties.wm?.protoImeInsetsSourceProvider
?.insetsSourceProvider?.controllable || "null";
}
public wmInsetsSourceProviderSourceFrameOrNull() {
return this.additionalProperties.wm?.protoImeInsetsSourceProvider
?.insetsSourceProvider?.source?.frame || "null";
}
public wmInsetsSourceProviderSourceVisibleOrNull() {
return this.additionalProperties.wm?.protoImeInsetsSourceProvider
?.insetsSourceProvider?.source?.visible || "null";
}
public wmInsetsSourceProviderSourceVisibleFrameOrNull() {
return this.additionalProperties.wm?.protoImeInsetsSourceProvider
?.insetsSourceProvider?.source?.visibleFrame || "null";
}
public wmImeControlTargetOrNull() {
return this.additionalProperties?.wm?.protoImeControlTarget ?
Object.assign({ "name": "IME Control Target" },
this.additionalProperties.wm.protoImeControlTarget) :
null;
}
public wmImeControlTargetTitleOrNull() {
return this.additionalProperties?.wm?.protoImeControlTarget?.windowContainer
?.identifier?.title || "null";
}
public wmImeInputTargetOrNull() {
return this.additionalProperties?.wm?.protoImeInputTarget ?
Object.assign({ "name": "IME Input Target" },
this.additionalProperties.wm.protoImeInputTarget) :
null;
}
public wmImeInputTargetTitleOrNull() {
return this.additionalProperties?.wm?.protoImeInputTarget?.windowContainer
?.identifier?.title || "null";
}
public wmImeLayeringTargetOrNull() {
return this.additionalProperties?.wm?.protoImeLayeringTarget ?
Object.assign({ "name": "IME Layering Target" },
this.additionalProperties.wm.protoImeLayeringTarget) :
null;
}
public wmImeLayeringTargetTitleOrNull() {
return this.additionalProperties?.wm?.protoImeLayeringTarget?.windowContainer
?.identifier?.title || "null";
}
public sfImeContainerScreenBoundsOrNull() {
return this.additionalProperties.sf?.inputMethodSurface.screenBounds || "null";
}
public sfImeContainerRectOrNull() {
return this.additionalProperties.sf?.inputMethodSurface.rect || "null";
}
public isAllPropertiesNull() {
if (this.isImeManagerService) {
return !this.additionalProperties.wm;
} else {
return !(this.additionalProperties.wm ||
this.additionalProperties.sf);
}
}
public onClickShowInPropertiesPanel(item: any, name?: string) {
if (item.id) {
this.updateHighlightedItems(item.id);
} else {
this.updateAdditionalPropertySelected(item, name);
}
}
private updateHighlightedItems(newId: string) {
const event: CustomEvent = new CustomEvent(
ViewerEvents.HighlightedChange,
{
bubbles: true,
detail: { id: newId }
});
this.elementRef.nativeElement.dispatchEvent(event);
}
private updateAdditionalPropertySelected(item: any, name?: string) {
const itemWrapper = {
name: name,
proto: item,
};
const event: CustomEvent = new CustomEvent(
ViewerEvents.AdditionalPropertySelected,
{
bubbles: true,
detail: { selectedItem: itemWrapper }
});
this.elementRef.nativeElement.dispatchEvent(event);
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {ComponentFixture, TestBed} from "@angular/core/testing";
import { PropertiesTableComponent } from "./properties_table.component";
describe("PropertiesTableComponent", () => {
let fixture: ComponentFixture<PropertiesTableComponent>;
let component: PropertiesTableComponent;
let htmlElement: HTMLElement;
beforeAll(async () => {
await TestBed.configureTestingModule({
declarations: [
PropertiesTableComponent
],
schemas: []
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(PropertiesTableComponent);
component = fixture.componentInstance;
htmlElement = fixture.nativeElement;
});
it("can be created", () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,75 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, Input } from "@angular/core";
import { TableProperties } from "viewers/common/table_properties";
@Component({
selector: "properties-table",
template: `
<div class="properties-table-wrapper">
<table class="table">
<tr *ngFor="let entry of objectEntries(properties)">
<td class="table-cell-name">
<span>{{ entry[0] }}</span>
</td>
<td class="table-cell-value">
<span>{{ entry[1] != undefined ? entry[1] : 'undefined' }}</span>
</td>
</tr>
</table>
</div>
`,
styles: [
`
.properties-table-wrapper {
border-bottom: 1px solid var(--default-border);
}
.properties-table-wrapper .table-cell-name {
background-color: rgba(158, 192, 200, 0.281);
border: 1px solid var(--default-border);
height: 20px;
padding: 0;
width: 20%;
}
.properties-table-wrapper .table-cell-value {
overflow-wrap: anywhere;
border: 1px solid var(--default-border);
height: 20px;
padding: 0;
width: 80%;
}
.properties-table-wrapper table {
height: auto;
border-collapse: collapse;
width: 100%;
}
.properties-table-wrapper span {
padding: 5px;
}
`
],
})
export class PropertiesTableComponent {
objectEntries = Object.entries;
@Input() properties!: TableProperties;
}

View File

@@ -26,16 +26,24 @@ import { ImeUiData } from "viewers/common/ime_ui_data";
selector: "viewer-input-method",
template: `
<div fxLayout="row wrap" fxLayoutGap="10px grid" class="card-grid">
<mat-card class="hierarchy-view">
<hierarchy-view
[tree]="inputData?.tree ?? null"
[dependencies]="inputData?.dependencies ?? []"
[highlightedItems]="inputData?.highlightedItems ?? []"
[pinnedItems]="inputData?.pinnedItems ?? []"
[store]="store"
[userOptions]="inputData?.hierarchyUserOptions ?? {}"
></hierarchy-view>
</mat-card>
<div class="left-views">
<mat-card class="hierarchy-view" [style]="hierarchyHeight()">
<hierarchy-view
[tree]="inputData?.tree ?? null"
[dependencies]="inputData?.dependencies ?? []"
[highlightedItems]="inputData?.highlightedItems ?? []"
[pinnedItems]="inputData?.pinnedItems ?? []"
[tableProperties]="inputData?.hierarchyTableProperties"
[store]="store"
[userOptions]="inputData?.hierarchyUserOptions ?? {}"
></hierarchy-view>
</mat-card>
<mat-card *ngIf="inputData?.additionalProperties" class="ime-additional-properties-view">
<ime-additional-properties
[additionalProperties]="inputData?.additionalProperties!"
></ime-additional-properties>
</mat-card>
</div>
<mat-card class="properties-view">
<properties-view
[userOptions]="inputData?.propertiesUserOptions ?? {}"
@@ -62,10 +70,6 @@ import { ImeUiData } from "viewers/common/ime_ui_data";
vertical-align: middle;
}
viewer-input-method {
font-family: Arial, Helvetica, sans-serif;
}
.header-button {
background: none;
border: none;
@@ -81,15 +85,27 @@ import { ImeUiData } from "viewers/common/ime_ui_data";
overflow: auto;
}
.hierarchy-view {
.left-views {
font: inherit;
margin: 0px;
width: 50%;
height: 52.5rem;
height: 100%;
display: flex;
flex-direction: column;
overflow: auto;
}
.hierarchy-view, .ime-additional-properties-view {
font: inherit;
margin: 0px;
border-radius: 0;
border-top: 1px solid var(--default-border);
border-right: 1px solid var(--default-border);
border-left: 1px solid var(--default-border);
box-shadow: none !important;
}
.ime-additional-properties-view {
height: 404px;
}
.properties-view {
@@ -100,6 +116,7 @@ import { ImeUiData } from "viewers/common/ime_ui_data";
border-radius: 0;
border-top: 1px solid var(--default-border);
border-left: 1px solid var(--default-border);
box-shadow: none !important;
}
`,
]
@@ -110,4 +127,10 @@ export class ViewerInputMethodComponent {
@Input() active = false;
TRACE_INFO = TRACE_INFO;
TraceType = TraceType;
public hierarchyHeight() {
return {
height: `${this.inputData?.additionalProperties ? 404 : 840}px`
};
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TraceType } from "common/trace/trace_type";
import { HierarchyTreeBuilder } from "test/unit/hierarchy_tree_builder";
import { PresenterInputMethodClients } from "./presenter_input_method_clients";
import { executePresenterInputMethodTests } from "viewers/common/presenter_input_method_test_utils";
import { UnitTestUtils } from "test/unit/utils";
describe("PresenterInputMethodClients", () => {
async function getEntry() {
return await UnitTestUtils.getInputMethodClientsEntry();
}
describe("PresenterInputMethod tests:", () => {
const selectedTree = new HierarchyTreeBuilder().setName("8m23s29ms - InputMethodManager#showSoftInput")
.setFilteredView(true).setKind("InputMethodClient entry").setId("entry").setStableId("entry")
.setChildren([
new HierarchyTreeBuilder().setKind("Client").setId("client").setFilteredView(true)
.setStableId("client").build()
]).build();
executePresenterInputMethodTests(
getEntry,
selectedTree,
"elapsed",
[3, 1],
PresenterInputMethodClients,
TraceType.INPUT_METHOD_CLIENTS,
);
});
});

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { PresenterInputMethod } from "viewers/common/presenter_input_method";
export class PresenterInputMethodClients extends PresenterInputMethod {
protected updateHierarchyTableProperties() {
return {...new ImClientsTableProperties(
this.entry?.obj?.client?.inputMethodManager?.curId,
this.entry?.obj?.client?.editorInfo?.packageName,
)};
}
}
class ImClientsTableProperties {
constructor(
public inputMethodId: string | undefined,
public packageName: string | undefined,
) {}
}

View File

@@ -14,18 +14,23 @@
* limitations under the License.
*/
import {TraceType} from "common/trace/trace_type";
import { PresenterInputMethodClients } from "./presenter_input_method_clients";
import { ViewerInputMethod } from "viewers/common/viewer_input_method";
class ViewerInputMethodClients extends ViewerInputMethod {
public override getTitle(): string {
public getTitle(): string {
return "Input Method Clients";
}
public override getDependencies(): TraceType[] {
public getDependencies(): TraceType[] {
return ViewerInputMethodClients.DEPENDENCIES;
}
public static override readonly DEPENDENCIES: TraceType[] = [TraceType.INPUT_METHOD_CLIENTS];
protected initialisePresenter() {
return new PresenterInputMethodClients(this.imeUiCallback, this.getDependencies());
}
public static readonly DEPENDENCIES: TraceType[] = [TraceType.INPUT_METHOD_CLIENTS];
}
export {ViewerInputMethodClients};

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TraceType } from "common/trace/trace_type";
import { HierarchyTreeBuilder } from "test/unit/hierarchy_tree_builder";
import { PresenterInputMethodManagerService } from "./presenter_input_method_manager_service";
import { executePresenterInputMethodTests } from "viewers/common/presenter_input_method_test_utils";
import { UnitTestUtils } from "test/unit/utils";
describe("PresenterInputMethodManagerService", () => {
async function getEntry() {
return await UnitTestUtils.getInputMethodManagerServiceEntry();
}
describe("PresenterInputMethod tests:", () => {
const selectedTree = new HierarchyTreeBuilder()
.setFilteredView(true).setKind("InputMethodManagerService").setId("managerservice")
.setStableId("managerservice").build();
executePresenterInputMethodTests(
getEntry,
selectedTree,
"cur",
[13, 8],
PresenterInputMethodManagerService,
TraceType.INPUT_METHOD_MANAGER_SERVICE,
);
});
});

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { PresenterInputMethod } from "viewers/common/presenter_input_method";
import { ImeAdditionalProperties } from "viewers/common/ime_additional_properties";
import { TraceTreeNode } from "common/trace/trace_tree_node";
import { ImeUtils } from "viewers/common/ime_utils";
export class PresenterInputMethodManagerService extends PresenterInputMethod {
protected updateHierarchyTableProperties() {
return {...new ImManagerServiceTableProperties(
this.entry?.obj?.inputMethodManagerService?.curMethodId,
this.entry?.obj?.inputMethodManagerService?.curFocusedWindowName,
this.entry?.obj?.inputMethodManagerService?.lastImeTargetWindowName,
this.entry?.obj?.inputMethodManagerService?.inputShown ?? false,
)};
}
protected override getAdditionalProperties(wmEntry: TraceTreeNode | undefined, sfEntry: TraceTreeNode | undefined) {
return new ImeAdditionalProperties(
wmEntry ? ImeUtils.processWindowManagerTraceEntry(wmEntry) : undefined,
undefined,
);
}
}
class ImManagerServiceTableProperties {
constructor(
public inputMethodId: string | undefined,
public curFocusedWindow: string | undefined,
public lastImeTargetWindow: string | undefined,
public inputShown: boolean,
) {}
}

View File

@@ -14,18 +14,23 @@
* limitations under the License.
*/
import {TraceType} from "common/trace/trace_type";
import { PresenterInputMethodManagerService } from "./presenter_input_method_manager_service";
import { ViewerInputMethod } from "viewers/common/viewer_input_method";
class ViewerInputMethodManagerService extends ViewerInputMethod {
public override getTitle(): string {
public getTitle(): string {
return "Input Method Manager Service";
}
public override getDependencies(): TraceType[] {
public getDependencies(): TraceType[] {
return ViewerInputMethodManagerService.DEPENDENCIES;
}
public static override readonly DEPENDENCIES: TraceType[] = [TraceType.INPUT_METHOD_MANAGER_SERVICE];
protected initialisePresenter() {
return new PresenterInputMethodManagerService(this.imeUiCallback, this.getDependencies());
}
public static readonly DEPENDENCIES: TraceType[] = [TraceType.INPUT_METHOD_MANAGER_SERVICE];
}
export {ViewerInputMethodManagerService};

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TraceType } from "common/trace/trace_type";
import { HierarchyTreeBuilder } from "test/unit/hierarchy_tree_builder";
import { PresenterInputMethodService } from "./presenter_input_method_service";
import { executePresenterInputMethodTests } from "viewers/common/presenter_input_method_test_utils";
import { UnitTestUtils } from "test/unit/utils";
describe("PresenterInputMethodService", () => {
async function getEntry() {
return await UnitTestUtils.getInputMethodServiceEntry();
}
describe("PresenterInputMethod tests:", () => {
const selectedTree = new HierarchyTreeBuilder().setKind("InputMethodService")
.setId("service").setFilteredView(true).setStableId("service").build();
executePresenterInputMethodTests(
getEntry,
selectedTree,
"visib",
[13, 3],
PresenterInputMethodService,
TraceType.INPUT_METHOD_SERVICE,
);
});
});

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { PresenterInputMethod } from "viewers/common/presenter_input_method";
export class PresenterInputMethodService extends PresenterInputMethod {
protected updateHierarchyTableProperties() {
return {...new ImServiceTableProperties(
this.entry?.obj?.inputMethodService?.windowVisible ?? false,
this.entry?.obj?.inputMethodService?.decorViewVisible ?? false,
this.entry?.obj?.inputMethodService?.inputEditorInfo?.packageName,
)};
}
}
class ImServiceTableProperties {
constructor(
public windowVisible: boolean,
public decorViewVisible: boolean,
public packageName: string | undefined
) {}
}

View File

@@ -14,18 +14,23 @@
* limitations under the License.
*/
import {TraceType} from "common/trace/trace_type";
import { PresenterInputMethodService } from "./presenter_input_method_service";
import { ViewerInputMethod } from "viewers/common/viewer_input_method";
class ViewerInputMethodService extends ViewerInputMethod {
public override getTitle(): string {
public getTitle(): string {
return "Input Method Service";
}
public override getDependencies(): TraceType[] {
public getDependencies(): TraceType[] {
return ViewerInputMethodService.DEPENDENCIES;
}
public static override readonly DEPENDENCIES: TraceType[] = [TraceType.INPUT_METHOD_SERVICE];
protected initialisePresenter() {
return new PresenterInputMethodService(this.imeUiCallback, this.getDependencies());
}
public static readonly DEPENDENCIES: TraceType[] = [TraceType.INPUT_METHOD_SERVICE];
}
export {ViewerInputMethodService};

View File

@@ -213,7 +213,7 @@ export class Presenter {
private getTreeWithTransformedProperties(selectedTree: HierarchyTreeNode): PropertiesTreeNode {
const transformer = new TreeTransformer(selectedTree, this.propertiesFilter)
.showOnlyProtoDump()
.setOnlyProtoDump(true)
.setIsShowDefaults(this.propertiesUserOptions["showDefaults"]?.enabled)
.setIsShowDiff(this.propertiesUserOptions["showDiff"]?.enabled)
.setTransformerOptions({skip: selectedTree.skip})

View File

@@ -76,10 +76,6 @@ import { PersistentStore } from "common/persistent_store";
vertical-align: middle;
}
viewer-surface-flinger {
font-family: Arial, Helvetica, sans-serif;
}
.header-button {
background: none;
border: none;

View File

@@ -215,7 +215,7 @@ export class Presenter {
return {};
}
const transformer = new TreeTransformer(selectedTree, this.propertiesFilter)
.showOnlyProtoDump()
.setOnlyProtoDump(true)
.setIsShowDefaults(this.propertiesUserOptions["showDefaults"]?.enabled)
.setIsShowDiff(this.propertiesUserOptions["showDiff"]?.enabled)
.setTransformerOptions({skip: selectedTree.skip})

View File

@@ -73,10 +73,6 @@ import { PersistentStore } from "common/persistent_store";
vertical-align: middle;
}
viewer-window-manager {
font-family: Arial, Helvetica, sans-serif;
}
.header-button {
background: none;
border: none;