IME Viewers.
IM Clients, Service and Manager Service viewers - not yet including new WM/SF combined properties view in hierarchy. Bug: b/238088679 Test: npm run test:all Change-Id: I4bd05ca5dff14abbf6d3a89b0e07730233edf1b0
This commit is contained in:
@@ -41,12 +41,14 @@ import { TreeNodePropertiesDataViewComponent } from "viewers/components/tree_nod
|
||||
import { PropertyGroupsComponent } from "viewers/components/property_groups.component";
|
||||
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";
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
ViewerWindowManagerComponent,
|
||||
ViewerSurfaceFlingerComponent,
|
||||
ViewerInputMethodComponent,
|
||||
CollectTracesComponent,
|
||||
UploadTracesComponent,
|
||||
AdbProxyComponent,
|
||||
|
||||
@@ -22,6 +22,7 @@ import { ViewerWindowManagerComponent } from "viewers/viewer_window_manager/view
|
||||
import { ViewerSurfaceFlingerComponent } from "viewers/viewer_surface_flinger/viewer_surface_flinger.component";
|
||||
import { Timestamp } from "common/trace/timestamp";
|
||||
import { MatSliderChange } from "@angular/material/slider";
|
||||
import { ViewerInputMethodComponent } from "viewers/components/viewer_input_method.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-root",
|
||||
@@ -128,6 +129,10 @@ export class AppComponent {
|
||||
customElements.define("viewer-surface-flinger",
|
||||
createCustomElement(ViewerSurfaceFlingerComponent, {injector}));
|
||||
}
|
||||
if (!customElements.get("viewer-input-method")) {
|
||||
customElements.define("viewer-input-method",
|
||||
createCustomElement(ViewerInputMethodComponent, {injector}));
|
||||
}
|
||||
}
|
||||
|
||||
public updateCurrentTimestamp(event: MatSliderChange) {
|
||||
|
||||
@@ -17,6 +17,9 @@ import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {Parser} from "./parser";
|
||||
import {InputMethodClientsTraceFileProto} from "./proto_types";
|
||||
import { TraceTreeNode } from "common/trace/trace_tree_node";
|
||||
import { StringUtils } from "common/utils/string_utils";
|
||||
import { ImeUtils } from "viewers/common/ime_utils";
|
||||
|
||||
class ParserInputMethodClients extends Parser {
|
||||
constructor(trace: File) {
|
||||
@@ -54,8 +57,25 @@ class ParserInputMethodClients extends Parser {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
override processDecodedEntry(entryProto: any): any {
|
||||
return entryProto;
|
||||
override processDecodedEntry(entryProto: TraceTreeNode): TraceTreeNode {
|
||||
return {
|
||||
name: StringUtils.nanosecondsToHuman(entryProto.elapsedRealtimeNanos ?? 0) + " - " + entryProto.where,
|
||||
kind: "InputMethodClient entry",
|
||||
children: [
|
||||
{
|
||||
obj: ImeUtils.transformInputConnectionCall(entryProto.client),
|
||||
kind: "Client",
|
||||
name: entryProto.client?.viewRootImpl?.view ?? "",
|
||||
children: [],
|
||||
stableId: "client",
|
||||
id: "client",
|
||||
}
|
||||
],
|
||||
obj: entryProto,
|
||||
stableId: "entry",
|
||||
id: "entry",
|
||||
elapsedRealtimeNanos: entryProto.elapsedRealtimeNanos,
|
||||
};
|
||||
}
|
||||
|
||||
private realToElapsedTimeOffsetNs: undefined|bigint;
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import { TraceTreeNode } from "common/trace/trace_tree_node";
|
||||
import { StringUtils } from "common/utils/string_utils";
|
||||
import {Parser} from "./parser";
|
||||
import {InputMethodManagerServiceTraceFileProto} from "./proto_types";
|
||||
|
||||
@@ -53,8 +55,25 @@ class ParserInputMethodManagerService extends Parser {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected override processDecodedEntry(entryProto: any): any {
|
||||
return entryProto;
|
||||
protected override processDecodedEntry(entryProto: TraceTreeNode): TraceTreeNode {
|
||||
return {
|
||||
name: StringUtils.nanosecondsToHuman(entryProto.elapsedRealtimeNanos ?? 0) + " - " + entryProto.where,
|
||||
kind: "InputMethodManagerService entry",
|
||||
children: [
|
||||
{
|
||||
obj: entryProto.inputMethodManagerService,
|
||||
kind: "InputMethodManagerService",
|
||||
name: "",
|
||||
children: [],
|
||||
stableId: "managerservice",
|
||||
id: "managerservice",
|
||||
}
|
||||
],
|
||||
obj: entryProto,
|
||||
stableId: "entry",
|
||||
id: "entry",
|
||||
elapsedRealtimeNanos: entryProto.elapsedRealtimeNanos,
|
||||
};
|
||||
}
|
||||
|
||||
private realToElapsedTimeOffsetNs: undefined|bigint;
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {ParserFactory} from "./parser_factory";
|
||||
import {Parser} from "./parser";
|
||||
import {UnitTestUtils} from "test/unit/utils";
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import { TraceTreeNode } from "common/trace/trace_tree_node";
|
||||
import { StringUtils } from "common/utils/string_utils";
|
||||
import { ImeUtils } from "viewers/common/ime_utils";
|
||||
import {Parser} from "./parser";
|
||||
import {InputMethodServiceTraceFileProto} from "./proto_types";
|
||||
|
||||
@@ -53,8 +56,25 @@ class ParserInputMethodService extends Parser {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
override processDecodedEntry(entryProto: any): any {
|
||||
return entryProto;
|
||||
override processDecodedEntry(entryProto: TraceTreeNode): TraceTreeNode {
|
||||
return {
|
||||
name: StringUtils.nanosecondsToHuman(entryProto.elapsedRealtimeNanos ?? 0) + " - " + entryProto.where,
|
||||
kind: "InputMethodService entry",
|
||||
children: [
|
||||
{
|
||||
obj: ImeUtils.transformInputConnectionCall(entryProto.inputMethodService),
|
||||
kind: "InputMethodService",
|
||||
name: "",
|
||||
children: [],
|
||||
stableId: "service",
|
||||
id: "service",
|
||||
}
|
||||
],
|
||||
obj: entryProto,
|
||||
stableId: "entry",
|
||||
id: "entry",
|
||||
elapsedRealtimeNanos: entryProto.elapsedRealtimeNanos,
|
||||
};
|
||||
}
|
||||
|
||||
private realToElapsedTimeOffsetNs: undefined|bigint;
|
||||
|
||||
@@ -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 {browser, element, by} from "protractor";
|
||||
import {E2eTestUtils} from "./utils";
|
||||
|
||||
describe("Viewer InputMethodClients", () => {
|
||||
beforeAll(async () => {
|
||||
browser.manage().timeouts().implicitlyWait(1000);
|
||||
browser.get("file://" + E2eTestUtils.getProductionIndexHtmlPath());
|
||||
}),
|
||||
|
||||
it("processes trace and renders view", async () => {
|
||||
const inputFile = element(by.css("input[type=\"file\"]"));
|
||||
await inputFile.sendKeys(E2eTestUtils.getFixturePath("traces/elapsed_and_real_timestamp/InputMethodClients.pb"));
|
||||
|
||||
const loadData = element(by.css(".load-btn"));
|
||||
await loadData.click();
|
||||
|
||||
const viewerPresent = await element(by.css("viewer-input-method")).isPresent();
|
||||
expect(viewerPresent).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -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 {browser, element, by} from "protractor";
|
||||
import {E2eTestUtils} from "./utils";
|
||||
|
||||
describe("Viewer InputMethodManagerService", () => {
|
||||
beforeAll(async () => {
|
||||
browser.manage().timeouts().implicitlyWait(1000);
|
||||
browser.get("file://" + E2eTestUtils.getProductionIndexHtmlPath());
|
||||
}),
|
||||
|
||||
it("processes trace and renders view", async () => {
|
||||
const inputFile = element(by.css("input[type=\"file\"]"));
|
||||
await inputFile.sendKeys(E2eTestUtils.getFixturePath("traces/elapsed_and_real_timestamp/InputMethodManagerService.pb"));
|
||||
|
||||
const loadData = element(by.css(".load-btn"));
|
||||
await loadData.click();
|
||||
|
||||
const viewerPresent = await element(by.css("viewer-input-method")).isPresent();
|
||||
expect(viewerPresent).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -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 {browser, element, by} from "protractor";
|
||||
import {E2eTestUtils} from "./utils";
|
||||
|
||||
describe("Viewer InputMethodService", () => {
|
||||
beforeAll(async () => {
|
||||
browser.manage().timeouts().implicitlyWait(1000);
|
||||
browser.get("file://" + E2eTestUtils.getProductionIndexHtmlPath());
|
||||
}),
|
||||
|
||||
it("processes trace and renders view", async () => {
|
||||
const inputFile = element(by.css("input[type=\"file\"]"));
|
||||
await inputFile.sendKeys(E2eTestUtils.getFixturePath("traces/elapsed_and_real_timestamp/InputMethodService.pb"));
|
||||
|
||||
const loadData = element(by.css(".load-btn"));
|
||||
await loadData.click();
|
||||
|
||||
const viewerPresent = await element(by.css("viewer-input-method")).isPresent();
|
||||
expect(viewerPresent).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {browser, element, by, ElementFinder} from "protractor";
|
||||
import {browser, element, by} from "protractor";
|
||||
import {E2eTestUtils} from "./utils";
|
||||
|
||||
describe("Viewer SurfaceFlinger", () => {
|
||||
|
||||
@@ -41,7 +41,7 @@ class HierarchyTreeBuilder {
|
||||
diffType?: string;
|
||||
skip?: any;
|
||||
|
||||
setId(id: number) {
|
||||
setId(id: string | number) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,18 @@ class UnitTestUtils extends CommonTestUtils {
|
||||
return await this.getTraceEntry("traces/elapsed_timestamp/SurfaceFlinger.pb");
|
||||
}
|
||||
|
||||
static async getInputMethodClientsEntry(): Promise<LayerTraceEntry> {
|
||||
return await this.getTraceEntry("traces/elapsed_timestamp/InputMethodClients.pb");
|
||||
}
|
||||
|
||||
static async getInputMethodServiceEntry(): Promise<LayerTraceEntry> {
|
||||
return await this.getTraceEntry("traces/elapsed_timestamp/InputMethodService.pb");
|
||||
}
|
||||
|
||||
static async getInputMethodManagerServiceEntry(): Promise<LayerTraceEntry> {
|
||||
return await this.getTraceEntry("traces/elapsed_timestamp/InputMethodManagerService.pb");
|
||||
}
|
||||
|
||||
private static async getTraceEntry(filename: string) {
|
||||
const parser = await this.getParser(filename);
|
||||
const timestamp = parser.getTimestamps(TimestampType.ELAPSED)![0];
|
||||
|
||||
182
tools/winscope-ng/src/viewers/common/ime_presenter.ts
Normal file
182
tools/winscope-ng/src/viewers/common/ime_presenter.ts
Normal file
@@ -0,0 +1,182 @@
|
||||
|
||||
/*
|
||||
* 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 { ImeUiData } from "./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";
|
||||
|
||||
export type NotifyImeViewCallbackType = (uiData: ImeUiData) => void;
|
||||
|
||||
export class ImePresenter {
|
||||
constructor(
|
||||
notifyViewCallback: NotifyImeViewCallbackType,
|
||||
dependencies: Array<TraceType>
|
||||
) {
|
||||
this.notifyViewCallback = notifyViewCallback;
|
||||
this.dependencies = dependencies;
|
||||
this.uiData = new ImeUiData(dependencies);
|
||||
this.notifyViewCallback(this.uiData);
|
||||
}
|
||||
|
||||
public updatePinnedItems(pinnedItem: HierarchyTreeNode) {
|
||||
const pinnedId = `${pinnedItem.id}`;
|
||||
if (this.pinnedItems.map(item => `${item.id}`).includes(pinnedId)) {
|
||||
this.pinnedItems = this.pinnedItems.filter(pinned => `${pinned.id}` != pinnedId);
|
||||
} else {
|
||||
this.pinnedItems.push(pinnedItem);
|
||||
}
|
||||
this.updatePinnedIds(pinnedId);
|
||||
this.uiData.pinnedItems = this.pinnedItems;
|
||||
this.notifyViewCallback(this.uiData);
|
||||
}
|
||||
|
||||
public updateHighlightedItems(id: string) {
|
||||
if (this.highlightedItems.includes(id)) {
|
||||
this.highlightedItems = this.highlightedItems.filter(hl => hl != id);
|
||||
} else {
|
||||
this.highlightedItems = []; //if multi-select surfaces implemented, remove this line
|
||||
this.highlightedItems.push(id);
|
||||
}
|
||||
this.uiData.highlightedItems = this.highlightedItems;
|
||||
this.notifyViewCallback(this.uiData);
|
||||
}
|
||||
|
||||
public updateHierarchyTree(userOptions: UserOptions) {
|
||||
this.hierarchyUserOptions = userOptions;
|
||||
this.uiData.hierarchyUserOptions = this.hierarchyUserOptions;
|
||||
this.uiData.tree = this.generateTree();
|
||||
this.notifyViewCallback(this.uiData);
|
||||
}
|
||||
|
||||
public filterHierarchyTree(filterString: string) {
|
||||
this.hierarchyFilter = TreeUtils.makeNodeFilter(filterString);
|
||||
this.uiData.tree = this.generateTree();
|
||||
this.notifyViewCallback(this.uiData);
|
||||
}
|
||||
|
||||
public updatePropertiesTree(userOptions: UserOptions) {
|
||||
this.propertiesUserOptions = userOptions;
|
||||
this.uiData.propertiesUserOptions = this.propertiesUserOptions;
|
||||
this.updateSelectedTreeUiData();
|
||||
}
|
||||
|
||||
public filterPropertiesTree(filterString: string) {
|
||||
this.propertiesFilter = TreeUtils.makeNodeFilter(filterString);
|
||||
this.updateSelectedTreeUiData();
|
||||
}
|
||||
|
||||
public newPropertiesTree(selectedItem: HierarchyTreeNode) {
|
||||
this.selectedHierarchyTree = selectedItem;
|
||||
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();
|
||||
}
|
||||
}
|
||||
this.notifyViewCallback(this.uiData);
|
||||
}
|
||||
|
||||
private updateSelectedTreeUiData() {
|
||||
if (this.selectedHierarchyTree) {
|
||||
this.uiData.propertiesTree = this.getTreeWithTransformedProperties(this.selectedHierarchyTree);
|
||||
}
|
||||
this.notifyViewCallback(this.uiData);
|
||||
}
|
||||
|
||||
private generateTree() {
|
||||
if (!this.entry) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const generator = new TreeGenerator(this.entry, this.hierarchyFilter, this.pinnedIds)
|
||||
.setIsOnlyVisibleView(this.hierarchyUserOptions["onlyVisible"]?.enabled)
|
||||
.setIsSimplifyNames(this.hierarchyUserOptions["simplifyNames"]?.enabled)
|
||||
.setIsFlatView(this.hierarchyUserOptions["flat"]?.enabled)
|
||||
.withUniqueNodeId();
|
||||
const tree: HierarchyTreeNode | null = generator.generateTree();
|
||||
this.pinnedItems = generator.getPinnedItems();
|
||||
this.uiData.pinnedItems = this.pinnedItems;
|
||||
return tree;
|
||||
}
|
||||
|
||||
private updatePinnedIds(newId: string) {
|
||||
if (this.pinnedIds.includes(newId)) {
|
||||
this.pinnedIds = this.pinnedIds.filter(pinned => pinned != newId);
|
||||
} else {
|
||||
this.pinnedIds.push(newId);
|
||||
}
|
||||
}
|
||||
|
||||
private getTreeWithTransformedProperties(selectedTree: HierarchyTreeNode): PropertiesTreeNode {
|
||||
const transformer = new TreeTransformer(selectedTree, this.propertiesFilter)
|
||||
.setIsShowDefaults(this.propertiesUserOptions["showDefaults"]?.enabled)
|
||||
.setTransformerOptions({skip: selectedTree.skip})
|
||||
.setProperties(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 = {
|
||||
simplifyNames: {
|
||||
name: "Simplify names",
|
||||
enabled: true
|
||||
},
|
||||
onlyVisible: {
|
||||
name: "Only visible",
|
||||
enabled: false
|
||||
},
|
||||
flat: {
|
||||
name: "Flat",
|
||||
enabled: false
|
||||
}
|
||||
};
|
||||
|
||||
private propertiesUserOptions: UserOptions = {
|
||||
showDefaults: {
|
||||
name: "Show defaults",
|
||||
enabled: true,
|
||||
tooltip: `
|
||||
If checked, shows the value of all properties.
|
||||
Otherwise, hides all properties whose value is
|
||||
the default for its data type.
|
||||
`
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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);
|
||||
});
|
||||
});
|
||||
32
tools/winscope-ng/src/viewers/common/ime_ui_data.ts
Normal file
32
tools/winscope-ng/src/viewers/common/ime_ui_data.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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 { HierarchyTreeNode, PropertiesTreeNode } from "./ui_tree_utils";
|
||||
import { UserOptions } from "viewers/common/user_options";
|
||||
import { TraceType } from "common/trace/trace_type";
|
||||
|
||||
export class ImeUiData {
|
||||
dependencies: Array<TraceType>;
|
||||
highlightedItems: Array<string> = [];
|
||||
pinnedItems: Array<HierarchyTreeNode> = [];
|
||||
hierarchyUserOptions: UserOptions = {};
|
||||
propertiesUserOptions: UserOptions = {};
|
||||
tree: HierarchyTreeNode | null = null;
|
||||
propertiesTree: PropertiesTreeNode | null = null;
|
||||
|
||||
constructor(dependencies?: Array<TraceType>) {
|
||||
this.dependencies = dependencies ?? [];
|
||||
}
|
||||
}
|
||||
@@ -99,6 +99,17 @@ class ImeUtils {
|
||||
);
|
||||
}
|
||||
|
||||
public static transformInputConnectionCall(entry: any) {
|
||||
const obj = Object.assign({}, entry);
|
||||
if (obj.inputConnectionCall) {
|
||||
Object.getOwnPropertyNames(obj.inputConnectionCall).forEach(name => {
|
||||
const value = Object.getOwnPropertyDescriptor(obj.inputConnectionCall, name);
|
||||
if (!value?.value) delete obj.inputConnectionCall[name];
|
||||
});
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
private static findAncestorTaskLayerOfImeLayer(entry: LayerTraceEntry, isTargetImeLayer: FilterType): Layer {
|
||||
const imeLayer = TreeUtils.findDescendantNode(entry, isTargetImeLayer);
|
||||
if (!imeLayer) {
|
||||
|
||||
@@ -234,7 +234,7 @@ export class TreeGenerator {
|
||||
}
|
||||
|
||||
if (this.isOnlyVisibleView) {
|
||||
newTree.showInOnlyVisibleView = newTree.isVisible;
|
||||
newTree.showInOnlyVisibleView = UiTreeUtils.isParentNode(tree.kind) ? true : newTree.isVisible;
|
||||
}
|
||||
|
||||
newTree.children = [];
|
||||
|
||||
@@ -96,5 +96,11 @@ export class UiTreeUtils
|
||||
return this.PARENT_NODE_KINDS.includes(kind);
|
||||
}
|
||||
|
||||
private static readonly PARENT_NODE_KINDS = ["entry", "WindowManagerState"];
|
||||
private static readonly PARENT_NODE_KINDS = [
|
||||
"entry",
|
||||
"WindowManagerState",
|
||||
"InputMethodClient entry",
|
||||
"InputMethodService entry",
|
||||
"InputMethodManagerService entry"
|
||||
];
|
||||
}
|
||||
|
||||
62
tools/winscope-ng/src/viewers/common/viewer_input_method.ts
Normal file
62
tools/winscope-ng/src/viewers/common/viewer_input_method.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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 {Viewer} from "viewers/viewer";
|
||||
import {ViewerEvents} from "viewers/common/viewer_events";
|
||||
import { ImePresenter } from "viewers/common/ime_presenter";
|
||||
import { ImeUiData } from "viewers/common/ime_ui_data";
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
public notifyCurrentTraceEntries(entries: Map<TraceType, any>): void {
|
||||
this.presenter.notifyCurrentTraceEntries(entries);
|
||||
}
|
||||
|
||||
public getView(): HTMLElement {
|
||||
return this.view;
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return "";
|
||||
}
|
||||
|
||||
public getDependencies(): TraceType[] {
|
||||
return ViewerInputMethod.DEPENDENCIES;
|
||||
}
|
||||
|
||||
public static readonly DEPENDENCIES: TraceType[] = [];
|
||||
private view: HTMLElement;
|
||||
private presenter: ImePresenter;
|
||||
}
|
||||
|
||||
export {ViewerInputMethod};
|
||||
@@ -51,7 +51,7 @@ import { PropertiesTreeNode, Terminal} from "viewers/common/ui_tree_utils";
|
||||
</div>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="properties-content" [style]="maxPropertiesHeight()">
|
||||
<span *ngIf="objectKeys(propertiesTree).length > 0" class="properties-title"> Properties - Proto Dump </span>
|
||||
<span *ngIf="objectKeys(propertiesTree).length > 0 && isProtoDump" class="properties-title"> Properties - Proto Dump </span>
|
||||
<div class="tree-wrapper">
|
||||
<tree-view
|
||||
class="tree-view"
|
||||
@@ -138,6 +138,7 @@ export class PropertiesComponent {
|
||||
@Input() propertiesTree: PropertiesTreeNode = {};
|
||||
@Input() selectedFlickerItem: TraceTreeNode | null = null;
|
||||
@Input() propertyGroups = false;
|
||||
@Input() isProtoDump = false;
|
||||
|
||||
constructor(
|
||||
@Inject(ElementRef) private elementRef: ElementRef,
|
||||
|
||||
@@ -44,7 +44,7 @@ export class TreeNodeDataViewComponent {
|
||||
}
|
||||
|
||||
public itemShortName() {
|
||||
return (this.item instanceof HierarchyTreeNode)? this.item.shortName : "";
|
||||
return (this.item instanceof HierarchyTreeNode && this.item.shortName) ? this.item.shortName : this.item.name;
|
||||
}
|
||||
|
||||
public itemTooltip() {
|
||||
@@ -55,7 +55,7 @@ export class TreeNodeDataViewComponent {
|
||||
}
|
||||
|
||||
public showShortName() {
|
||||
return (this.item instanceof HierarchyTreeNode) && this.item.simplifyNames && this.item.shortName !== this.item.name;
|
||||
return (this.item instanceof HierarchyTreeNode) && this.item.simplifyNames && this.item.shortName && this.item.shortName !== this.item.name;
|
||||
}
|
||||
|
||||
public chipClass(chip: Chip) {
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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 { ViewerInputMethodComponent } from "./viewer_input_method.component";
|
||||
import { HierarchyComponent } from "viewers/components/hierarchy.component";
|
||||
import { PropertiesComponent } from "viewers/components/properties.component";
|
||||
import { MatIconModule } from "@angular/material/icon";
|
||||
import { MatCardModule } from "@angular/material/card";
|
||||
import { ComponentFixtureAutoDetect } from "@angular/core/testing";
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from "@angular/core";
|
||||
|
||||
describe("ViewerInputMethodComponent", () => {
|
||||
let fixture: ComponentFixture<ViewerInputMethodComponent>;
|
||||
let component: ViewerInputMethodComponent;
|
||||
let htmlElement: HTMLElement;
|
||||
|
||||
beforeAll(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{ provide: ComponentFixtureAutoDetect, useValue: true }
|
||||
],
|
||||
imports: [
|
||||
MatIconModule,
|
||||
MatCardModule
|
||||
],
|
||||
declarations: [
|
||||
ViewerInputMethodComponent,
|
||||
HierarchyComponent,
|
||||
PropertiesComponent,
|
||||
],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ViewerInputMethodComponent);
|
||||
component = fixture.componentInstance;
|
||||
htmlElement = fixture.nativeElement;
|
||||
});
|
||||
|
||||
it("can be created", () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it("creates hierarchy view", () => {
|
||||
const hierarchyView = htmlElement.querySelector(".hierarchy-view");
|
||||
expect(hierarchyView).toBeTruthy();
|
||||
});
|
||||
|
||||
it("creates properties view", () => {
|
||||
const propertiesView = htmlElement.querySelector(".properties-view");
|
||||
expect(propertiesView).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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 { TRACE_INFO } from "app/trace_info";
|
||||
import { TraceType } from "common/trace/trace_type";
|
||||
import { PersistentStore } from "common/persistent_store";
|
||||
import { ImeUiData } from "viewers/common/ime_ui_data";
|
||||
|
||||
@Component({
|
||||
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>
|
||||
<mat-card class="properties-view">
|
||||
<properties-view
|
||||
[userOptions]="inputData?.propertiesUserOptions ?? {}"
|
||||
[propertiesTree]="inputData?.propertiesTree ?? {}"
|
||||
></properties-view>
|
||||
</mat-card>
|
||||
</div>
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
@import 'https://fonts.googleapis.com/icon?family=Material+Icons';
|
||||
:root {
|
||||
--default-border: #DADCE0;
|
||||
}
|
||||
|
||||
mat-icon {
|
||||
margin: 5px
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
background: none;
|
||||
border: none;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
viewer-input-method {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.header-button {
|
||||
background: none;
|
||||
border: none;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.card-grid {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.hierarchy-view {
|
||||
font: inherit;
|
||||
margin: 0px;
|
||||
width: 50%;
|
||||
height: 52.5rem;
|
||||
border-radius: 0;
|
||||
border-top: 1px solid var(--default-border);
|
||||
border-right: 1px solid var(--default-border);
|
||||
border-left: 1px solid var(--default-border);
|
||||
}
|
||||
|
||||
.properties-view {
|
||||
font: inherit;
|
||||
margin: 0px;
|
||||
width: 50%;
|
||||
height: 52.5rem;
|
||||
border-radius: 0;
|
||||
border-top: 1px solid var(--default-border);
|
||||
border-left: 1px solid var(--default-border);
|
||||
}
|
||||
`,
|
||||
]
|
||||
})
|
||||
export class ViewerInputMethodComponent {
|
||||
@Input() inputData: ImeUiData | null = null;
|
||||
@Input() store: PersistentStore = new PersistentStore();
|
||||
@Input() active = false;
|
||||
TRACE_INFO = TRACE_INFO;
|
||||
TraceType = TraceType;
|
||||
}
|
||||
@@ -17,11 +17,17 @@ import { TraceType } from "common/trace/trace_type";
|
||||
import { Viewer } from "./viewer";
|
||||
import { ViewerWindowManager } from "./viewer_window_manager/viewer_window_manager";
|
||||
import { ViewerSurfaceFlinger } from "./viewer_surface_flinger/viewer_surface_flinger";
|
||||
import { ViewerInputMethodClients } from "./viewer_input_method_clients/viewer_input_method_clients";
|
||||
import { ViewerInputMethodService } from "./viewer_input_method_service/viewer_input_method_service";
|
||||
import { ViewerInputMethodManagerService } from "./viewer_input_method_manager_service/viewer_input_method_manager_service";
|
||||
|
||||
class ViewerFactory {
|
||||
static readonly VIEWERS = [
|
||||
ViewerWindowManager,
|
||||
ViewerSurfaceFlinger
|
||||
ViewerSurfaceFlinger,
|
||||
ViewerInputMethodClients,
|
||||
ViewerInputMethodService,
|
||||
ViewerInputMethodManagerService,
|
||||
];
|
||||
|
||||
public createViewers(activeTraceTypes: Set<TraceType>): Viewer[] {
|
||||
@@ -31,6 +37,7 @@ class ViewerFactory {
|
||||
const areViewerDepsSatisfied = Viewer.DEPENDENCIES.every((traceType: TraceType) =>
|
||||
activeTraceTypes.has(traceType)
|
||||
);
|
||||
|
||||
if (areViewerDepsSatisfied) {
|
||||
viewers.push(new Viewer());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 { ViewerInputMethod } from "viewers/common/viewer_input_method";
|
||||
|
||||
class ViewerInputMethodClients extends ViewerInputMethod {
|
||||
public override getTitle(): string {
|
||||
return "Input Method Clients";
|
||||
}
|
||||
|
||||
public override getDependencies(): TraceType[] {
|
||||
return ViewerInputMethodClients.DEPENDENCIES;
|
||||
}
|
||||
|
||||
public static override readonly DEPENDENCIES: TraceType[] = [TraceType.INPUT_METHOD_CLIENTS];
|
||||
}
|
||||
|
||||
export {ViewerInputMethodClients};
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 { ViewerInputMethod } from "viewers/common/viewer_input_method";
|
||||
|
||||
class ViewerInputMethodManagerService extends ViewerInputMethod {
|
||||
public override getTitle(): string {
|
||||
return "Input Method Manager Service";
|
||||
}
|
||||
|
||||
public override getDependencies(): TraceType[] {
|
||||
return ViewerInputMethodManagerService.DEPENDENCIES;
|
||||
}
|
||||
|
||||
public static override readonly DEPENDENCIES: TraceType[] = [TraceType.INPUT_METHOD_MANAGER_SERVICE];
|
||||
}
|
||||
|
||||
export {ViewerInputMethodManagerService};
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 { ViewerInputMethod } from "viewers/common/viewer_input_method";
|
||||
|
||||
class ViewerInputMethodService extends ViewerInputMethod {
|
||||
public override getTitle(): string {
|
||||
return "Input Method Service";
|
||||
}
|
||||
|
||||
public override getDependencies(): TraceType[] {
|
||||
return ViewerInputMethodService.DEPENDENCIES;
|
||||
}
|
||||
|
||||
public static override readonly DEPENDENCIES: TraceType[] = [TraceType.INPUT_METHOD_SERVICE];
|
||||
}
|
||||
|
||||
export {ViewerInputMethodService};
|
||||
@@ -29,7 +29,7 @@ type NotifyViewCallbackType = (uiData: UiData) => void;
|
||||
export class Presenter {
|
||||
constructor(notifyViewCallback: NotifyViewCallbackType) {
|
||||
this.notifyViewCallback = notifyViewCallback;
|
||||
this.uiData = new UiData();
|
||||
this.uiData = new UiData([TraceType.SURFACE_FLINGER]);
|
||||
this.notifyViewCallback(this.uiData);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import { TraceType } from "common/trace/trace_type";
|
||||
import { Rectangle } from "viewers/common/rectangle";
|
||||
|
||||
export class UiData {
|
||||
dependencies: Array<TraceType> = [TraceType.SURFACE_FLINGER];
|
||||
dependencies: Array<TraceType>;
|
||||
rects: Rectangle[] = [];
|
||||
displayIds: number[] = [];
|
||||
hasVirtualDisplays = false;
|
||||
@@ -31,4 +31,8 @@ export class UiData {
|
||||
tree: HierarchyTreeNode | null = null;
|
||||
propertiesTree: PropertiesTreeNode | null = null;
|
||||
selectedLayer: Layer = {};
|
||||
|
||||
constructor(dependencies?: Array<TraceType>) {
|
||||
this.dependencies = dependencies ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ import { PersistentStore } from "common/persistent_store";
|
||||
[propertiesTree]="inputData?.propertiesTree ?? {}"
|
||||
[selectedFlickerItem]="inputData?.selectedLayer ?? {}"
|
||||
[propertyGroups]="true"
|
||||
[isProtoDump]="true"
|
||||
></properties-view>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
||||
@@ -29,7 +29,7 @@ type NotifyViewCallbackType = (uiData: UiData) => void;
|
||||
export class Presenter {
|
||||
constructor(notifyViewCallback: NotifyViewCallbackType) {
|
||||
this.notifyViewCallback = notifyViewCallback;
|
||||
this.uiData = new UiData();
|
||||
this.uiData = new UiData([TraceType.WINDOW_MANAGER]);
|
||||
this.notifyViewCallback(this.uiData);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import { TraceType } from "common/trace/trace_type";
|
||||
import { Rectangle } from "viewers/common/rectangle";
|
||||
|
||||
export class UiData {
|
||||
dependencies: Array<TraceType> = [TraceType.WINDOW_MANAGER];
|
||||
dependencies: Array<TraceType>;
|
||||
rects: Rectangle[] = [];
|
||||
displayIds: number[] = [];
|
||||
highlightedItems: Array<string> = [];
|
||||
@@ -28,4 +28,8 @@ export class UiData {
|
||||
propertiesUserOptions: UserOptions = {};
|
||||
tree: HierarchyTreeNode | null = null;
|
||||
propertiesTree: PropertiesTreeNode | null = null;
|
||||
|
||||
constructor(dependencies?: Array<TraceType>) {
|
||||
this.dependencies = dependencies ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ import { PersistentStore } from "common/persistent_store";
|
||||
<properties-view
|
||||
[userOptions]="inputData?.propertiesUserOptions ?? {}"
|
||||
[propertiesTree]="inputData?.propertiesTree ?? {}"
|
||||
[isProtoDump]="true"
|
||||
></properties-view>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user