(Rects View Review Changes) Create SF viewer.

Review changes for ag/19482843 (in separate CL as requested).

Bug: b/238089034
Test: npm run test:all
Change-Id: I9d2f99090f2e2125271b702c8a1dc409f112b776
This commit is contained in:
Priyanka Patel
2022-08-16 08:33:28 +00:00
parent 43cf6c1dd2
commit c1541c1bd4
38 changed files with 208 additions and 271 deletions

View File

@@ -31,7 +31,6 @@ import { PropertiesComponent } from "viewers/properties.component";
import { RectsComponent } from "viewers/rects.component";
import { TraceViewHeaderComponent } from "./components/trace_view_header.component";
import { TraceViewComponent } from "./components/trace_view.component";
import { CanvasService } from "viewers/canvas.service";
@NgModule({
declarations: [
@@ -70,7 +69,6 @@ import { CanvasService } from "viewers/canvas.service";
MatSliderModule,
MatRadioModule
],
providers: [CanvasService],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@@ -16,13 +16,13 @@
import { CommonModule } from "@angular/common";
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { AdbProxyComponent } from "./adb_proxy.component";
import { proxyClient, ProxyState } from "../../trace_collection/proxy_client";
import { proxyClient, ProxyState } from "trace_collection/proxy_client";
import { MatIconModule } from "@angular/material/icon";
import { MatFormFieldModule } from "@angular/material/form-field";
import { MatInputModule } from "@angular/material/input";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { MatButtonModule } from "@angular/material/button";
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from "@angular/core";
import { NO_ERRORS_SCHEMA } from "@angular/core";
describe("AdbProxyComponent", () => {
let fixture: ComponentFixture<AdbProxyComponent>;
@@ -40,7 +40,7 @@ describe("AdbProxyComponent", () => {
MatButtonModule
],
declarations: [AdbProxyComponent],
schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA]
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
fixture = TestBed.createComponent(AdbProxyComponent);
component = fixture.componentInstance;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { Component, Input, Output, EventEmitter } from "@angular/core";
import { proxyClient, ProxyClient, ProxyState } from "../../trace_collection/proxy_client";
import { proxyClient, ProxyClient, ProxyState } from "trace_collection/proxy_client";
@Component({
selector: "adb-proxy",

View File

@@ -17,7 +17,7 @@ import { Component, Injector, Inject, ViewEncapsulation, Input } from "@angular/
import { createCustomElement } from "@angular/elements";
import { TraceCoordinator } from "../trace_coordinator";
import { proxyClient, ProxyState } from "trace_collection/proxy_client";
import { PersistentStore } from "../../common/persistent_store";
import { PersistentStore } from "common/persistent_store";
import { ViewerWindowManagerComponent } from "viewers/viewer_window_manager/viewer_window_manager.component";
import { ViewerSurfaceFlingerComponent } from "viewers/viewer_surface_flinger/viewer_surface_flinger.component";
import { TraceViewComponent } from "./trace_view.component";
@@ -36,7 +36,7 @@ import { Viewer } from "viewers/viewer";
*ngIf="dataLoaded"
step="1"
min="0"
[max]="traceCoordinator.getTimestamps().length -1"
[max]="this.allTimestamps.length-1"
aria-label="units"
[value]="currentTimestampIndex"
(input)="updateCurrentTimestamp($event)"
@@ -72,8 +72,9 @@ export class AppComponent {
store: PersistentStore = new PersistentStore();
@Input() dataLoaded = false;
viewersCreated = false;
currentTimestamp: Timestamp;
currentTimestamp?: Timestamp;
currentTimestampIndex = 0;
allTimestamps: Timestamp[] = [];
constructor(
@Inject(Injector) injector: Injector
@@ -95,6 +96,7 @@ export class AppComponent {
onDataLoadedChange(dataLoaded: boolean) {
if (dataLoaded && !this.viewersCreated) {
this.allTimestamps = this.traceCoordinator.getTimestamps();
this.traceCoordinator.createViewers();
this.createViewerElements();
this.currentTimestampIndex = 0;
@@ -108,8 +110,10 @@ export class AppComponent {
const viewersDiv = document.querySelector("div#viewers")!;
viewersDiv.innerHTML = "";
let cardCounter = 0;
this.traceCoordinator.getViewers().forEach((viewer: Viewer) => {
const traceView = document.createElement("trace-view");
(traceView as any).title = viewer.getTitle();
(traceView as any).dependencies = viewer.getDependencies();
(traceView as any).showTrace = true;
traceView.addEventListener("saveTraces", ($event: any) => {
@@ -118,15 +122,15 @@ export class AppComponent {
viewersDiv.appendChild(traceView);
const traceCard = traceView.querySelector(".trace-card")!;
traceCard.id = `card-${viewer.getDependencies()}`;
traceCard.id = `card-${cardCounter}`;
(traceView as any).cardId = cardCounter;
cardCounter++;
const traceCardContent = traceCard.querySelector(".trace-card-content")!;
const view = viewer.getView();
(view as any).showTrace = (traceView as any).showTrace;
traceCardContent.appendChild(view);
});
this.currentTimestampIndex = 0;
this.notifyCurrentTimestamp();
}
updateCurrentTimestamp(event: MatSliderChange) {
@@ -137,13 +141,13 @@ export class AppComponent {
}
public notifyCurrentTimestamp() {
this.currentTimestamp = this.traceCoordinator.getTimestamps()[this.currentTimestampIndex];
this.currentTimestamp = this.allTimestamps[this.currentTimestampIndex];
this.traceCoordinator.notifyCurrentTimestamp(this.currentTimestamp);
}
public toggleTimestamp() {
if (this.currentTimestampIndex===0) {
this.currentTimestampIndex = this.traceCoordinator.getTimestamps().length-1;
this.currentTimestampIndex = this.allTimestamps.length-1;
} else {
this.currentTimestampIndex = 0;
}

View File

@@ -24,7 +24,7 @@ import { MatListModule } from "@angular/material/list";
import { MatButtonModule } from "@angular/material/button";
import { MatProgressBarModule } from "@angular/material/progress-bar";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from "@angular/core";
import { NO_ERRORS_SCHEMA } from "@angular/core";
describe("CollectTracesComponent", () => {
let fixture: ComponentFixture<CollectTracesComponent>;
@@ -47,7 +47,7 @@ describe("CollectTracesComponent", () => {
WebAdbComponent,
TraceConfigComponent,
],
schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA]
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
fixture = TestBed.createComponent(CollectTracesComponent);
component = fixture.componentInstance;

View File

@@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, Inject, Input, Output, EventEmitter, NgZone } from "@angular/core";
import { Component, Inject, Input, Output, EventEmitter, OnInit, OnDestroy } from "@angular/core";
import { ProxyConnection } from "trace_collection/proxy_connection";
import { Connection } from "trace_collection/connection";
import { setTraces } from "trace_collection/set_traces";
import { ProxyState } from "../../trace_collection/proxy_client";
import { traceConfigurations, configMap, SelectionConfiguration, EnableConfiguration } from "../../trace_collection/trace_collection_utils";
import { ProxyState } from "trace_collection/proxy_client";
import { traceConfigurations, configMap, SelectionConfiguration, EnableConfiguration } from "trace_collection/trace_collection_utils";
import { TraceCoordinator } from "app/trace_coordinator";
import { PersistentStore } from "../../common/persistent_store";
import { PersistentStore } from "common/persistent_store";
@Component({
@@ -33,9 +33,9 @@ import { PersistentStore } from "../../common/persistent_store";
<div class="set-up-adb" *ngIf="!connect.adbSuccess()">
<button id="proxy-tab" mat-raised-button [ngClass]="tabClass(true)" (click)="displayAdbProxyTab()">ADB Proxy</button>
<button id="web-tab" mat-raised-button [ngClass]="tabClass(false)" (click)="displayWebAdbTab()">Web ADB</button>
<!-- <button id="web-tab" mat-raised-button [ngClass]="tabClass(false)" (click)="displayWebAdbTab()">Web ADB</button> -->
<adb-proxy *ngIf="isAdbProxy" [(proxy)]="connect.proxy!" (addKey)="onAddKey($event)"></adb-proxy>
<web-adb *ngIf="!isAdbProxy"></web-adb>
<!-- <web-adb *ngIf="!isAdbProxy"></web-adb> TODO: fix web adb workflow -->
</div>
<div id="devices-connecting" *ngIf="connect.isDevicesState()">
@@ -116,28 +116,20 @@ import { PersistentStore } from "../../common/persistent_store";
".mat-checkbox-checked .mat-checkbox-background {transform: scale(0.7); font-size: 10;}"
]
})
export class CollectTracesComponent {
export class CollectTracesComponent implements OnInit, OnDestroy {
objectKeys = Object.keys;
isAdbProxy = true;
traceConfigurations = traceConfigurations;
connect: Connection = new ProxyConnection();
setTraces = setTraces;
@Input()
store: PersistentStore = new PersistentStore();
@Input()
traceCoordinator: TraceCoordinator;
@Output()
traceCoordinatorChange = new EventEmitter<TraceCoordinator>();
dataLoaded = false;
@Output()
dataLoadedChange = new EventEmitter<boolean>();
@Input() store!: PersistentStore;
@Input() traceCoordinator!: TraceCoordinator;
constructor(@Inject(NgZone) private ngZone: NgZone) {
@Output() dataLoadedChange = new EventEmitter<boolean>();
ngOnInit() {
if (this.isAdbProxy) {
this.connect = new ProxyConnection();
} else {
@@ -278,11 +270,8 @@ export class CollectTracesComponent {
this.traceCoordinator.clearData();
await this.traceCoordinator.addTraces(this.connect.adbData());
this.ngZone.run(() => {
this.dataLoaded = true;
this.dataLoadedChange.emit(this.dataLoaded);
});
this.dataLoaded = true;
this.dataLoadedChange.emit(this.dataLoaded);
console.log("finished loading data!");
}

View File

@@ -21,7 +21,7 @@ import { MatFormFieldModule } from "@angular/material/form-field";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { MatInputModule } from "@angular/material/input";
import { MatSelectModule } from "@angular/material/select";
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from "@angular/core";
import { NO_ERRORS_SCHEMA } from "@angular/core";
describe("TraceConfigComponent", () => {
let fixture: ComponentFixture<TraceConfigComponent>;
@@ -39,7 +39,7 @@ describe("TraceConfigComponent", () => {
BrowserAnimationsModule
],
declarations: [TraceConfigComponent],
schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA]
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
fixture = TestBed.createComponent(TraceConfigComponent);
component = fixture.componentInstance;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { Component, Input } from "@angular/core";
import { EnableConfiguration, SelectionConfiguration, TraceConfiguration } from "../../trace_collection/trace_collection_utils";
import { EnableConfiguration, SelectionConfiguration, TraceConfiguration } from "trace_collection/trace_collection_utils";
@Component({
selector: "trace-config",

View File

@@ -29,8 +29,10 @@ import { TraceType } from "common/trace/trace_type";
<mat-card-header>
<mat-card-title class="trace-card-title" *ngIf="dependencies">
<trace-view-header
[title]="title"
[(showTrace)]="showTrace"
[dependencies]="dependencies"
[cardId]="cardId"
(saveTraceChange)="onSaveTraces($event)"
></trace-view-header>
</mat-card-title>
@@ -41,14 +43,11 @@ import { TraceType } from "common/trace/trace_type";
`,
})
export class TraceViewComponent {
@Input()
dependencies: TraceType[];
@Input()
showTrace: boolean;
@Output()
saveTraces = new EventEmitter<TraceType[]>();
@Input() title!: string;
@Input() dependencies!: TraceType[];
@Input() showTrace = true;
@Input() cardId = 0;
@Output() saveTraces = new EventEmitter<TraceType[]>();
TRACE_INFO = TRACE_INFO;

View File

@@ -18,7 +18,6 @@ import { ComponentFixture, TestBed } from "@angular/core/testing";
import { TraceViewHeaderComponent } from "./trace_view_header.component";
import { MatIconModule } from "@angular/material/icon";
import { MatButtonModule } from "@angular/material/button";
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from "@angular/core";
import { TraceType } from "common/trace/trace_type";
describe("TraceViewHeaderComponent", () => {
@@ -33,8 +32,7 @@ describe("TraceViewHeaderComponent", () => {
MatIconModule,
MatButtonModule
],
declarations: [TraceViewHeaderComponent],
schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA]
declarations: [TraceViewHeaderComponent]
}).compileComponents();
fixture = TestBed.createComponent(TraceViewHeaderComponent);
component = fixture.componentInstance;
@@ -85,6 +83,7 @@ describe("TraceViewHeaderComponent", () => {
});
it("check that title is displayed", () => {
component.title = "Surface Flinger, Window Manager";
fixture.detectChanges();
const title = htmlElement.querySelector(".trace-card-title-text");
expect(title).toBeTruthy();

View File

@@ -33,7 +33,7 @@ import html2canvas from "html2canvas";
</button>
<mat-icon id="dep-icon" *ngFor="let dep of dependencies" aria-hidden="true" class="icon-button">{{TRACE_INFO[dep].icon}}</mat-icon>
<span class="trace-card-title-text">
{{getTitle()}}
{{title}}
</span>
<button id="save-btn" class="icon-button" (click)="saveTraces()">
<mat-icon aria-hidden="true">save_alt</mat-icon>
@@ -47,17 +47,13 @@ import html2canvas from "html2canvas";
]
})
export class TraceViewHeaderComponent {
@Input()
dependencies: TraceType[];
@Input() title?: string;
@Input() dependencies?: TraceType[];
@Input() showTrace = true;
@Input() cardId!: number ;
@Input()
showTrace = true;
@Output()
showTraceChange = new EventEmitter<boolean>();
@Output()
saveTraceChange = new EventEmitter<TraceType[]>();
@Output() showTraceChange = new EventEmitter<boolean>();
@Output() saveTraceChange = new EventEmitter<TraceType[]>();
TRACE_INFO = TRACE_INFO;
@@ -66,25 +62,12 @@ export class TraceViewHeaderComponent {
this.showTraceChange.emit(this.showTrace);
}
getTitle() {
if (this.dependencies === undefined) {
return "Unknown Trace";
}
let title = `${TRACE_INFO[this.dependencies[0]].name}`;
if (this.dependencies.length > 1) {
for (const dep of this.dependencies.slice(1)) {
title += `, ${TRACE_INFO[dep].name}`;
}
}
return title;
}
public saveTraces() {
this.saveTraceChange.emit(this.dependencies);
}
public takeScreenshot() {
const el = document.querySelector(`#card-${this.dependencies}`);
const el = document.querySelector(`#card-${this.cardId}`);
if (el) {
html2canvas((el as HTMLElement)).then((canvas) => {
const uri = canvas.toDataURL();

View File

@@ -17,7 +17,7 @@ import {ComponentFixture, TestBed} from "@angular/core/testing";
import {UploadTracesComponent} from "./upload_traces.component";
import { MatCardModule } from "@angular/material/card";
describe("CollectTracesComponent", () => {
describe("UploadTracesComponent", () => {
let fixture: ComponentFixture<UploadTracesComponent>;
let component: UploadTracesComponent;
let htmlElement: HTMLElement;

View File

@@ -74,16 +74,11 @@ import { LoadedTrace } from "app/loaded_trace";
]
})
export class UploadTracesComponent {
@Input()
traceCoordinator: TraceCoordinator;
@Output()
traceCoordinatorChange = new EventEmitter<TraceCoordinator>();
@Input() traceCoordinator!: TraceCoordinator;
dataLoaded = false;
@Output()
dataLoadedChange = new EventEmitter<boolean>();
@Output() dataLoadedChange = new EventEmitter<boolean>();
constructor(@Inject(NgZone) private ngZone: NgZone) {}
@@ -121,7 +116,6 @@ export class UploadTracesComponent {
this.dataLoaded = false;
this.loadedTraces = [];
this.dataLoadedChange.emit(this.dataLoaded);
this.traceCoordinatorChange.emit(this.traceCoordinator);
}
public onFileDragIn(e: DragEvent) {

View File

@@ -1,4 +1,4 @@
import { TraceType } from "../common/trace/trace_type";
import { TraceType } from "common/trace/trace_type";
export interface LoadedTrace {
name: string;

View File

@@ -22,7 +22,6 @@ import { Viewer } from "viewers/viewer";
import { ViewerFactory } from "viewers/viewer_factory";
import { LoadedTrace } from "app/loaded_trace";
import { TRACE_INFO } from "./trace_info";
import { bigIntMath } from "common/utils/bigint_utils";
class TraceCoordinator {
private parsers: Parser[];
@@ -105,19 +104,9 @@ class TraceCoordinator {
this.parsers.forEach(parser => {
const targetTimestamp = timestamp;
const parserTimestamps = parser.getTimestamps(timestamp.getType());
const closestTimestamp = parserTimestamps?.reduce((prev, curr) => {
const prevDiff = bigIntMath.abs(prev.getValueNs() - targetTimestamp.getValueNs());
const currDiff = bigIntMath.abs(curr.getValueNs() - targetTimestamp.getValueNs());
return currDiff < prevDiff ? curr : prev;
});
if (closestTimestamp) {
const entry = parser.getTraceEntry(closestTimestamp);
if (entry != undefined) {
traceEntries.set(parser.getTraceType(), entry);
}
const entry = parser.getTraceEntry(targetTimestamp);
if (entry !== undefined) {
traceEntries.set(parser.getTraceType(), entry);
}
});

View File

@@ -1,4 +1,4 @@
import { TraceType } from "../common/trace/trace_type";
import { TraceType } from "common/trace/trace_type";
const WINDOW_MANAGER_ICON = "view_compact";
const SURFACE_FLINGER_ICON = "filter_none";

View File

@@ -1,4 +1,4 @@
import { TraceType } from "../common/trace/trace_type";
import { TraceType } from "common/trace/trace_type";
const WINDOW_MANAGER_ICON = "view_compact";
const SURFACE_FLINGER_ICON = "filter_none";

View File

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

View File

@@ -51,6 +51,14 @@ button {
margin: 10px;
}
.card-grid {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
overflow: auto;
}
mat-checkbox {
margin-left: 5px;
}

View File

@@ -29,7 +29,7 @@ describe("Viewer SurfaceFlinger", () => {
const loadData = element(by.css(".load-btn"));
loadData.click();
const surfaceFlingerCard: ElementFinder = element(by.css("#card-2"));
const surfaceFlingerCard: ElementFinder = element(by.css(".trace-card"));
expect(surfaceFlingerCard.getText()).toContain("Surface Flinger");
});
});

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { PersistentStore } from "../common/persistent_store";
import { PersistentStore } from "common/persistent_store";
import { configMap, TRACES } from "./trace_collection_utils";
import { setTraces, SetTraces } from "./set_traces";
import { Device } from "./connection";

View File

@@ -13,15 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from "@angular/core";
import {Rectangle } from "ui_data/ui_data_surface_flinger";
import { Rectangle } from "viewers/viewer_surface_flinger/ui_data";
import * as THREE from "three";
import { CSS2DRenderer, CSS2DObject } from "three/examples/jsm/renderers/CSS2DRenderer";
@Injectable()
export class CanvasService {
initialise() {
export class CanvasGraphics {
constructor() {
//set up camera
const left = -this.cameraHalfWidth,
right = this.cameraHalfWidth,
@@ -32,16 +29,17 @@ export class CanvasService {
this.camera = new THREE.OrthographicCamera(
left,right,top,bottom,near,far
);
}
initialise(canvas: HTMLCanvasElement) {
// initialise canvas
this.canvas = document.getElementById("rects-canvas") as HTMLCanvasElement;
if (!this.canvas) return;
this.canvas = canvas;
}
refreshCanvas() {
//set canvas size
this.canvas.style.width = "100%";
this.canvas.style.height = "40rem";
this.canvas!.style.width = "100%";
this.canvas!.style.height = "40rem";
// TODO: click and drag rotation control
this.camera.position.set(this.xyCameraPos, this.xyCameraPos, 6);
@@ -103,7 +101,7 @@ export class CanvasService {
return y;
})) - labelYShift;
this.createRects(
this.drawScene(
rectCounter,
numberOfVisibleRects,
visibleDarkFactor,
@@ -121,15 +119,15 @@ export class CanvasService {
// const gridHelper = new THREE.GridHelper(5);
// scene.add(axesHelper, gridHelper)
renderer.setSize(this.canvas.clientWidth, this.canvas.clientHeight);
renderer.setSize(this.canvas!.clientWidth, this.canvas!.clientHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.render(scene, this.camera);
labelRenderer.setSize(this.canvas.clientWidth, this.canvas.clientHeight);
labelRenderer.setSize(this.canvas!.clientWidth, this.canvas!.clientHeight);
labelRenderer.render(scene, this.camera);
}
private createRects(
private drawScene(
rectCounter: number,
visibleRects: number,
visibleDarkFactor:number,
@@ -144,12 +142,13 @@ export class CanvasService {
) {
this.targetObjects = [];
this.rects.forEach(rect => {
if (this.visibleView && !rect.isVisible ||
!this.visibleView && !this.showVirtualDisplays && rect.isDisplay && rect.isVirtual) {
document.querySelector(`.label-${rectCounter}`)?.remove();
const visibleViewInvisibleRect = this.visibleView && !rect.isVisible;
const xrayViewNoVirtualDisplaysVirtualRect = !this.visibleView && !this.showVirtualDisplays && rect.isDisplay && rect.isVirtual;
if (visibleViewInvisibleRect || xrayViewNoVirtualDisplaysVirtualRect) {
rectCounter++;
return;
}
//set colour mapping
let planeColor;
if (this.highlighted === `${rect.id}`) {
@@ -224,7 +223,7 @@ export class CanvasService {
private setCircleMaterial(planeRect: THREE.Mesh, rect: Rectangle) {
const labelCircle = new THREE.CircleGeometry(0.02, 200);
const circleMaterial = new THREE.MeshBasicMaterial({color: 0x000000 });
const circleMaterial = new THREE.MeshBasicMaterial({ color: 0x000000 });
const circle = new THREE.Mesh(labelCircle, circleMaterial);
circle.position.set(
planeRect.position.x + rect.width/2 - 0.05,
@@ -413,5 +412,5 @@ export class CanvasService {
private rects: Rectangle[] = [];
private labelElements: HTMLElement[] = [];
private targetObjects: any[] = [];
private canvas: HTMLCanvasElement;
private canvas?: HTMLCanvasElement;
}

View File

@@ -21,9 +21,8 @@ import { MatCheckboxModule } from "@angular/material/checkbox";
import { MatCardModule } from "@angular/material/card";
import { MatRadioModule } from "@angular/material/radio";
import { MatSliderModule } from "@angular/material/slider";
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from "@angular/core";
import { Rectangle } from "../ui_data/ui_data_surface_flinger";
import { CanvasService } from "./canvas.service";
import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
import { Rectangle } from "./viewer_surface_flinger/ui_data";
describe("RectsComponent", () => {
let component: TestHostComponent;
@@ -40,8 +39,7 @@ describe("RectsComponent", () => {
MatRadioModule
],
declarations: [RectsComponent, TestHostComponent],
providers: [CanvasService],
schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA]
schemas: [CUSTOM_ELEMENTS_SCHEMA]
}).compileComponents();
});
@@ -64,10 +62,10 @@ describe("RectsComponent", () => {
it("check that layer separation slider causes view to change", () => {
const slider = htmlElement.querySelector("mat-slider");
spyOn(component.rectsComponent.canvasService, "updateLayerSeparation");
spyOn(component.rectsComponent.canvasGraphics, "updateLayerSeparation");
slider?.dispatchEvent(new MouseEvent("mousedown"));
fixture.detectChanges();
expect(component.rectsComponent.canvasService.updateLayerSeparation).toHaveBeenCalled();
expect(component.rectsComponent.canvasGraphics.updateLayerSeparation).toHaveBeenCalled();
});
it("check that rects canvas is rendered", () => {
@@ -101,24 +99,23 @@ describe("RectsComponent", () => {
stackId: 0,
}
]);
spyOn(component.rectsComponent, "updateVariablesBeforeRefresh").and.callThrough();
spyOn(component.rectsComponent, "drawRects").and.callThrough();
fixture.detectChanges();
await new Promise( resolve => setTimeout(resolve, 4000));
expect(component.rectsComponent.updateVariablesBeforeRefresh).toHaveBeenCalled();
expect(component.rectsComponent.drawRects).toHaveBeenCalled();
});
@Component({
selector: "host-component",
template: "<rects-view [rects]=\"rects ?? []\"></rects-view>"
template: "<rects-view [rects]=\"rects\"></rects-view>"
})
class TestHostComponent {
public rects: Rectangle[];
public rects: Rectangle[] = [];
addRects(newRects: Rectangle[]) {
this.rects = newRects;
}
@ViewChild(RectsComponent)
public rectsComponent: RectsComponent;
public rectsComponent!: RectsComponent;
}
});

View File

@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, Input, OnChanges, OnDestroy, Inject, NgZone, ElementRef, SimpleChanges } from "@angular/core";
import { MatrixUtils } from "common/utils/matrix_utils";
import { Point, Rectangle, RectMatrix, RectTransform } from "ui_data/ui_data_surface_flinger";
import { Component, Input, OnChanges, OnDestroy, Inject, ElementRef, SimpleChanges } from "@angular/core";
import { RectsUtils } from "./rects_utils";
import { Point, Rectangle, RectMatrix, RectTransform } from "viewers/viewer_surface_flinger/ui_data";
import { interval, Subscription } from "rxjs";
import { CanvasService } from "./canvas.service";
import { CanvasGraphics } from "./canvas_graphics";
import * as THREE from "three";
@Component({
@@ -34,7 +34,7 @@ import * as THREE from "three";
max="0.4"
aria-label="units"
[value]="getLayerSeparation()"
(input)="canvasService.updateLayerSeparation($event.value!)"
(input)="canvasGraphics.updateLayerSeparation($event.value!)"
></mat-slider>
<mat-slider
step="0.01"
@@ -42,24 +42,24 @@ import * as THREE from "three";
max="4"
aria-label="units"
[value]="xyCameraPos()"
(input)="canvasService.updateRotation($event.value!)"
(input)="canvasGraphics.updateRotation($event.value!)"
></mat-slider>
<mat-checkbox
[hidden]="visibleView()"
class="rects-checkbox"
[checked]="showVirtualDisplays()"
(change)="canvasService.updateVirtualDisplays($event.checked!)"
(change)="canvasGraphics.updateVirtualDisplays($event.checked!)"
>Show virtual displays</mat-checkbox>
</mat-card-header>
<mat-card-content class="rects-content">
<div class="canvas-container">
<div class="zoom-container">
<button id="zoom-btn" (click)="canvasService.updateZoom(true)">
<button id="zoom-btn" (click)="canvasGraphics.updateZoom(true)">
<mat-icon aria-hidden="true">
zoom_in
</mat-icon>
</button>
<button id="zoom-btn" (click)="canvasService.updateZoom(false)">
<button id="zoom-btn" (click)="canvasGraphics.updateZoom(false)">
<mat-icon aria-hidden="true">
zoom_out
</mat-icon>
@@ -91,20 +91,15 @@ import * as THREE from "three";
})
export class RectsComponent implements OnChanges, OnDestroy {
@Input()
bounds: any;
@Input() rects!: Rectangle[];
@Input()
rects: Rectangle[];
@Input()
highlighted = "";
@Input() highlighted = "";
constructor(
@Inject(NgZone) private ngZone: NgZone,
@Inject(ElementRef) private elementRef: ElementRef,
@Inject(CanvasService) public canvasService: CanvasService
) {}
) {
this.canvasGraphics = new CanvasGraphics();
}
ngOnDestroy() {
if (this.canvasSubscription) {
@@ -115,13 +110,13 @@ export class RectsComponent implements OnChanges, OnDestroy {
ngOnChanges(changes: SimpleChanges) {
if (this.rects.length > 0) {
//change in rects so they must undergo transformation and scaling before canvas refreshed
this.canvasService.clearLabelElements();
this.canvasGraphics.clearLabelElements();
this.rects = this.rects.filter(rect => rect.isVisible || rect.isDisplay);
this.displayRects = this.rects.filter(rect => rect.isDisplay);
this.computeBounds();
this.rects = this.rects.map(rect => {
if (changes["rects"] && rect.transform) {
return MatrixUtils.transformRect(rect.transform.matrix ?? rect.transform, rect);
return RectsUtils.transformRect(rect.transform.matrix ?? rect.transform, rect);
} else {
return rect;
}
@@ -133,32 +128,31 @@ export class RectsComponent implements OnChanges, OnDestroy {
}
}
onRectClick(event:any) {
onRectClick(event:PointerEvent) {
this.setNormalisedMousePos(event);
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(this.mouse, this.canvasService.getCamera());
raycaster.setFromCamera(this.mouse, this.canvasGraphics.getCamera());
// create an array containing all objects in the scene with which the ray intersects
const intersects = raycaster.intersectObjects(this.canvasService.getTargetObjects());
const intersects = raycaster.intersectObjects(this.canvasGraphics.getTargetObjects());
// if there is one (or more) intersections
if (intersects.length > 0){
if (this.highlighted === intersects[0].object.name) {
this.highlighted = "";
this.canvasService.updateHighlighted("");
this.canvasGraphics.updateHighlighted("");
} else {
this.highlighted = intersects[0].object.name;
this.canvasService.updateHighlighted(intersects[0].object.name);
this.canvasGraphics.updateHighlighted(intersects[0].object.name);
}
this.ngZone.run(() => {
this.updateHighlightedRect();
});
this.updateHighlightedRect();
}
}
setNormalisedMousePos(event:any) {
setNormalisedMousePos(event:PointerEvent) {
event.preventDefault();
const canvasOffset = event.target.getBoundingClientRect();
this.mouse.x = ((event.clientX-canvasOffset.left)/event.target.clientWidth) * 2 - 1;
this.mouse.y = -((event.clientY-canvasOffset.top)/event.target.clientHeight) * 2 + 1;
const canvas = (event.target as Element);
const canvasOffset = canvas.getBoundingClientRect();
this.mouse.x = ((event.clientX-canvasOffset.left)/canvas.clientWidth) * 2 - 1;
this.mouse.y = -((event.clientY-canvasOffset.top)/canvas.clientHeight) * 2 + 1;
this.mouse.z = 0;
}
@@ -174,22 +168,23 @@ export class RectsComponent implements OnChanges, OnDestroy {
if (this.canvasSubscription) {
this.canvasSubscription.unsubscribe();
}
this.canvasService.initialise();
const canvas = document.getElementById("rects-canvas") as HTMLCanvasElement;
this.canvasGraphics.initialise(canvas);
this.canvasSubscription = this.drawRectsInterval.subscribe(() => {
this.updateVariablesBeforeRefresh();
this.canvasService.refreshCanvas();
this.canvasGraphics.refreshCanvas();
});
}
updateVariablesBeforeRefresh() {
this.canvasService.updateRects(this.rects);
this.canvasGraphics.updateRects(this.rects);
const biggestX = Math.max(...this.rects.map(rect => rect.topLeft.x + rect.width/2));
this.canvasService.updateIsLandscape(biggestX > this.s({x: this.boundsWidth, y:this.boundsHeight}).x/2);
this.canvasGraphics.updateIsLandscape(biggestX > this.s({x: this.boundsWidth, y:this.boundsHeight}).x/2);
}
onChangeView(visible: boolean) {
this.canvasService.updateVisibleView(visible);
this.canvasService.clearLabelElements();
this.canvasGraphics.updateVisibleView(visible);
this.canvasGraphics.clearLabelElements();
}
scaleRects() {
@@ -209,20 +204,17 @@ export class RectsComponent implements OnChanges, OnDestroy {
}
computeBounds(): any {
if (this.bounds) {
return this.bounds;
}
this.boundsWidth = Math.max(...this.rects.map((rect) => {
const mat = this.getMatrix(rect);
if (mat) {
return MatrixUtils.transformRect(mat, rect).width;
return RectsUtils.transformRect(mat, rect).width;
} else {
return rect.width;
}}));
this.boundsHeight = Math.max(...this.rects.map((rect) => {
const mat = this.getMatrix(rect);
if (mat) {
return MatrixUtils.transformRect(mat, rect).height;
return RectsUtils.transformRect(mat, rect).height;
} else {
return rect.height;
}}));
@@ -245,9 +237,9 @@ export class RectsComponent implements OnChanges, OnDestroy {
s(sourceCoordinates: Point) {
let scale;
if (this.boundsWidth < this.boundsHeight) {
scale = this.canvasService.cameraHalfHeight*2 * 0.6 / this.boundsHeight;
scale = this.canvasGraphics.cameraHalfHeight*2 * 0.6 / this.boundsHeight;
} else {
scale = this.canvasService.cameraHalfWidth*2 * 0.6 / this.boundsWidth;
scale = this.canvasGraphics.cameraHalfWidth*2 * 0.6 / this.boundsWidth;
}
return {
x: sourceCoordinates.x * scale,
@@ -268,26 +260,27 @@ export class RectsComponent implements OnChanges, OnDestroy {
}
visibleView() {
return this.canvasService.getVisibleView();
return this.canvasGraphics.getVisibleView();
}
getLayerSeparation() {
return this.canvasService.getLayerSeparation();
return this.canvasGraphics.getLayerSeparation();
}
xyCameraPos() {
return this.canvasService.getXyCameraPos();
return this.canvasGraphics.getXyCameraPos();
}
showVirtualDisplays() {
return this.canvasService.getShowVirtualDisplays();
return this.canvasGraphics.getShowVirtualDisplays();
}
canvasGraphics: CanvasGraphics;
private readonly _60fpsInterval = 16.66666666666667;
private drawRectsInterval = interval(this._60fpsInterval);
private boundsWidth = 0;
private boundsHeight = 0;
private displayRects: Rectangle[];
private canvasSubscription: Subscription;
private displayRects!: Rectangle[];
private canvasSubscription?: Subscription;
private mouse = new THREE.Vector3(0, 0, 0);
}

View File

@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { MatrixUtils } from "./matrix_utils";
import { RectsUtils } from "./rects_utils";
describe("MatrixUtils", () => {
describe("RectsUtils", () => {
it("transforms rect", () => {
const transform = {
matrix: {
@@ -54,6 +54,6 @@ describe("MatrixUtils", () => {
stackId: 0,
isVirtual: undefined
};
expect(MatrixUtils.transformRect(rect.transform.matrix, rect)).toEqual(expected);
expect(RectsUtils.transformRect(rect.transform.matrix, rect)).toEqual(expected);
});
});

View File

@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Point, Rectangle, RectMatrix, RectTransform } from "ui_data/ui_data_surface_flinger";
import { Point, Rectangle, RectMatrix, RectTransform } from "viewers/viewer_surface_flinger/ui_data";
export const MatrixUtils = {
export const RectsUtils = {
multiplyMatrix(matrix:any, corner: Point): Point {
if (!matrix) return corner;
// |dsdx dsdy tx| | x | |x*dsdx + y*dsdy + tx|

View File

@@ -19,6 +19,7 @@ interface Viewer {
//TODO: add TraceEntry data type
notifyCurrentTraceEntries(entries: Map<TraceType, any>): void;
getView(): HTMLElement;
getTitle(): string;
getDependencies(): TraceType[];
}

View File

@@ -13,30 +13,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Rectangle, RectMatrix, UiDataSurfaceFlinger } from "ui_data/ui_data_surface_flinger";
import { Presenter } from "./presenter";
import { UiDataCallbackType } from "./presenter";
import { Rectangle, RectMatrix, RectTransform, UiData } from "viewers/viewer_surface_flinger/ui_data";
import { TraceType } from "common/trace/trace_type";
class PresenterSurfaceFlinger extends Presenter {
constructor(uiDataCallback: UiDataCallbackType) {
super(uiDataCallback);
this.uiDataCallback = uiDataCallback;
this.uiData = new UiDataSurfaceFlinger("Initial UI data");
this.uiDataCallback(this.uiData);
type NotifyViewCallbackType = (uiData: UiData) => void;
class Presenter {
constructor(notifyViewCallback: NotifyViewCallbackType) {
this.notifyViewCallback = notifyViewCallback;
this.uiData = new UiData("Initial UI data");
this.notifyViewCallback(this.uiData);
}
updateHighlightedRect(event: any) {
updateHighlightedRect(event: CustomEvent) {
this.highlighted = event.detail.layerId;
this.uiData.highlighted = this.highlighted;
console.log("changed highlighted rect: ", this.uiData.highlighted);
this.uiDataCallback(this.uiData);
this.notifyViewCallback(this.uiData);
}
override notifyCurrentTraceEntries(entries: Map<TraceType, any>) {
notifyCurrentTraceEntries(entries: Map<TraceType, any>) {
const entry = entries.get(TraceType.SURFACE_FLINGER);
this.uiData = new UiDataSurfaceFlinger("New surface flinger ui data");
this.uiData.rects = [];
this.uiData = new UiData("New surface flinger ui data");
const displayRects = entry.displays.map((display: any) => {
const rect = display.layerStackSpace;
rect.label = display.name;
@@ -45,13 +43,14 @@ class PresenterSurfaceFlinger extends Presenter {
rect.isDisplay = true;
rect.isVirtual = display.isVirtual;
return rect;
});
}) ?? [];
this.uiData.highlighted = this.highlighted;
this.rectToUiData(entry.rects.concat(displayRects));
this.uiDataCallback(this.uiData);
this.uiData.rects = this.rectsToUiData(entry.rects.concat(displayRects));
this.notifyViewCallback(this.uiData);
}
rectToUiData(rects: any[]) {
rectsToUiData(rects: any[]): Rectangle[] {
const uiRects: Rectangle[] = [];
rects.forEach((rect: any) => {
let t = null;
if (rect.transform && rect.transform.matrix) {
@@ -59,7 +58,7 @@ class PresenterSurfaceFlinger extends Presenter {
} else if (rect.transform) {
t = rect.transform;
}
let transform = null;
let transform: RectTransform | null = null;
if (t !== null) {
const matrix: RectMatrix = {
dsdx: t.dsdx,
@@ -96,13 +95,14 @@ class PresenterSurfaceFlinger extends Presenter {
stackId: rect.stackId ?? rect.ref.stackId,
isVirtual: rect.isVirtual
};
this.uiData.rects?.push(newRect);
uiRects.push(newRect);
});
return uiRects;
}
override readonly uiDataCallback: UiDataCallbackType;
override uiData: UiDataSurfaceFlinger;
private readonly notifyViewCallback: NotifyViewCallbackType;
private uiData: UiData;
private highlighted = "";
}
export {PresenterSurfaceFlinger};
export {Presenter};

View File

@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { UiData } from "./ui_data";
class UiDataSurfaceFlinger extends UiData {
class UiData {
constructor(public text: string) {
console.log(text);
}
rects?: Rectangle[] = [];
highlighted?: string = "";
}
@@ -59,4 +60,4 @@ export interface RectMatrix {
ty: number;
}
export {UiDataSurfaceFlinger};
export {UiData};

View File

@@ -22,8 +22,7 @@ import { RectsComponent } from "viewers/rects.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";
import { CanvasService } from "viewers/canvas.service";
import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
describe("ViewerSurfaceFlingerComponent", () => {
let fixture: ComponentFixture<ViewerSurfaceFlingerComponent>;
@@ -33,7 +32,6 @@ describe("ViewerSurfaceFlingerComponent", () => {
beforeAll(async () => {
await TestBed.configureTestingModule({
providers: [
CanvasService,
{ provide: ComponentFixtureAutoDetect, useValue: true }
],
imports: [
@@ -46,7 +44,7 @@ describe("ViewerSurfaceFlingerComponent", () => {
PropertiesComponent,
RectsComponent
],
schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA]
schemas: [CUSTOM_ELEMENTS_SCHEMA]
}).compileComponents();
});

View File

@@ -17,7 +17,7 @@ import {
Component,
Input
} from "@angular/core";
import { UiDataSurfaceFlinger } from "../../ui_data/ui_data_surface_flinger";
import { UiData } from "./ui_data";
import { TRACE_INFO } from "app/trace_info";
import { TraceType } from "common/trace/trace_type";
@@ -29,7 +29,6 @@ import { TraceType } from "common/trace/trace_type";
<rects-view
[rects]="inputData?.rects ?? []"
[highlighted]="inputData?.highlighted ?? ''"
[id]="TraceType.SURFACE_FLINGER"
class="rects-view"
></rects-view>
</mat-card>
@@ -54,7 +53,7 @@ import { TraceType } from "common/trace/trace_type";
})
export class ViewerSurfaceFlingerComponent {
@Input()
inputData?: UiDataSurfaceFlinger;
inputData?: UiData;
TRACE_INFO = TRACE_INFO;
TraceType = TraceType;

View File

@@ -15,16 +15,16 @@
*/
import {TraceType} from "common/trace/trace_type";
import {Viewer} from "viewers/viewer";
import {PresenterSurfaceFlinger} from "../../presenters/presenter_surface_flinger";
import {UiDataSurfaceFlinger} from "../../ui_data/ui_data_surface_flinger";
import {Presenter} from "./presenter";
import {UiData} from "./ui_data";
class ViewerSurfaceFlinger implements Viewer {
constructor() {
this.view = document.createElement("viewer-surface-flinger");
this.presenter = new PresenterSurfaceFlinger((uiData: UiDataSurfaceFlinger) => {
this.presenter = new Presenter((uiData: UiData) => {
(this.view as any).inputData = uiData;
});
this.view.addEventListener("highlightedChange", (event) => this.presenter.updateHighlightedRect(event));
this.view.addEventListener("highlightedChange", (event) => this.presenter.updateHighlightedRect((event as CustomEvent)));
}
public notifyCurrentTraceEntries(entries: Map<TraceType, any>): void {
@@ -35,13 +35,17 @@ class ViewerSurfaceFlinger implements Viewer {
return this.view;
}
public getTitle(): string {
return "Surface Flinger";
}
public getDependencies(): TraceType[] {
return ViewerSurfaceFlinger.DEPENDENCIES;
}
public static readonly DEPENDENCIES: TraceType[] = [TraceType.SURFACE_FLINGER];
private view: HTMLElement;
private presenter: PresenterSurfaceFlinger;
private presenter: Presenter;
}
export {ViewerSurfaceFlinger};

View File

@@ -14,31 +14,31 @@
* limitations under the License.
*/
import {TraceType} from "common/trace/trace_type";
import {UiData} from "../ui_data/ui_data";
import {UiData} from "./ui_data";
type UiDataCallbackType = (uiData: UiData) => void;
type NotifyViewCallbackType = (uiData: UiData) => void;
class Presenter {
constructor(uiDataCallback: UiDataCallbackType) {
this.uiDataCallback = uiDataCallback;
constructor(notifyViewCallback: NotifyViewCallbackType) {
this.notifyViewCallback = notifyViewCallback;
this.uiData = new UiData("Initial UI data");
this.uiDataCallback(this.uiData);
this.notifyViewCallback(this.uiData);
}
public notifyCurrentTraceEntries(entries: Map<TraceType, any>) {
this.uiData = new UiData("UI data selected by user on time scrub");
this.uiDataCallback(this.uiData);
this.notifyViewCallback(this.uiData);
}
public notifyUiEvent() {
const oldUiDataText = this.uiData ? this.uiData.text : "";
this.uiData = new UiData(oldUiDataText);
this.uiData.text += " | UI data updated because of UI event";
this.uiDataCallback(this.uiData!);
this.notifyViewCallback(this.uiData!);
}
readonly uiDataCallback: UiDataCallbackType;
readonly notifyViewCallback: NotifyViewCallbackType;
uiData?: UiData;
}
export {Presenter, UiDataCallbackType};
export {Presenter};

View File

@@ -15,7 +15,7 @@
*/
import {ComponentFixture, TestBed} from "@angular/core/testing";
import {ViewerWindowManagerComponent} from "./viewer_window_manager.component";
import {UiData} from "../../ui_data/ui_data";
import {UiData} from "./ui_data";
describe("ViewerWindowManagerComponent", () => {
let fixture: ComponentFixture<ViewerWindowManagerComponent>;

View File

@@ -19,7 +19,7 @@ import {
Input,
Output
} from "@angular/core";
import {UiData} from "../../ui_data/ui_data";
import {UiData} from "./ui_data";
@Component({
selector: "viewer-window-manager",

View File

@@ -15,8 +15,8 @@
*/
import {TraceType} from "common/trace/trace_type";
import {Viewer} from "viewers/viewer";
import {Presenter} from "../../presenters/presenter";
import {UiData} from "../../ui_data/ui_data";
import {Presenter} from "./presenter";
import {UiData} from "./ui_data";
class ViewerWindowManager implements Viewer {
constructor() {
@@ -27,6 +27,10 @@ class ViewerWindowManager implements Viewer {
this.view.addEventListener("outputEvent", () => this.presenter.notifyUiEvent());
}
public getTitle() {
return "Window Manager";
}
public notifyCurrentTraceEntries(entries: Map<TraceType, any>): void {
this.presenter.notifyCurrentTraceEntries(entries);
}

View File

@@ -7,7 +7,6 @@
"allowJs": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"strictPropertyInitialization": false,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
@@ -17,7 +16,6 @@
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "es2020",