Merge changes Iaf248668,Ic1701cd0,I56274973
* changes: Mediator cleanup Refactor dependency inversion interfaces Remove legacy data and properties view from layer trace root
This commit is contained in:
committed by
Android (Google) Code Review
commit
eadf44ec29
@@ -14,29 +14,29 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
AbtChromeExtensionProtocolDependencyInversion,
|
||||
OnBugAttachmentsDownloadStart,
|
||||
OnBugAttachmentsReceived
|
||||
} from "./abt_chrome_extension_protocol_dependency_inversion";
|
||||
import {
|
||||
MessageType,
|
||||
OpenBuganizerResponse,
|
||||
OpenRequest,
|
||||
WebCommandMessage} from "./messages";
|
||||
import {FunctionUtils} from "common/utils/function_utils";
|
||||
import {
|
||||
BuganizerAttachmentsDownloadEmitter,
|
||||
OnBuganizerAttachmentsDownloadStart,
|
||||
OnBuganizerAttachmentsDownloaded
|
||||
} from "interfaces/buganizer_attachments_download_emitter";
|
||||
|
||||
export class AbtChromeExtensionProtocol implements AbtChromeExtensionProtocolDependencyInversion {
|
||||
export class AbtChromeExtensionProtocol implements BuganizerAttachmentsDownloadEmitter {
|
||||
static readonly ABT_EXTENSION_ID = "mbbaofdfoekifkfpgehgffcpagbbjkmj";
|
||||
private onBugAttachmentsDownloadStart: OnBugAttachmentsDownloadStart = FunctionUtils.DO_NOTHING;
|
||||
private onBugAttachmentsReceived: OnBugAttachmentsReceived = FunctionUtils.DO_NOTHING_ASYNC;
|
||||
private onAttachmentsDownloadStart: OnBuganizerAttachmentsDownloadStart = FunctionUtils.DO_NOTHING;
|
||||
private onttachmentsDownloaded: OnBuganizerAttachmentsDownloaded = FunctionUtils.DO_NOTHING_ASYNC;
|
||||
|
||||
setOnBugAttachmentsDownloadStart(callback: OnBugAttachmentsDownloadStart) {
|
||||
this.onBugAttachmentsDownloadStart = callback;
|
||||
setOnBuganizerAttachmentsDownloadStart(callback: OnBuganizerAttachmentsDownloadStart) {
|
||||
this.onAttachmentsDownloadStart = callback;
|
||||
}
|
||||
|
||||
setOnBugAttachmentsReceived(callback: OnBugAttachmentsReceived) {
|
||||
this.onBugAttachmentsReceived = callback;
|
||||
setOnBuganizerAttachmentsDownloaded(callback: OnBuganizerAttachmentsDownloaded) {
|
||||
this.onttachmentsDownloaded = callback;
|
||||
}
|
||||
|
||||
run() {
|
||||
@@ -45,7 +45,7 @@ export class AbtChromeExtensionProtocol implements AbtChromeExtensionProtocolDep
|
||||
return;
|
||||
}
|
||||
|
||||
this.onBugAttachmentsDownloadStart();
|
||||
this.onAttachmentsDownloadStart();
|
||||
|
||||
const openRequestMessage: OpenRequest = {
|
||||
action: MessageType.OPEN_REQUEST
|
||||
@@ -88,7 +88,7 @@ export class AbtChromeExtensionProtocol implements AbtChromeExtensionProtocolDep
|
||||
});
|
||||
|
||||
const files = await Promise.all(filesBlobPromises);
|
||||
await this.onBugAttachmentsReceived(files);
|
||||
await this.onttachmentsDownloaded(files);
|
||||
}
|
||||
|
||||
private isOpenBuganizerResponseMessage(message: WebCommandMessage):
|
||||
|
||||
@@ -14,23 +14,26 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {FunctionUtils} from "common/utils/function_utils";
|
||||
import {
|
||||
OnBugAttachmentsDownloadStart,
|
||||
OnBugAttachmentsReceived,
|
||||
AbtChromeExtensionProtocolDependencyInversion
|
||||
} from "./abt_chrome_extension_protocol_dependency_inversion";
|
||||
import {FunctionUtils} from "../common/utils/function_utils";
|
||||
BuganizerAttachmentsDownloadEmitter,
|
||||
OnBuganizerAttachmentsDownloadStart,
|
||||
OnBuganizerAttachmentsDownloaded
|
||||
} from "interfaces/buganizer_attachments_download_emitter";
|
||||
import {Runnable} from "interfaces/runnable";
|
||||
|
||||
export class AbtChromeExtensionProtocolStub implements AbtChromeExtensionProtocolDependencyInversion {
|
||||
onBugAttachmentsDownloadStart: OnBugAttachmentsDownloadStart = FunctionUtils.DO_NOTHING;
|
||||
onBugAttachmentsReceived: OnBugAttachmentsReceived = FunctionUtils.DO_NOTHING_ASYNC;
|
||||
export class AbtChromeExtensionProtocolStub implements
|
||||
BuganizerAttachmentsDownloadEmitter,
|
||||
Runnable {
|
||||
onBuganizerAttachmentsDownloadStart: OnBuganizerAttachmentsDownloadStart = FunctionUtils.DO_NOTHING;
|
||||
onBuganizerAttachmentsDownloaded: OnBuganizerAttachmentsDownloaded = FunctionUtils.DO_NOTHING_ASYNC;
|
||||
|
||||
setOnBugAttachmentsDownloadStart(callback: OnBugAttachmentsDownloadStart) {
|
||||
this.onBugAttachmentsDownloadStart = callback;
|
||||
setOnBuganizerAttachmentsDownloadStart(callback: OnBuganizerAttachmentsDownloadStart) {
|
||||
this.onBuganizerAttachmentsDownloadStart = callback;
|
||||
}
|
||||
|
||||
setOnBugAttachmentsReceived(callback: OnBugAttachmentsReceived) {
|
||||
this.onBugAttachmentsReceived = callback;
|
||||
setOnBuganizerAttachmentsDownloaded(callback: OnBuganizerAttachmentsDownloaded) {
|
||||
this.onBuganizerAttachmentsDownloaded = callback;
|
||||
}
|
||||
|
||||
run() {
|
||||
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
ViewEncapsulation
|
||||
} from "@angular/core";
|
||||
import { createCustomElement } from "@angular/elements";
|
||||
import { AppComponentDependencyInversion } from "./app_component_dependency_inversion";
|
||||
import { TimelineComponent} from "./timeline/timeline.component";
|
||||
import {AbtChromeExtensionProtocol} from "abt_chrome_extension/abt_chrome_extension_protocol";
|
||||
import {CrossToolProtocol} from "cross_tool/cross_tool_protocol";
|
||||
@@ -45,6 +44,7 @@ import { TimelineData } from "app/timeline_data";
|
||||
import { TracingConfig } from "trace_collection/tracing_config";
|
||||
import {TRACE_INFO} from "app/trace_info";
|
||||
import {UploadTracesComponent} from "./upload_traces.component";
|
||||
import {TraceDataListener} from "interfaces/trace_data_listener";
|
||||
|
||||
@Component({
|
||||
selector: "app-root",
|
||||
@@ -65,7 +65,7 @@ import {UploadTracesComponent} from "./upload_traces.component";
|
||||
</div>
|
||||
|
||||
<button *ngIf="dataLoaded" color="primary" mat-stroked-button
|
||||
(click)="onUploadNewClick()">
|
||||
(click)="mediator.onWinscopeUploadNew()">
|
||||
Upload New
|
||||
</button>
|
||||
|
||||
@@ -198,7 +198,7 @@ import {UploadTracesComponent} from "./upload_traces.component";
|
||||
],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class AppComponent implements AppComponentDependencyInversion {
|
||||
export class AppComponent implements TraceDataListener {
|
||||
title = "winscope-ng";
|
||||
changeDetectorRef: ChangeDetectorRef;
|
||||
traceData = new TraceData();
|
||||
@@ -285,19 +285,18 @@ export class AppComponent implements AppComponentDependencyInversion {
|
||||
return this.timelineData.getScreenRecordingVideo();
|
||||
}
|
||||
|
||||
public onUploadNewClick() {
|
||||
this.dataLoaded = false;
|
||||
this.mediator.onWinscopeUploadNew();
|
||||
proxyClient.adbData = [];
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
public onTraceDataLoaded(viewers: Viewer[]) {
|
||||
this.viewers = viewers;
|
||||
this.dataLoaded = true;
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
public onTraceDataUnloaded() {
|
||||
proxyClient.adbData = [];
|
||||
this.dataLoaded = false;
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
public setDarkMode(enabled: boolean) {
|
||||
document.body.classList.toggle("dark-mode", enabled);
|
||||
this.store.add("dark-mode", `${enabled}`);
|
||||
|
||||
@@ -14,15 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {AppComponentDependencyInversion} from "./app_component_dependency_inversion";
|
||||
import {Viewer} from "viewers/viewer";
|
||||
import {TraceDataListener} from "interfaces/trace_data_listener";
|
||||
|
||||
export class AppComponentStub implements AppComponentDependencyInversion {
|
||||
export class AppComponentStub implements TraceDataListener {
|
||||
onTraceDataLoaded(viewers: Viewer[]) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
onUploadNewClick() {
|
||||
onTraceDataUnloaded() {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,11 +30,11 @@ import { FormControl, FormGroup, Validators} from "@angular/forms";
|
||||
import { DomSanitizer, SafeUrl } from "@angular/platform-browser";
|
||||
import { TraceType } from "common/trace/trace_type";
|
||||
import { TRACE_INFO } from "app/trace_info";
|
||||
import { TimelineComponentDependencyInversion } from "./timeline_component_dependency_inversion";
|
||||
import { TimelineData } from "app/timeline_data";
|
||||
import { MiniTimelineComponent } from "./mini_timeline.component";
|
||||
import { ElapsedTimestamp, RealTimestamp, Timestamp, TimestampType } from "common/trace/timestamp";
|
||||
import { TimeUtils } from "common/utils/time_utils";
|
||||
import {TimestampChangeListener} from "interfaces/timestamp_change_listener";
|
||||
|
||||
@Component({
|
||||
selector: "timeline",
|
||||
@@ -271,7 +271,7 @@ import { TimeUtils } from "common/utils/time_utils";
|
||||
}
|
||||
`],
|
||||
})
|
||||
export class TimelineComponent implements TimelineComponentDependencyInversion {
|
||||
export class TimelineComponent implements TimestampChangeListener {
|
||||
public readonly TOGGLE_BUTTON_CLASS: string = "button-toggle-expansion";
|
||||
public readonly MAX_SELECTED_TRACES = 3;
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {TimelineComponentDependencyInversion} from "./timeline_component_dependency_inversion";
|
||||
import {Timestamp} from "common/trace/timestamp";
|
||||
import {TimestampChangeListener} from "interfaces/timestamp_change_listener";
|
||||
|
||||
export class TimelineComponentStub implements TimelineComponentDependencyInversion {
|
||||
export class TimelineComponentStub implements TimestampChangeListener {
|
||||
onCurrentTimestampChanged(timestamp: Timestamp|undefined) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@@ -23,12 +23,12 @@ import {
|
||||
Output
|
||||
} from "@angular/core";
|
||||
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||
import {UploadTracesComponentDependencyInversion} from "./upload_traces_component_dependency_inversion";
|
||||
import {TraceData} from "app/trace_data";
|
||||
import {TRACE_INFO} from "app/trace_info";
|
||||
import {Trace, TraceFile} from "common/trace/trace";
|
||||
import {FileUtils, OnFile} from "common/utils/file_utils";
|
||||
import {ParserErrorSnackBarComponent} from "./parser_error_snack_bar_component";
|
||||
import {FilesDownloadListener} from "interfaces/files_download_listener";
|
||||
|
||||
@Component({
|
||||
selector: "upload-traces",
|
||||
@@ -54,7 +54,7 @@ import {ParserErrorSnackBarComponent} from "./parser_error_snack_bar_component";
|
||||
/>
|
||||
|
||||
<load-progress *ngIf="isLoadingFiles"
|
||||
[progressPercentage]="progresPercentage"
|
||||
[progressPercentage]="progressPercentage"
|
||||
[message]="progressMessage">
|
||||
</load-progress>
|
||||
|
||||
@@ -174,11 +174,11 @@ import {ParserErrorSnackBarComponent} from "./parser_error_snack_bar_component";
|
||||
`
|
||||
]
|
||||
})
|
||||
export class UploadTracesComponent implements UploadTracesComponentDependencyInversion {
|
||||
export class UploadTracesComponent implements FilesDownloadListener {
|
||||
TRACE_INFO = TRACE_INFO;
|
||||
isLoadingFiles = false;
|
||||
progressMessage = "";
|
||||
progresPercentage?: number;
|
||||
progressPercentage?: number;
|
||||
|
||||
@Input() traceData!: TraceData;
|
||||
@Output() traceDataLoaded = new EventEmitter<void>();
|
||||
@@ -197,52 +197,19 @@ export class UploadTracesComponent implements UploadTracesComponentDependencyInv
|
||||
public onFilesDownloadStart() {
|
||||
this.isLoadingFiles = true;
|
||||
this.progressMessage = "Downloading files...";
|
||||
this.progresPercentage = undefined;
|
||||
this.progressPercentage = undefined;
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
public async onFilesDownloaded(files: File[]) {
|
||||
await this.processFiles(files);
|
||||
}
|
||||
|
||||
public async onInputFiles(event: Event) {
|
||||
const files = this.getInputFiles(event);
|
||||
await this.processFiles(files);
|
||||
}
|
||||
|
||||
public async processFiles(files: File[]) {
|
||||
const UI_PROGRESS_UPDATE_PERIOD_MS = 200;
|
||||
let lastUiProgressUpdate = Date.now();
|
||||
|
||||
const onProgressUpdate = (progress: number) => {
|
||||
const now = Date.now();
|
||||
if ((Date.now() - lastUiProgressUpdate) < UI_PROGRESS_UPDATE_PERIOD_MS) {
|
||||
// Let's limit the amount of UI updates, because the progress bar component
|
||||
// renders weird stuff when updated too frequently
|
||||
return;
|
||||
}
|
||||
lastUiProgressUpdate = now;
|
||||
|
||||
this.progresPercentage = progress;
|
||||
this.changeDetectorRef.detectChanges();
|
||||
};
|
||||
|
||||
const traceFiles: TraceFile[] = [];
|
||||
const onFile: OnFile = (file: File, parentArchive?: File) => {
|
||||
traceFiles.push(new TraceFile(file, parentArchive));
|
||||
};
|
||||
|
||||
this.isLoadingFiles = true;
|
||||
this.progressMessage = "Unzipping files...";
|
||||
this.changeDetectorRef.detectChanges();
|
||||
await FileUtils.unzipFilesIfNeeded(files, onFile, onProgressUpdate);
|
||||
|
||||
this.progressMessage = "Parsing files...";
|
||||
this.changeDetectorRef.detectChanges();
|
||||
const parserErrors = await this.traceData.loadTraces(traceFiles, onProgressUpdate);
|
||||
|
||||
this.isLoadingFiles = false;
|
||||
this.changeDetectorRef.detectChanges();
|
||||
|
||||
ParserErrorSnackBarComponent.showIfNeeded(this.ngZone, this.snackBar, parserErrors);
|
||||
}
|
||||
|
||||
public onViewTracesButtonClick() {
|
||||
this.traceDataLoaded.emit();
|
||||
}
|
||||
@@ -277,6 +244,43 @@ export class UploadTracesComponent implements UploadTracesComponentDependencyInv
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
private async processFiles(files: File[]) {
|
||||
const UI_PROGRESS_UPDATE_PERIOD_MS = 200;
|
||||
let lastUiProgressUpdate = Date.now();
|
||||
|
||||
const onProgressUpdate = (progress: number) => {
|
||||
const now = Date.now();
|
||||
if ((Date.now() - lastUiProgressUpdate) < UI_PROGRESS_UPDATE_PERIOD_MS) {
|
||||
// Let's limit the amount of UI updates, because the progress bar component
|
||||
// renders weird stuff when updated too frequently
|
||||
return;
|
||||
}
|
||||
lastUiProgressUpdate = now;
|
||||
|
||||
this.progressPercentage = progress;
|
||||
this.changeDetectorRef.detectChanges();
|
||||
};
|
||||
|
||||
const traceFiles: TraceFile[] = [];
|
||||
const onFile: OnFile = (file: File, parentArchive?: File) => {
|
||||
traceFiles.push(new TraceFile(file, parentArchive));
|
||||
};
|
||||
|
||||
this.isLoadingFiles = true;
|
||||
this.progressMessage = "Unzipping files...";
|
||||
this.changeDetectorRef.detectChanges();
|
||||
await FileUtils.unzipFilesIfNeeded(files, onFile, onProgressUpdate);
|
||||
|
||||
this.progressMessage = "Parsing files...";
|
||||
this.changeDetectorRef.detectChanges();
|
||||
const parserErrors = await this.traceData.loadTraces(traceFiles, onProgressUpdate);
|
||||
|
||||
this.isLoadingFiles = false;
|
||||
this.changeDetectorRef.detectChanges();
|
||||
|
||||
ParserErrorSnackBarComponent.showIfNeeded(this.ngZone, this.snackBar, parserErrors);
|
||||
}
|
||||
|
||||
private getInputFiles(event: Event): File[] {
|
||||
const files: FileList | null = (event?.target as HTMLInputElement)?.files;
|
||||
if (!files || !files[0]) {
|
||||
|
||||
@@ -14,14 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {UploadTracesComponentDependencyInversion} from "./upload_traces_component_dependency_inversion";
|
||||
import {FilesDownloadListener} from "interfaces/files_download_listener";
|
||||
|
||||
export class UploadTracesComponentStub implements UploadTracesComponentDependencyInversion {
|
||||
export class UploadTracesComponentStub implements FilesDownloadListener {
|
||||
onFilesDownloadStart() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
async processFiles(files: File[]) {
|
||||
async onFilesDownloaded(files: File[]) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,18 +93,18 @@ describe("Mediator", () => {
|
||||
spyOn(uploadTracesComponent, "onFilesDownloadStart");
|
||||
expect(uploadTracesComponent.onFilesDownloadStart).toHaveBeenCalledTimes(0);
|
||||
|
||||
abtChromeExtensionProtocol.onBugAttachmentsDownloadStart();
|
||||
abtChromeExtensionProtocol.onBuganizerAttachmentsDownloadStart();
|
||||
expect(uploadTracesComponent.onFilesDownloadStart).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("handles empty downloaded files from ABT chrome extension", async () => {
|
||||
spyOn(uploadTracesComponent, "processFiles");
|
||||
expect(uploadTracesComponent.processFiles).toHaveBeenCalledTimes(0);
|
||||
spyOn(uploadTracesComponent, "onFilesDownloaded");
|
||||
expect(uploadTracesComponent.onFilesDownloaded).toHaveBeenCalledTimes(0);
|
||||
|
||||
// Pass files even if empty so that the upload component will update the progress bar
|
||||
// and display error messages
|
||||
await abtChromeExtensionProtocol.onBugAttachmentsReceived([]);
|
||||
expect(uploadTracesComponent.processFiles).toHaveBeenCalledTimes(1);
|
||||
await abtChromeExtensionProtocol.onBuganizerAttachmentsDownloaded([]);
|
||||
expect(uploadTracesComponent.onFilesDownloaded).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("propagates current timestamp changed through timeline", async () => {
|
||||
|
||||
@@ -14,21 +14,29 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {AppComponentDependencyInversion} from "./components/app_component_dependency_inversion";
|
||||
import {TimelineComponentDependencyInversion}
|
||||
from "./components/timeline/timeline_component_dependency_inversion";
|
||||
import {UploadTracesComponentDependencyInversion} from "./components/upload_traces_component_dependency_inversion";
|
||||
import {TimelineData} from "./timeline_data";
|
||||
import {TraceData} from "./trace_data";
|
||||
import {AbtChromeExtensionProtocolDependencyInversion}
|
||||
from "abt_chrome_extension/abt_chrome_extension_protocol_dependency_inversion";
|
||||
import {CrossToolProtocolDependencyInversion}
|
||||
from "cross_tool/cross_tool_protocol_dependency_inversion";
|
||||
import {Timestamp, TimestampType} from "common/trace/timestamp";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {BuganizerAttachmentsDownloadEmitter} from "interfaces/buganizer_attachments_download_emitter";
|
||||
import {FilesDownloadListener} from "interfaces/files_download_listener";
|
||||
import {RemoteBugreportReceiver} from "interfaces/remote_bugreport_receiver";
|
||||
import {RemoteTimestampReceiver} from "interfaces/remote_timestamp_receiver";
|
||||
import {RemoteTimestampSender} from "interfaces/remote_timestamp_sender";
|
||||
import {Runnable} from "interfaces/runnable";
|
||||
import {TimestampChangeListener} from "interfaces/timestamp_change_listener";
|
||||
import {TraceDataListener} from "interfaces/trace_data_listener";
|
||||
import {Viewer} from "viewers/viewer";
|
||||
import {ViewerFactory} from "viewers/viewer_factory";
|
||||
|
||||
export type CrossToolProtocolDependencyInversion =
|
||||
RemoteBugreportReceiver & RemoteTimestampReceiver & RemoteTimestampSender;
|
||||
export type AbtChromeExtensionProtocolDependencyInversion =
|
||||
BuganizerAttachmentsDownloadEmitter & Runnable;
|
||||
export type AppComponentDependencyInversion = TraceDataListener;
|
||||
export type TimelineComponentDependencyInversion = TimestampChangeListener;
|
||||
export type UploadTracesComponentDependencyInversion = FilesDownloadListener;
|
||||
|
||||
export class Mediator {
|
||||
private abtChromeExtensionProtocol: AbtChromeExtensionProtocolDependencyInversion;
|
||||
private crossToolProtocol: CrossToolProtocolDependencyInversion;
|
||||
@@ -64,20 +72,19 @@ export class Mediator {
|
||||
});
|
||||
|
||||
this.crossToolProtocol.setOnBugreportReceived(async (bugreport: File, timestamp?: Timestamp) => {
|
||||
await this.onRemoteToolBugreportReceived(bugreport, timestamp);
|
||||
await this.onRemoteBugreportReceived(bugreport, timestamp);
|
||||
});
|
||||
|
||||
this.crossToolProtocol.setOnTimestampReceived(async (timestamp: Timestamp) => {
|
||||
this.onRemoteToolTimestampReceived(timestamp);
|
||||
this.onRemoteTimestampReceived(timestamp);
|
||||
});
|
||||
|
||||
this.abtChromeExtensionProtocol.setOnBugAttachmentsReceived(async (attachments: File[]) => {
|
||||
await this.onAbtChromeExtensionBugAttachmentsReceived(attachments);
|
||||
this.abtChromeExtensionProtocol.setOnBuganizerAttachmentsDownloadStart(() => {
|
||||
this.onBuganizerAttachmentsDownloadStart();
|
||||
});
|
||||
|
||||
this.abtChromeExtensionProtocol.setOnBugAttachmentsDownloadStart(() => {
|
||||
console.log("Mediator notifying onFilesDownloadStart()");
|
||||
this.uploadTracesComponent?.onFilesDownloadStart();
|
||||
this.abtChromeExtensionProtocol.setOnBuganizerAttachmentsDownloaded(async (attachments: File[]) => {
|
||||
await this.onBuganizerAttachmentsDownloaded(attachments);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -95,21 +102,14 @@ export class Mediator {
|
||||
this.abtChromeExtensionProtocol.run();
|
||||
}
|
||||
|
||||
public onWinscopeUploadNew() {
|
||||
this.resetAppToInitialState();
|
||||
}
|
||||
|
||||
public onWinscopeTraceDataLoaded() {
|
||||
this.processTraceData();
|
||||
}
|
||||
|
||||
public async onRemoteToolBugreportReceived(bugreport: File, timestamp?: Timestamp) {
|
||||
await this.processRemoteFilesReceived([bugreport]);
|
||||
if (timestamp !== undefined) {
|
||||
this.onRemoteToolTimestampReceived(timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
public async onAbtChromeExtensionBugAttachmentsReceived(attachments: File[]) {
|
||||
await this.processRemoteFilesReceived(attachments);
|
||||
}
|
||||
|
||||
public onWinscopeCurrentTimestampChanged(timestamp: Timestamp|undefined) {
|
||||
this.executeIgnoringRecursiveTimestampNotifications(() => {
|
||||
const entries = this.traceData.getTraceEntries(timestamp);
|
||||
@@ -133,7 +133,23 @@ export class Mediator {
|
||||
});
|
||||
}
|
||||
|
||||
public onRemoteToolTimestampReceived(timestamp: Timestamp) {
|
||||
private onBuganizerAttachmentsDownloadStart() {
|
||||
this.resetAppToInitialState();
|
||||
this.uploadTracesComponent?.onFilesDownloadStart();
|
||||
}
|
||||
|
||||
private async onBuganizerAttachmentsDownloaded(attachments: File[]) {
|
||||
await this.processRemoteFilesReceived(attachments);
|
||||
}
|
||||
|
||||
private async onRemoteBugreportReceived(bugreport: File, timestamp?: Timestamp) {
|
||||
await this.processRemoteFilesReceived([bugreport]);
|
||||
if (timestamp !== undefined) {
|
||||
this.onRemoteTimestampReceived(timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
private onRemoteTimestampReceived(timestamp: Timestamp) {
|
||||
this.executeIgnoringRecursiveTimestampNotifications(() => {
|
||||
this.lastRemoteToolTimestampReceived = timestamp;
|
||||
|
||||
@@ -164,15 +180,9 @@ export class Mediator {
|
||||
});
|
||||
}
|
||||
|
||||
public onWinscopeUploadNew() {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
private async processRemoteFilesReceived(files: File[]) {
|
||||
this.appComponent.onUploadNewClick();
|
||||
this.traceData.clear();
|
||||
this.uploadTracesComponent?.processFiles(files); // will notify back "trace data loaded"
|
||||
this.isTraceDataVisualized = false;
|
||||
this.resetAppToInitialState();
|
||||
this.uploadTracesComponent?.onFilesDownloaded(files);
|
||||
}
|
||||
|
||||
private processTraceData() {
|
||||
@@ -185,7 +195,7 @@ export class Mediator {
|
||||
this.isTraceDataVisualized = true;
|
||||
|
||||
if (this.lastRemoteToolTimestampReceived !== undefined) {
|
||||
this.onRemoteToolTimestampReceived(this.lastRemoteToolTimestampReceived);
|
||||
this.onRemoteTimestampReceived(this.lastRemoteToolTimestampReceived);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,12 +221,12 @@ export class Mediator {
|
||||
}
|
||||
}
|
||||
|
||||
private reset() {
|
||||
private resetAppToInitialState() {
|
||||
this.traceData.clear();
|
||||
this.timelineData.clear();
|
||||
this.viewers = [];
|
||||
this.isChangingCurrentTimestamp = false;
|
||||
this.isTraceDataVisualized = false;
|
||||
this.lastRemoteToolTimestampReceived = undefined;
|
||||
this.appComponent.onTraceDataUnloaded();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
toSize, toActiveBuffer, toColor, toColor3, toPoint, toPointF, toRect,
|
||||
toRectF, toRegion, toMatrix22, toTransform, toInsets
|
||||
} from './common';
|
||||
import {ArrayUtils} from "common/utils/array_utils";
|
||||
import { PropertiesDump } from "viewers/common/ui_tree_utils";
|
||||
import intDefMapping from
|
||||
'../../../../../../../prebuilts/misc/common/winscope/intDefMapping.json';
|
||||
@@ -176,6 +177,16 @@ export default class ObjectFormatter {
|
||||
}
|
||||
}
|
||||
|
||||
// Raw long number (no type name, no constructor name, no useful toString() method)
|
||||
if (ArrayUtils.equal(Object.keys(obj).sort(), ["high_", "low_"])) {
|
||||
const high = BigInt(obj.high_) << 32n;
|
||||
let low = BigInt(obj.low_);
|
||||
if (low < 0) {
|
||||
low = -low;
|
||||
}
|
||||
return (high | low).toString();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ import { TimeUtils } from "common/utils/time_utils";
|
||||
import { ElapsedTimestamp, RealTimestamp } from "common/trace/timestamp";
|
||||
|
||||
LayerTraceEntry.fromProto = function (
|
||||
protos: any[],
|
||||
displayProtos: any[],
|
||||
protos: object[],
|
||||
displayProtos: object[],
|
||||
elapsedTimestamp: bigint,
|
||||
vSyncId: number,
|
||||
hwcBlob: string,
|
||||
@@ -49,7 +49,7 @@ LayerTraceEntry.fromProto = function (
|
||||
return entry;
|
||||
}
|
||||
|
||||
function addAttributes(entry: LayerTraceEntry, protos: any, useElapsedTime = false) {
|
||||
function addAttributes(entry: LayerTraceEntry, protos: object[], useElapsedTime = false) {
|
||||
entry.kind = "entry";
|
||||
// Avoid parsing the entry root because it is an array of layers
|
||||
// containing all trace information, this slows down the property tree.
|
||||
|
||||
@@ -16,12 +16,11 @@
|
||||
|
||||
import {Message, MessageBugReport, MessagePong, MessageTimestamp, MessageType} from "./messages";
|
||||
import {OriginAllowList} from "./origin_allow_list";
|
||||
import {
|
||||
CrossToolProtocolDependencyInversion,
|
||||
OnBugreportReceived,
|
||||
OnTimestampReceived} from "cross_tool/cross_tool_protocol_dependency_inversion";
|
||||
import {RealTimestamp} from "common/trace/timestamp";
|
||||
import {FunctionUtils} from "common/utils/function_utils";
|
||||
import {RemoteBugreportReceiver, OnBugreportReceived} from "interfaces/remote_bugreport_receiver";
|
||||
import {RemoteTimestampReceiver, OnTimestampReceived} from "interfaces/remote_timestamp_receiver";
|
||||
import {RemoteTimestampSender} from "interfaces/remote_timestamp_sender";
|
||||
|
||||
class RemoteTool {
|
||||
constructor(
|
||||
@@ -30,7 +29,10 @@ class RemoteTool {
|
||||
}
|
||||
}
|
||||
|
||||
export class CrossToolProtocol implements CrossToolProtocolDependencyInversion {
|
||||
export class CrossToolProtocol implements
|
||||
RemoteBugreportReceiver,
|
||||
RemoteTimestampReceiver,
|
||||
RemoteTimestampSender {
|
||||
private remoteTool?: RemoteTool;
|
||||
private onBugreportReceived: OnBugreportReceived = FunctionUtils.DO_NOTHING_ASYNC;
|
||||
private onTimestampReceived: OnTimestampReceived = FunctionUtils.DO_NOTHING;
|
||||
|
||||
@@ -14,14 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
CrossToolProtocolDependencyInversion,
|
||||
OnBugreportReceived,
|
||||
OnTimestampReceived} from "cross_tool/cross_tool_protocol_dependency_inversion";
|
||||
import {RealTimestamp} from "common/trace/timestamp";
|
||||
import {FunctionUtils} from "common/utils/function_utils";
|
||||
import {RemoteBugreportReceiver, OnBugreportReceived} from "interfaces/remote_bugreport_receiver";
|
||||
import {RemoteTimestampReceiver, OnTimestampReceived} from "interfaces/remote_timestamp_receiver";
|
||||
import {RemoteTimestampSender} from "interfaces/remote_timestamp_sender";
|
||||
|
||||
export class CrossToolProtocolStub implements CrossToolProtocolDependencyInversion {
|
||||
export class CrossToolProtocolStub implements
|
||||
RemoteBugreportReceiver,
|
||||
RemoteTimestampReceiver,
|
||||
RemoteTimestampSender {
|
||||
onBugreportReceived: OnBugreportReceived = FunctionUtils.DO_NOTHING_ASYNC;
|
||||
onTimestampReceived: OnTimestampReceived = FunctionUtils.DO_NOTHING;
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export type OnBuganizerAttachmentsDownloadStart = () => void;
|
||||
export type OnBuganizerAttachmentsDownloaded = (attachments: File[]) => Promise<void>;
|
||||
|
||||
export interface BuganizerAttachmentsDownloadEmitter {
|
||||
setOnBuganizerAttachmentsDownloadStart(callback: OnBuganizerAttachmentsDownloadStart): void;
|
||||
setOnBuganizerAttachmentsDownloaded(callback: OnBuganizerAttachmentsDownloaded): void;
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export interface UploadTracesComponentDependencyInversion {
|
||||
export interface FilesDownloadListener {
|
||||
onFilesDownloadStart(): void;
|
||||
processFiles(files: File[]): Promise<void>;
|
||||
onFilesDownloaded(files: File[]): Promise<void>;
|
||||
}
|
||||
@@ -17,10 +17,7 @@
|
||||
import {RealTimestamp} from "common/trace/timestamp";
|
||||
|
||||
export type OnBugreportReceived = (bugreport: File, timestamp?: RealTimestamp) => Promise<void>;
|
||||
export type OnTimestampReceived = (timestamp: RealTimestamp) => void;
|
||||
|
||||
export interface CrossToolProtocolDependencyInversion {
|
||||
export interface RemoteBugreportReceiver {
|
||||
setOnBugreportReceived(callback: OnBugreportReceived): void;
|
||||
setOnTimestampReceived(callback: OnTimestampReceived): void;
|
||||
sendTimestamp(timestamp: RealTimestamp): void;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {RealTimestamp} from "common/trace/timestamp";
|
||||
|
||||
export type OnTimestampReceived = (timestamp: RealTimestamp) => void;
|
||||
|
||||
export interface RemoteTimestampReceiver {
|
||||
setOnTimestampReceived(callback: OnTimestampReceived): void;
|
||||
}
|
||||
21
tools/winscope-ng/src/interfaces/remote_timestamp_sender.ts
Normal file
21
tools/winscope-ng/src/interfaces/remote_timestamp_sender.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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 {RealTimestamp} from "common/trace/timestamp";
|
||||
|
||||
export interface RemoteTimestampSender {
|
||||
sendTimestamp(timestamp: RealTimestamp): void;
|
||||
}
|
||||
@@ -14,11 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export type OnBugAttachmentsDownloadStart = () => void;
|
||||
export type OnBugAttachmentsReceived = (attachments: File[]) => Promise<void>;
|
||||
|
||||
export interface AbtChromeExtensionProtocolDependencyInversion {
|
||||
setOnBugAttachmentsDownloadStart(callback: OnBugAttachmentsDownloadStart): void;
|
||||
setOnBugAttachmentsReceived(callback: OnBugAttachmentsReceived): void;
|
||||
export interface Runnable {
|
||||
run(): void;
|
||||
}
|
||||
@@ -16,6 +16,6 @@
|
||||
|
||||
import {Timestamp} from "common/trace/timestamp";
|
||||
|
||||
export interface TimelineComponentDependencyInversion {
|
||||
export interface TimestampChangeListener {
|
||||
onCurrentTimestampChanged(timestamp: Timestamp|undefined): void;
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import {Viewer} from "viewers/viewer";
|
||||
|
||||
export interface AppComponentDependencyInversion {
|
||||
export interface TraceDataListener {
|
||||
onTraceDataUnloaded(): void;
|
||||
onTraceDataLoaded(viewers: Viewer[]): void;
|
||||
onUploadNewClick(): void;
|
||||
}
|
||||
@@ -39,7 +39,3 @@ app-root {
|
||||
flex-direction: row;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
viewer-surface-flinger .properties-view .view-header {
|
||||
flex: 3;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ import { PropertiesTreeNode, Terminal} from "viewers/common/ui_tree_utils";
|
||||
@Component({
|
||||
selector: "properties-view",
|
||||
template: `
|
||||
<div class="view-header">
|
||||
<div class="view-header"
|
||||
[class.view-header-with-property-groups]="displayPropertyGroups">
|
||||
<div class="title-filter">
|
||||
<h2 class="properties-title mat-title">Properties</h2>
|
||||
|
||||
@@ -49,7 +50,7 @@ import { PropertiesTreeNode, Terminal} from "viewers/common/ui_tree_utils";
|
||||
</div>
|
||||
|
||||
<property-groups
|
||||
*ngIf="itemIsSelected() && propertyGroups"
|
||||
*ngIf="itemIsSelected() && displayPropertyGroups"
|
||||
class="property-groups"
|
||||
[item]="selectedFlickerItem"
|
||||
></property-groups>
|
||||
@@ -58,7 +59,8 @@ import { PropertiesTreeNode, Terminal} from "viewers/common/ui_tree_utils";
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<div class="properties-content">
|
||||
<h3 *ngIf="objectKeys(propertiesTree).length > 0 && isProtoDump" class="properties-title mat-subheading-2">Properties - Proto Dump</h3>
|
||||
<h3 *ngIf="objectKeys(propertiesTree).length > 0 && isProtoDump"
|
||||
class="properties-title mat-subheading-2">Properties - Proto Dump</h3>
|
||||
|
||||
<div class="tree-wrapper">
|
||||
<tree-view
|
||||
@@ -80,6 +82,10 @@ import { PropertiesTreeNode, Terminal} from "viewers/common/ui_tree_utils";
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.view-header-with-property-groups {
|
||||
flex: 3;
|
||||
}
|
||||
|
||||
.title-filter {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@@ -121,7 +127,7 @@ export class PropertiesComponent {
|
||||
@Input() userOptions: UserOptions = {};
|
||||
@Input() propertiesTree: PropertiesTreeNode = {};
|
||||
@Input() selectedFlickerItem: TraceTreeNode | null = null;
|
||||
@Input() propertyGroups = false;
|
||||
@Input() displayPropertyGroups = false;
|
||||
@Input() isProtoDump = false;
|
||||
|
||||
constructor(
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Component, Inject, Input, Output, ElementRef, EventEmitter, ChangeDetectionStrategy } from "@angular/core";
|
||||
import {Component, Inject, Input, Output, ElementRef, EventEmitter, ChangeDetectionStrategy} from "@angular/core";
|
||||
import { PersistentStore } from "common/utils/persistent_store";
|
||||
import { nodeStyles, treeNodeDataViewStyles } from "viewers/components/styles/node.styles";
|
||||
import { UiTreeUtils, UiTreeNode, HierarchyTreeNode, PropertiesTreeNode } from "viewers/common/ui_tree_utils";
|
||||
import { UiTreeUtils, UiTreeNode, HierarchyTreeNode } from "viewers/common/ui_tree_utils";
|
||||
import { TraceType } from "common/trace/trace_type";
|
||||
|
||||
@Component({
|
||||
@@ -76,8 +76,12 @@ export class TreeComponent {
|
||||
diffClass = UiTreeUtils.diffClass;
|
||||
isHighlighted = UiTreeUtils.isHighlighted;
|
||||
|
||||
@Input() item?: UiTreeNode;
|
||||
// TODO (b/263779536): this array is passed down from viewers/presenters and is used to generate
|
||||
// an identifier supposed to be unique for each viewer. Let's just use a proper identifier
|
||||
// instead. Each viewer/presenter could pass down a random magic number, an UUID, ...
|
||||
@Input() dependencies: Array<TraceType> = [];
|
||||
|
||||
@Input() item?: UiTreeNode;
|
||||
@Input() store!: PersistentStore;
|
||||
@Input() isFlattened? = false;
|
||||
@Input() initialDepth = 0;
|
||||
@@ -117,7 +121,7 @@ export class TreeComponent {
|
||||
}
|
||||
|
||||
constructor(
|
||||
@Inject(ElementRef) public elementRef: ElementRef,
|
||||
@Inject(ElementRef) public elementRef: ElementRef
|
||||
) {
|
||||
this.nodeElement = elementRef.nativeElement.querySelector(".node");
|
||||
this.nodeElement?.addEventListener("mousedown", this.nodeMouseDownEventListener);
|
||||
@@ -168,18 +172,14 @@ export class TreeComponent {
|
||||
}
|
||||
|
||||
private updateHighlightedItems() {
|
||||
if (this.item instanceof HierarchyTreeNode) {
|
||||
if (this.item.stableId) {
|
||||
this.highlightedItemChange.emit(`${this.item.stableId}`);
|
||||
} else if (!this.item.stableId) {
|
||||
//this.selectedTreeChange.emit(this.item);
|
||||
}
|
||||
if (this.item?.stableId) {
|
||||
this.highlightedItemChange.emit(`${this.item.stableId}`);
|
||||
}
|
||||
}
|
||||
|
||||
public isPinned() {
|
||||
if (this.item instanceof HierarchyTreeNode) {
|
||||
return this.pinnedItems?.map(item => `${item.id}`).includes(`${this.item.id}`);
|
||||
return this.pinnedItems?.map(item => `${item.stableId}`).includes(`${this.item.stableId}`);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
@@ -31,7 +30,7 @@ export class Presenter {
|
||||
constructor(notifyViewCallback: NotifyViewCallbackType, private storage: Storage) {
|
||||
this.notifyViewCallback = notifyViewCallback;
|
||||
this.uiData = new UiData([TraceType.SURFACE_FLINGER]);
|
||||
this.notifyViewCallback(this.uiData);
|
||||
this.copyUiDataAndNotifyView();
|
||||
}
|
||||
|
||||
public updatePinnedItems(pinnedItem: HierarchyTreeNode) {
|
||||
@@ -43,7 +42,7 @@ export class Presenter {
|
||||
}
|
||||
this.updatePinnedIds(pinnedId);
|
||||
this.uiData.pinnedItems = this.pinnedItems;
|
||||
this.notifyViewCallback(this.uiData);
|
||||
this.copyUiDataAndNotifyView();
|
||||
}
|
||||
|
||||
public updateHighlightedItems(id: string) {
|
||||
@@ -54,20 +53,20 @@ export class Presenter {
|
||||
this.highlightedItems.push(id);
|
||||
}
|
||||
this.uiData.highlightedItems = this.highlightedItems;
|
||||
this.notifyViewCallback(this.uiData);
|
||||
this.copyUiDataAndNotifyView();
|
||||
}
|
||||
|
||||
public updateHierarchyTree(userOptions: UserOptions) {
|
||||
this.hierarchyUserOptions = userOptions;
|
||||
this.uiData.hierarchyUserOptions = this.hierarchyUserOptions;
|
||||
this.uiData.tree = this.generateTree();
|
||||
this.notifyViewCallback(this.uiData);
|
||||
this.copyUiDataAndNotifyView();
|
||||
}
|
||||
|
||||
public filterHierarchyTree(filterString: string) {
|
||||
this.hierarchyFilter = TreeUtils.makeNodeFilter(filterString);
|
||||
this.uiData.tree = this.generateTree();
|
||||
this.notifyViewCallback(this.uiData);
|
||||
this.copyUiDataAndNotifyView();
|
||||
}
|
||||
|
||||
public updatePropertiesTree(userOptions: UserOptions) {
|
||||
@@ -101,7 +100,7 @@ export class Presenter {
|
||||
this.uiData.tree = this.generateTree();
|
||||
}
|
||||
}
|
||||
this.notifyViewCallback(this.uiData);
|
||||
this.copyUiDataAndNotifyView();
|
||||
}
|
||||
|
||||
private generateRects(): Rectangle[] {
|
||||
@@ -162,8 +161,9 @@ export class Presenter {
|
||||
if (this.selectedHierarchyTree) {
|
||||
this.uiData.propertiesTree = this.getTreeWithTransformedProperties(this.selectedHierarchyTree);
|
||||
this.uiData.selectedLayer = this.selectedLayer;
|
||||
this.uiData.displayPropertyGroups = this.shouldDisplayPropertyGroups(this.selectedLayer);
|
||||
}
|
||||
this.notifyViewCallback(this.uiData);
|
||||
this.copyUiDataAndNotifyView();
|
||||
}
|
||||
|
||||
private generateTree() {
|
||||
@@ -253,6 +253,20 @@ export class Presenter {
|
||||
return transformedTree;
|
||||
}
|
||||
|
||||
private shouldDisplayPropertyGroups(selectedLayer: Layer): boolean {
|
||||
// Do not display property groups when the root layer is selected. The root layer doesn't
|
||||
// provide property groups info (visibility, geometry transforms, ...).
|
||||
const isRoot = selectedLayer === this.entry;
|
||||
return !isRoot;
|
||||
}
|
||||
|
||||
private copyUiDataAndNotifyView() {
|
||||
// Create a shallow copy of the data, otherwise the Angular OnPush change detection strategy
|
||||
// won't detect the new input
|
||||
const copy = Object.assign({}, this.uiData);
|
||||
this.notifyViewCallback(copy);
|
||||
}
|
||||
|
||||
private readonly notifyViewCallback: NotifyViewCallbackType;
|
||||
private uiData: UiData;
|
||||
private hierarchyFilter: FilterType = TreeUtils.makeNodeFilter("");
|
||||
|
||||
@@ -30,6 +30,7 @@ export class UiData {
|
||||
tree: HierarchyTreeNode | null = null;
|
||||
propertiesTree: PropertiesTreeNode | null = null;
|
||||
selectedLayer: Layer = {};
|
||||
displayPropertyGroups = true;
|
||||
|
||||
constructor(dependencies?: Array<TraceType>) {
|
||||
this.dependencies = dependencies ?? [];
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
Input,
|
||||
} from "@angular/core";
|
||||
@@ -24,6 +25,7 @@ import { PersistentStore } from "common/utils/persistent_store";
|
||||
|
||||
@Component({
|
||||
selector: "viewer-surface-flinger",
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<div class="card-grid">
|
||||
<rects-view
|
||||
@@ -49,7 +51,7 @@ import { PersistentStore } from "common/utils/persistent_store";
|
||||
[userOptions]="inputData?.propertiesUserOptions ?? {}"
|
||||
[propertiesTree]="inputData?.propertiesTree ?? {}"
|
||||
[selectedFlickerItem]="inputData?.selectedLayer ?? {}"
|
||||
[propertyGroups]="true"
|
||||
[displayPropertyGroups]="inputData?.displayPropertyGroups"
|
||||
[isProtoDump]="true"
|
||||
></properties-view>
|
||||
</div>
|
||||
@@ -67,7 +69,7 @@ import { PersistentStore } from "common/utils/persistent_store";
|
||||
]
|
||||
})
|
||||
export class ViewerSurfaceFlingerComponent {
|
||||
@Input() inputData: UiData | null = null;
|
||||
@Input() inputData?: UiData;
|
||||
@Input() store: PersistentStore = new PersistentStore();
|
||||
@Input() active = false;
|
||||
TRACE_INFO = TRACE_INFO;
|
||||
|
||||
@@ -13,22 +13,24 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {View, Viewer, ViewType} from "viewers/viewer";
|
||||
import {Presenter} from "./presenter";
|
||||
import {UiData} from "./ui_data";
|
||||
import {TraceType} from "common/trace/trace_type";
|
||||
import {View, Viewer, ViewType} from "viewers/viewer";
|
||||
import {ViewerEvents} from "viewers/common/viewer_events";
|
||||
|
||||
class ViewerSurfaceFlinger implements Viewer {
|
||||
public static readonly DEPENDENCIES: TraceType[] = [TraceType.SURFACE_FLINGER];
|
||||
private htmlElement: HTMLElement;
|
||||
private presenter: Presenter;
|
||||
|
||||
constructor(storage: Storage) {
|
||||
this.htmlElement = document.createElement("viewer-surface-flinger");
|
||||
|
||||
this.presenter = new Presenter((uiData: UiData) => {
|
||||
// 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.htmlElement as any).inputData = null;
|
||||
(this.htmlElement as any).inputData = uiData;
|
||||
}, storage);
|
||||
|
||||
this.htmlElement.addEventListener(ViewerEvents.HierarchyPinnedChange, (event) => this.presenter.updatePinnedItems(((event as CustomEvent).detail.pinnedItem)));
|
||||
this.htmlElement.addEventListener(ViewerEvents.HighlightedChange, (event) => this.presenter.updateHighlightedItems(`${(event as CustomEvent).detail.id}`));
|
||||
this.htmlElement.addEventListener(ViewerEvents.HierarchyUserOptionsChange, (event) => this.presenter.updateHierarchyTree((event as CustomEvent).detail.userOptions));
|
||||
@@ -49,10 +51,6 @@ class ViewerSurfaceFlinger implements Viewer {
|
||||
public getDependencies(): TraceType[] {
|
||||
return ViewerSurfaceFlinger.DEPENDENCIES;
|
||||
}
|
||||
|
||||
public static readonly DEPENDENCIES: TraceType[] = [TraceType.SURFACE_FLINGER];
|
||||
private htmlElement: HTMLElement;
|
||||
private presenter: Presenter;
|
||||
}
|
||||
|
||||
export {ViewerSurfaceFlinger};
|
||||
|
||||
@@ -65,7 +65,7 @@ import { PersistentStore } from "common/utils/persistent_store";
|
||||
]
|
||||
})
|
||||
export class ViewerWindowManagerComponent {
|
||||
@Input() inputData: UiData | null = null;
|
||||
@Input() inputData?: UiData;
|
||||
@Input() store: PersistentStore = new PersistentStore();
|
||||
@Input() active = false;
|
||||
TRACE_INFO = TRACE_INFO;
|
||||
|
||||
Reference in New Issue
Block a user