From 32c0ee72db4e1fb49d903eb582a1841a7cb85e39 Mon Sep 17 00:00:00 2001 From: Priyanka Patel Date: Tue, 20 Sep 2022 11:05:26 +0000 Subject: [PATCH] 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 --- tools/winscope-ng/src/app/app.module.ts | 2 + .../src/app/components/app.component.ts | 5 + .../parsers/parser_input_method_clients.ts | 24 ++- .../parser_input_method_manager_service.ts | 23 ++- .../parser_input_method_service.spec.ts | 1 - .../parsers/parser_input_method_service.ts | 24 ++- .../e2e/viewer_input_method_clients.spec.ts | 35 ++++ ...iewer_input_method_manager_service.spec.ts | 35 ++++ .../e2e/viewer_input_method_service.spec.ts | 35 ++++ .../test/e2e/viewer_surface_flinger.spec.ts | 2 +- .../src/test/unit/hierarchy_tree_builder.ts | 2 +- tools/winscope-ng/src/test/unit/utils.ts | 12 ++ .../src/viewers/common/ime_presenter.ts | 182 ++++++++++++++++++ .../common/ime_presenter_clients.spec.ts | 154 +++++++++++++++ .../ime_presenter_manager_service.spec.ts | 150 +++++++++++++++ .../common/ime_presenter_service.spec.ts | 149 ++++++++++++++ .../src/viewers/common/ime_ui_data.ts | 32 +++ .../src/viewers/common/ime_utils.ts | 11 ++ .../src/viewers/common/tree_generator.ts | 2 +- .../src/viewers/common/ui_tree_utils.ts | 8 +- .../src/viewers/common/viewer_input_method.ts | 62 ++++++ .../components/properties.component.ts | 3 +- .../tree_node_data_view.component.ts | 4 +- .../viewer_input_method.component.spec.ts | 67 +++++++ .../viewer_input_method.component.ts | 113 +++++++++++ .../winscope-ng/src/viewers/viewer_factory.ts | 9 +- .../viewer_input_method_clients.ts | 31 +++ .../viewer_input_method_manager_service.ts | 31 +++ .../viewer_input_method_service.ts | 31 +++ .../viewer_surface_flinger/presenter.ts | 2 +- .../viewers/viewer_surface_flinger/ui_data.ts | 6 +- .../viewer_surface_flinger.component.ts | 1 + .../viewer_window_manager/presenter.ts | 2 +- .../viewers/viewer_window_manager/ui_data.ts | 6 +- .../viewer_window_manager.component.ts | 1 + 35 files changed, 1238 insertions(+), 19 deletions(-) create mode 100644 tools/winscope-ng/src/test/e2e/viewer_input_method_clients.spec.ts create mode 100644 tools/winscope-ng/src/test/e2e/viewer_input_method_manager_service.spec.ts create mode 100644 tools/winscope-ng/src/test/e2e/viewer_input_method_service.spec.ts create mode 100644 tools/winscope-ng/src/viewers/common/ime_presenter.ts create mode 100644 tools/winscope-ng/src/viewers/common/ime_presenter_clients.spec.ts create mode 100644 tools/winscope-ng/src/viewers/common/ime_presenter_manager_service.spec.ts create mode 100644 tools/winscope-ng/src/viewers/common/ime_presenter_service.spec.ts create mode 100644 tools/winscope-ng/src/viewers/common/ime_ui_data.ts create mode 100644 tools/winscope-ng/src/viewers/common/viewer_input_method.ts create mode 100644 tools/winscope-ng/src/viewers/components/viewer_input_method.component.spec.ts create mode 100644 tools/winscope-ng/src/viewers/components/viewer_input_method.component.ts create mode 100644 tools/winscope-ng/src/viewers/viewer_input_method_clients/viewer_input_method_clients.ts create mode 100644 tools/winscope-ng/src/viewers/viewer_input_method_manager_service/viewer_input_method_manager_service.ts create mode 100644 tools/winscope-ng/src/viewers/viewer_input_method_service/viewer_input_method_service.ts diff --git a/tools/winscope-ng/src/app/app.module.ts b/tools/winscope-ng/src/app/app.module.ts index 64326cd32..5aa92634b 100644 --- a/tools/winscope-ng/src/app/app.module.ts +++ b/tools/winscope-ng/src/app/app.module.ts @@ -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, diff --git a/tools/winscope-ng/src/app/components/app.component.ts b/tools/winscope-ng/src/app/components/app.component.ts index 7d8a41a33..65d006522 100644 --- a/tools/winscope-ng/src/app/components/app.component.ts +++ b/tools/winscope-ng/src/app/components/app.component.ts @@ -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) { diff --git a/tools/winscope-ng/src/parsers/parser_input_method_clients.ts b/tools/winscope-ng/src/parsers/parser_input_method_clients.ts index c0bc824fe..9c4a2ba08 100644 --- a/tools/winscope-ng/src/parsers/parser_input_method_clients.ts +++ b/tools/winscope-ng/src/parsers/parser_input_method_clients.ts @@ -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; diff --git a/tools/winscope-ng/src/parsers/parser_input_method_manager_service.ts b/tools/winscope-ng/src/parsers/parser_input_method_manager_service.ts index 5ddd1fee9..88f03c8e7 100644 --- a/tools/winscope-ng/src/parsers/parser_input_method_manager_service.ts +++ b/tools/winscope-ng/src/parsers/parser_input_method_manager_service.ts @@ -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; diff --git a/tools/winscope-ng/src/parsers/parser_input_method_service.spec.ts b/tools/winscope-ng/src/parsers/parser_input_method_service.spec.ts index a606f45c8..d1591975a 100644 --- a/tools/winscope-ng/src/parsers/parser_input_method_service.spec.ts +++ b/tools/winscope-ng/src/parsers/parser_input_method_service.spec.ts @@ -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"; diff --git a/tools/winscope-ng/src/parsers/parser_input_method_service.ts b/tools/winscope-ng/src/parsers/parser_input_method_service.ts index 095f923f0..103cc6f3d 100644 --- a/tools/winscope-ng/src/parsers/parser_input_method_service.ts +++ b/tools/winscope-ng/src/parsers/parser_input_method_service.ts @@ -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; diff --git a/tools/winscope-ng/src/test/e2e/viewer_input_method_clients.spec.ts b/tools/winscope-ng/src/test/e2e/viewer_input_method_clients.spec.ts new file mode 100644 index 000000000..92b6638d8 --- /dev/null +++ b/tools/winscope-ng/src/test/e2e/viewer_input_method_clients.spec.ts @@ -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(); + }); +}); diff --git a/tools/winscope-ng/src/test/e2e/viewer_input_method_manager_service.spec.ts b/tools/winscope-ng/src/test/e2e/viewer_input_method_manager_service.spec.ts new file mode 100644 index 000000000..b81810cae --- /dev/null +++ b/tools/winscope-ng/src/test/e2e/viewer_input_method_manager_service.spec.ts @@ -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(); + }); +}); diff --git a/tools/winscope-ng/src/test/e2e/viewer_input_method_service.spec.ts b/tools/winscope-ng/src/test/e2e/viewer_input_method_service.spec.ts new file mode 100644 index 000000000..e62d5c10d --- /dev/null +++ b/tools/winscope-ng/src/test/e2e/viewer_input_method_service.spec.ts @@ -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(); + }); +}); diff --git a/tools/winscope-ng/src/test/e2e/viewer_surface_flinger.spec.ts b/tools/winscope-ng/src/test/e2e/viewer_surface_flinger.spec.ts index 47597caa7..b93b1b6ba 100644 --- a/tools/winscope-ng/src/test/e2e/viewer_surface_flinger.spec.ts +++ b/tools/winscope-ng/src/test/e2e/viewer_surface_flinger.spec.ts @@ -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", () => { diff --git a/tools/winscope-ng/src/test/unit/hierarchy_tree_builder.ts b/tools/winscope-ng/src/test/unit/hierarchy_tree_builder.ts index 7956781d2..0a2d1c4fe 100644 --- a/tools/winscope-ng/src/test/unit/hierarchy_tree_builder.ts +++ b/tools/winscope-ng/src/test/unit/hierarchy_tree_builder.ts @@ -41,7 +41,7 @@ class HierarchyTreeBuilder { diffType?: string; skip?: any; - setId(id: number) { + setId(id: string | number) { this.id = id; return this; } diff --git a/tools/winscope-ng/src/test/unit/utils.ts b/tools/winscope-ng/src/test/unit/utils.ts index d537b64f7..bae42e637 100644 --- a/tools/winscope-ng/src/test/unit/utils.ts +++ b/tools/winscope-ng/src/test/unit/utils.ts @@ -38,6 +38,18 @@ class UnitTestUtils extends CommonTestUtils { return await this.getTraceEntry("traces/elapsed_timestamp/SurfaceFlinger.pb"); } + static async getInputMethodClientsEntry(): Promise { + return await this.getTraceEntry("traces/elapsed_timestamp/InputMethodClients.pb"); + } + + static async getInputMethodServiceEntry(): Promise { + return await this.getTraceEntry("traces/elapsed_timestamp/InputMethodService.pb"); + } + + static async getInputMethodManagerServiceEntry(): Promise { + 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]; diff --git a/tools/winscope-ng/src/viewers/common/ime_presenter.ts b/tools/winscope-ng/src/viewers/common/ime_presenter.ts new file mode 100644 index 000000000..5513c6426 --- /dev/null +++ b/tools/winscope-ng/src/viewers/common/ime_presenter.ts @@ -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 + ) { + 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) { + 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; + uiData: ImeUiData; + private hierarchyFilter: FilterType = TreeUtils.makeNodeFilter(""); + private propertiesFilter: FilterType = TreeUtils.makeNodeFilter(""); + private highlightedItems: Array = []; + private pinnedItems: Array = []; + private pinnedIds: Array = []; + 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. + ` + }, + }; +} diff --git a/tools/winscope-ng/src/viewers/common/ime_presenter_clients.spec.ts b/tools/winscope-ng/src/viewers/common/ime_presenter_clients.spec.ts new file mode 100644 index 000000000..17013f4b0 --- /dev/null +++ b/tools/winscope-ng/src/viewers/common/ime_presenter_clients.spec.ts @@ -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; + let selectedTree: HierarchyTreeNode; + + beforeAll(async () => { + entries = new Map(); + 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(); + 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); + }); +}); diff --git a/tools/winscope-ng/src/viewers/common/ime_presenter_manager_service.spec.ts b/tools/winscope-ng/src/viewers/common/ime_presenter_manager_service.spec.ts new file mode 100644 index 000000000..acd3ff79a --- /dev/null +++ b/tools/winscope-ng/src/viewers/common/ime_presenter_manager_service.spec.ts @@ -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; + let selectedTree: HierarchyTreeNode; + + beforeAll(async () => { + entries = new Map(); + 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(); + 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); + }); +}); diff --git a/tools/winscope-ng/src/viewers/common/ime_presenter_service.spec.ts b/tools/winscope-ng/src/viewers/common/ime_presenter_service.spec.ts new file mode 100644 index 000000000..270867c65 --- /dev/null +++ b/tools/winscope-ng/src/viewers/common/ime_presenter_service.spec.ts @@ -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; + let selectedTree: HierarchyTreeNode; + + beforeAll(async () => { + entries = new Map(); + 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(); + 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); + }); +}); diff --git a/tools/winscope-ng/src/viewers/common/ime_ui_data.ts b/tools/winscope-ng/src/viewers/common/ime_ui_data.ts new file mode 100644 index 000000000..925fa8aef --- /dev/null +++ b/tools/winscope-ng/src/viewers/common/ime_ui_data.ts @@ -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; + highlightedItems: Array = []; + pinnedItems: Array = []; + hierarchyUserOptions: UserOptions = {}; + propertiesUserOptions: UserOptions = {}; + tree: HierarchyTreeNode | null = null; + propertiesTree: PropertiesTreeNode | null = null; + + constructor(dependencies?: Array) { + this.dependencies = dependencies ?? []; + } +} diff --git a/tools/winscope-ng/src/viewers/common/ime_utils.ts b/tools/winscope-ng/src/viewers/common/ime_utils.ts index ba67b7678..b7d93a67d 100644 --- a/tools/winscope-ng/src/viewers/common/ime_utils.ts +++ b/tools/winscope-ng/src/viewers/common/ime_utils.ts @@ -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) { diff --git a/tools/winscope-ng/src/viewers/common/tree_generator.ts b/tools/winscope-ng/src/viewers/common/tree_generator.ts index 0f8a4f064..dd654f96f 100644 --- a/tools/winscope-ng/src/viewers/common/tree_generator.ts +++ b/tools/winscope-ng/src/viewers/common/tree_generator.ts @@ -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 = []; diff --git a/tools/winscope-ng/src/viewers/common/ui_tree_utils.ts b/tools/winscope-ng/src/viewers/common/ui_tree_utils.ts index fed76b3fd..0fb44cd88 100644 --- a/tools/winscope-ng/src/viewers/common/ui_tree_utils.ts +++ b/tools/winscope-ng/src/viewers/common/ui_tree_utils.ts @@ -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" + ]; } diff --git a/tools/winscope-ng/src/viewers/common/viewer_input_method.ts b/tools/winscope-ng/src/viewers/common/viewer_input_method.ts new file mode 100644 index 000000000..7e4ebcc69 --- /dev/null +++ b/tools/winscope-ng/src/viewers/common/viewer_input_method.ts @@ -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): 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}; diff --git a/tools/winscope-ng/src/viewers/components/properties.component.ts b/tools/winscope-ng/src/viewers/components/properties.component.ts index 39e2a68a3..cd9af4b00 100644 --- a/tools/winscope-ng/src/viewers/components/properties.component.ts +++ b/tools/winscope-ng/src/viewers/components/properties.component.ts @@ -51,7 +51,7 @@ import { PropertiesTreeNode, Terminal} from "viewers/common/ui_tree_utils"; - Properties - Proto Dump + Properties - Proto Dump
{ + let fixture: ComponentFixture; + 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(); + }); +}); diff --git a/tools/winscope-ng/src/viewers/components/viewer_input_method.component.ts b/tools/winscope-ng/src/viewers/components/viewer_input_method.component.ts new file mode 100644 index 000000000..647a24647 --- /dev/null +++ b/tools/winscope-ng/src/viewers/components/viewer_input_method.component.ts @@ -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: ` +
+ + + + + + +
+ `, + 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; +} diff --git a/tools/winscope-ng/src/viewers/viewer_factory.ts b/tools/winscope-ng/src/viewers/viewer_factory.ts index db9377d5d..64ca5fa62 100644 --- a/tools/winscope-ng/src/viewers/viewer_factory.ts +++ b/tools/winscope-ng/src/viewers/viewer_factory.ts @@ -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): Viewer[] { @@ -31,6 +37,7 @@ class ViewerFactory { const areViewerDepsSatisfied = Viewer.DEPENDENCIES.every((traceType: TraceType) => activeTraceTypes.has(traceType) ); + if (areViewerDepsSatisfied) { viewers.push(new Viewer()); } diff --git a/tools/winscope-ng/src/viewers/viewer_input_method_clients/viewer_input_method_clients.ts b/tools/winscope-ng/src/viewers/viewer_input_method_clients/viewer_input_method_clients.ts new file mode 100644 index 000000000..c38c63930 --- /dev/null +++ b/tools/winscope-ng/src/viewers/viewer_input_method_clients/viewer_input_method_clients.ts @@ -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}; diff --git a/tools/winscope-ng/src/viewers/viewer_input_method_manager_service/viewer_input_method_manager_service.ts b/tools/winscope-ng/src/viewers/viewer_input_method_manager_service/viewer_input_method_manager_service.ts new file mode 100644 index 000000000..b42001b79 --- /dev/null +++ b/tools/winscope-ng/src/viewers/viewer_input_method_manager_service/viewer_input_method_manager_service.ts @@ -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}; diff --git a/tools/winscope-ng/src/viewers/viewer_input_method_service/viewer_input_method_service.ts b/tools/winscope-ng/src/viewers/viewer_input_method_service/viewer_input_method_service.ts new file mode 100644 index 000000000..221b5be3e --- /dev/null +++ b/tools/winscope-ng/src/viewers/viewer_input_method_service/viewer_input_method_service.ts @@ -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}; diff --git a/tools/winscope-ng/src/viewers/viewer_surface_flinger/presenter.ts b/tools/winscope-ng/src/viewers/viewer_surface_flinger/presenter.ts index c9735b2d3..e77b1770f 100644 --- a/tools/winscope-ng/src/viewers/viewer_surface_flinger/presenter.ts +++ b/tools/winscope-ng/src/viewers/viewer_surface_flinger/presenter.ts @@ -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); } diff --git a/tools/winscope-ng/src/viewers/viewer_surface_flinger/ui_data.ts b/tools/winscope-ng/src/viewers/viewer_surface_flinger/ui_data.ts index f1f14fafc..50f5bedfb 100644 --- a/tools/winscope-ng/src/viewers/viewer_surface_flinger/ui_data.ts +++ b/tools/winscope-ng/src/viewers/viewer_surface_flinger/ui_data.ts @@ -20,7 +20,7 @@ import { TraceType } from "common/trace/trace_type"; import { Rectangle } from "viewers/common/rectangle"; export class UiData { - dependencies: Array = [TraceType.SURFACE_FLINGER]; + dependencies: Array; 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) { + this.dependencies = dependencies ?? []; + } } diff --git a/tools/winscope-ng/src/viewers/viewer_surface_flinger/viewer_surface_flinger.component.ts b/tools/winscope-ng/src/viewers/viewer_surface_flinger/viewer_surface_flinger.component.ts index ac18e83e7..fca9730bd 100644 --- a/tools/winscope-ng/src/viewers/viewer_surface_flinger/viewer_surface_flinger.component.ts +++ b/tools/winscope-ng/src/viewers/viewer_surface_flinger/viewer_surface_flinger.component.ts @@ -52,6 +52,7 @@ import { PersistentStore } from "common/persistent_store"; [propertiesTree]="inputData?.propertiesTree ?? {}" [selectedFlickerItem]="inputData?.selectedLayer ?? {}" [propertyGroups]="true" + [isProtoDump]="true" >
diff --git a/tools/winscope-ng/src/viewers/viewer_window_manager/presenter.ts b/tools/winscope-ng/src/viewers/viewer_window_manager/presenter.ts index 1f9878698..3a39f1497 100644 --- a/tools/winscope-ng/src/viewers/viewer_window_manager/presenter.ts +++ b/tools/winscope-ng/src/viewers/viewer_window_manager/presenter.ts @@ -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); } diff --git a/tools/winscope-ng/src/viewers/viewer_window_manager/ui_data.ts b/tools/winscope-ng/src/viewers/viewer_window_manager/ui_data.ts index 728c514de..9bf29c504 100644 --- a/tools/winscope-ng/src/viewers/viewer_window_manager/ui_data.ts +++ b/tools/winscope-ng/src/viewers/viewer_window_manager/ui_data.ts @@ -19,7 +19,7 @@ import { TraceType } from "common/trace/trace_type"; import { Rectangle } from "viewers/common/rectangle"; export class UiData { - dependencies: Array = [TraceType.WINDOW_MANAGER]; + dependencies: Array; rects: Rectangle[] = []; displayIds: number[] = []; highlightedItems: Array = []; @@ -28,4 +28,8 @@ export class UiData { propertiesUserOptions: UserOptions = {}; tree: HierarchyTreeNode | null = null; propertiesTree: PropertiesTreeNode | null = null; + + constructor(dependencies?: Array) { + this.dependencies = dependencies ?? []; + } } diff --git a/tools/winscope-ng/src/viewers/viewer_window_manager/viewer_window_manager.component.ts b/tools/winscope-ng/src/viewers/viewer_window_manager/viewer_window_manager.component.ts index 300ff0093..941148c4d 100644 --- a/tools/winscope-ng/src/viewers/viewer_window_manager/viewer_window_manager.component.ts +++ b/tools/winscope-ng/src/viewers/viewer_window_manager/viewer_window_manager.component.ts @@ -49,6 +49,7 @@ import { PersistentStore } from "common/persistent_store";