Merge changes I7dddbb10,I47e6dc88

* changes:
  Add link to legacy Winscope
  Show progress bar while downloading from ABT chrome extension
This commit is contained in:
Kean Mariotti
2022-12-20 17:10:48 +00:00
committed by Android (Google) Code Review
12 changed files with 164 additions and 49 deletions

View File

@@ -16,6 +16,7 @@
import {
AbtChromeExtensionProtocolDependencyInversion,
OnBugAttachmentsDownloadStart,
OnBugAttachmentsReceived
} from "./abt_chrome_extension_protocol_dependency_inversion";
import {
@@ -27,7 +28,12 @@ import {FunctionUtils} from "common/utils/function_utils";
export class AbtChromeExtensionProtocol implements AbtChromeExtensionProtocolDependencyInversion {
static readonly ABT_EXTENSION_ID = "mbbaofdfoekifkfpgehgffcpagbbjkmj";
onBugAttachmentsReceived: OnBugAttachmentsReceived = FunctionUtils.DO_NOTHING_ASYNC;
private onBugAttachmentsDownloadStart: OnBugAttachmentsDownloadStart = FunctionUtils.DO_NOTHING;
private onBugAttachmentsReceived: OnBugAttachmentsReceived = FunctionUtils.DO_NOTHING_ASYNC;
setOnBugAttachmentsDownloadStart(callback: OnBugAttachmentsDownloadStart) {
this.onBugAttachmentsDownloadStart = callback;
}
setOnBugAttachmentsReceived(callback: OnBugAttachmentsReceived) {
this.onBugAttachmentsReceived = callback;
@@ -39,6 +45,8 @@ export class AbtChromeExtensionProtocol implements AbtChromeExtensionProtocolDep
return;
}
this.onBugAttachmentsDownloadStart();
const openRequestMessage: OpenRequest = {
action: MessageType.OPEN_REQUEST
};
@@ -65,7 +73,6 @@ export class AbtChromeExtensionProtocol implements AbtChromeExtensionProtocolDep
if (message.attachments.length === 0) {
console.warn("ABT chrome extension protocol received no attachments");
return;
}
const filesBlobPromises = message.attachments.map(async attachment => {

View File

@@ -14,9 +14,11 @@
* 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;
run(): void;
}

View File

@@ -15,13 +15,22 @@
*/
import {
OnBugAttachmentsDownloadStart,
OnBugAttachmentsReceived,
AbtChromeExtensionProtocolDependencyInversion
} from "./abt_chrome_extension_protocol_dependency_inversion";
import {FunctionUtils} from "../common/utils/function_utils";
export class AbtChromeExtensionProtocolStub implements AbtChromeExtensionProtocolDependencyInversion {
onBugAttachmentsDownloadStart: OnBugAttachmentsDownloadStart = FunctionUtils.DO_NOTHING;
onBugAttachmentsReceived: OnBugAttachmentsReceived = FunctionUtils.DO_NOTHING_ASYNC;
setOnBugAttachmentsDownloadStart(callback: OnBugAttachmentsDownloadStart) {
this.onBugAttachmentsDownloadStart = callback;
}
setOnBugAttachmentsReceived(callback: OnBugAttachmentsReceived) {
// do nothing
this.onBugAttachmentsReceived = callback;
}
run() {

View File

@@ -52,6 +52,12 @@ import {UploadTracesComponent} from "./upload_traces.component";
<mat-toolbar class="toolbar">
<span class="app-title">Winscope</span>
<a href="http://go/winscope-legacy">
<button color="primary" mat-button>
Open legacy Winscope
</button>
</a>
<div class="spacer"></div>
<button *ngIf="dataLoaded" color="primary" mat-stroked-button
@@ -251,8 +257,12 @@ export class AppComponent implements AppComponentDependencyInversion {
TracingConfig.getInstance().initialize(localStorage);
}
ngAfterViewChecked() {
ngAfterViewInit() {
this.mediator.setUploadTracesComponent(this.uploadTracesComponent);
this.mediator.onWinscopeInitialized();
}
ngAfterViewChecked() {
this.mediator.setTimelineComponent(this.timelineComponent);
}

View File

@@ -25,7 +25,11 @@ import {Component, Input} from "@angular/core";
</mat-icon>
</p>
<mat-progress-bar mode="determinate"
<mat-progress-bar *ngIf="progressPercentage === undefined"
mode="indeterminate">
</mat-progress-bar>
<mat-progress-bar *ngIf="progressPercentage !== undefined"
mode="determinate"
[value]="progressPercentage">
</mat-progress-bar>
@@ -60,6 +64,6 @@ import {Component, Input} from "@angular/core";
]
})
export class LoadProgressComponent {
@Input() progressPercentage = 0;
@Input() progressPercentage?: number;
@Input() message = "Loading...";
}

View File

@@ -15,8 +15,8 @@
*/
import {Component, Inject, NgZone} from "@angular/core";
import {MAT_SNACK_BAR_DATA, MatSnackBar, MatSnackBarRef} from "@angular/material/snack-bar";
import {TRACE_INFO} from "app/trace_info";
import {ParserError, ParserErrorType} from "parsers/parser_factory";
import {TRACE_INFO} from "app/trace_info";
@Component({
selector: "upload-snack-bar",
@@ -89,14 +89,22 @@ export class ParserErrorSnackBarComponent {
}
private static convertErrorToMessage(error: ParserError): string {
let message = `${error.trace.name}: unknown error occurred`;
if (error.type === ParserErrorType.OVERRIDE && error.traceType) {
message = `${error.trace.name}:` +
` overridden by another trace of type ${TRACE_INFO[error.traceType].name}`;
} else if (error.type === ParserErrorType.UNSUPPORTED_FORMAT) {
message = `${error.trace.name}: unsupported file format`;
const fileName = error.trace !== undefined ?
error.trace.name : "<no file name>";
const traceTypeName = error.traceType !== undefined ?
TRACE_INFO[error.traceType].name : "<unknown>";
switch (error.type) {
case ParserErrorType.NO_INPUT_FILES:
return "No input files";
case ParserErrorType.UNSUPPORTED_FORMAT:
return `${fileName}: unsupported file format`;
case ParserErrorType.OVERRIDE: {
return `${fileName}: overridden by another trace of type ${traceTypeName}`;
}
default:
return `${fileName}: unknown error occurred`;
}
return message;
}
private static makeCroppedMessage(type: ParserErrorType, count: number): string {

View File

@@ -37,29 +37,30 @@ import {ParserErrorSnackBarComponent} from "./parser_error_snack_bar_component";
<mat-card-title class="title">Upload Traces</mat-card-title>
<mat-card-content
class="drop-box"
ref="drop-box"
(dragleave)="onFileDragOut($event)"
(dragover)="onFileDragIn($event)"
(drop)="onHandleFileDrop($event)"
(click)="fileDropRef.click()"
class="drop-box"
ref="drop-box"
(dragleave)="onFileDragOut($event)"
(dragover)="onFileDragIn($event)"
(drop)="onHandleFileDrop($event)"
(click)="fileDropRef.click()"
>
<input
id="fileDropRef"
hidden
type="file"
multiple
#fileDropRef
(change)="onInputFiles($event)"
id="fileDropRef"
hidden
type="file"
multiple
#fileDropRef
(change)="onInputFiles($event)"
/>
<load-progress *ngIf="isLoadingFiles"
[progressPercentage]="loadFilesProgress"
[message]="loadFilesMessage">
[progressPercentage]="progresPercentage"
[message]="progressMessage">
</load-progress>
<mat-list *ngIf="!isLoadingFiles && this.traceData.getLoadedTraces().length > 0"
class="uploaded-files">
<mat-list
*ngIf="!isLoadingFiles && this.traceData.getLoadedTraces().length > 0"
class="uploaded-files">
<mat-list-item *ngFor="let trace of this.traceData.getLoadedTraces()">
<mat-icon matListIcon>
{{TRACE_INFO[trace.type].icon}}
@@ -69,13 +70,15 @@ import {ParserErrorSnackBarComponent} from "./parser_error_snack_bar_component";
{{trace.file.name}} ({{TRACE_INFO[trace.type].name}})
</p>
<button color="primary" mat-icon-button (click)="onRemoveTrace($event, trace)">
<button color="primary" mat-icon-button
(click)="onRemoveTrace($event, trace)">
<mat-icon>close</mat-icon>
</button>
</mat-list-item>
</mat-list>
<div *ngIf="!isLoadingFiles && traceData.getLoadedTraces().length === 0" class="drop-info">
<div *ngIf="!isLoadingFiles && traceData.getLoadedTraces().length === 0"
class="drop-info">
<p class="mat-body-3 icon">
<mat-icon inline fontIcon="upload"></mat-icon>
</p>
@@ -85,16 +88,20 @@ import {ParserErrorSnackBarComponent} from "./parser_error_snack_bar_component";
</div>
</mat-card-content>
<div *ngIf="!isLoadingFiles && traceData.getLoadedTraces().length > 0" class="trace-actions-container">
<button color="primary" mat-raised-button class="load-btn" (click)="onViewTracesButtonClick()">
<div *ngIf="!isLoadingFiles && traceData.getLoadedTraces().length > 0"
class="trace-actions-container">
<button color="primary" mat-raised-button class="load-btn"
(click)="onViewTracesButtonClick()">
View traces
</button>
<button color="primary" mat-stroked-button for="fileDropRef" (click)="fileDropRef.click()">
<button color="primary" mat-stroked-button for="fileDropRef"
(click)="fileDropRef.click()">
Upload another file
</button>
<button color="primary" mat-stroked-button (click)="onClearButtonClick()">
<button color="primary" mat-stroked-button
(click)="onClearButtonClick()">
Clear all
</button>
</div>
@@ -170,8 +177,8 @@ import {ParserErrorSnackBarComponent} from "./parser_error_snack_bar_component";
export class UploadTracesComponent implements UploadTracesComponentDependencyInversion {
TRACE_INFO = TRACE_INFO;
isLoadingFiles = false;
loadFilesMessage = "Unzipping files...";
loadFilesProgress = 0;
progressMessage = "";
progresPercentage?: number;
@Input() traceData!: TraceData;
@Output() traceDataLoaded = new EventEmitter<void>();
@@ -187,6 +194,13 @@ export class UploadTracesComponent implements UploadTracesComponentDependencyInv
this.traceData.clear();
}
public onFilesDownloadStart() {
this.isLoadingFiles = true;
this.progressMessage = "Downloading files...";
this.progresPercentage = undefined;
this.changeDetectorRef.detectChanges();
}
public async onInputFiles(event: Event) {
const files = this.getInputFiles(event);
await this.processFiles(files);
@@ -205,16 +219,16 @@ export class UploadTracesComponent implements UploadTracesComponentDependencyInv
}
lastUiProgressUpdate = now;
this.loadFilesProgress = progress;
this.progresPercentage = progress;
this.changeDetectorRef.detectChanges();
};
this.isLoadingFiles = true;
this.loadFilesMessage = "Unzipping files...";
this.progressMessage = "Unzipping files...";
this.changeDetectorRef.detectChanges();
const unzippedFiles = await FileUtils.unzipFilesIfNeeded(files, onProgressUpdate);
this.loadFilesMessage = "Parsing files...";
this.progressMessage = "Parsing files...";
this.changeDetectorRef.detectChanges();
const parserErrors = await this.traceData.loadTraces(unzippedFiles, onProgressUpdate);

View File

@@ -15,5 +15,6 @@
*/
export interface UploadTracesComponentDependencyInversion {
onFilesDownloadStart(): void;
processFiles(files: File[]): Promise<void>;
}

View File

@@ -0,0 +1,27 @@
/*
* 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 {UploadTracesComponentDependencyInversion} from "./upload_traces_component_dependency_inversion";
export class UploadTracesComponentStub implements UploadTracesComponentDependencyInversion {
onFilesDownloadStart() {
// do nothing
}
async processFiles(files: File[]) {
// do nothing
}
}

View File

@@ -16,6 +16,7 @@
import {AppComponentStub} from "./components/app_component_stub";
import {TimelineComponentStub} from "./components/timeline/timeline_component_stub";
import {UploadTracesComponentStub} from "./components/upload_traces_component_stub";
import {Mediator} from "./mediator";
import {AbtChromeExtensionProtocolStub} from "abt_chrome_extension/abt_chrome_extension_protocol_stub";
import {CrossToolProtocolStub} from "cross_tool/cross_tool_protocol_stub";
@@ -35,6 +36,7 @@ describe("Mediator", () => {
let crossToolProtocol: CrossToolProtocolStub;
let appComponent: AppComponentStub;
let timelineComponent: TimelineComponentStub;
let uploadTracesComponent: UploadTracesComponentStub;
let mediator: Mediator;
const TIMESTAMP_10 = new RealTimestamp(10n);
@@ -48,6 +50,7 @@ describe("Mediator", () => {
crossToolProtocol = new CrossToolProtocolStub();
appComponent = new AppComponentStub();
timelineComponent = new TimelineComponentStub();
uploadTracesComponent = new UploadTracesComponentStub();
mediator = new Mediator(
traceData,
timelineData,
@@ -57,6 +60,7 @@ describe("Mediator", () => {
new MockStorage()
);
mediator.setTimelineComponent(timelineComponent);
mediator.setUploadTracesComponent(uploadTracesComponent);
spyOn(ViewerFactory.prototype, "createViewers").and.returnValue([viewerStub]);
});
@@ -84,6 +88,24 @@ describe("Mediator", () => {
//TODO: test "data from ABT chrome extension" when FileUtils is fully compatible with Node.js
// (b/262269229).
it("handles start download event from ABT chrome extension", () => {
spyOn(uploadTracesComponent, "onFilesDownloadStart");
expect(uploadTracesComponent.onFilesDownloadStart).toHaveBeenCalledTimes(0);
abtChromeExtensionProtocol.onBugAttachmentsDownloadStart();
expect(uploadTracesComponent.onFilesDownloadStart).toHaveBeenCalledTimes(1);
});
it("handles empty downloaded files from ABT chrome extension", async () => {
spyOn(uploadTracesComponent, "processFiles");
expect(uploadTracesComponent.processFiles).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);
});
it("propagates current timestamp changed through timeline", async () => {
await loadTraces();
mediator.onWinscopeTraceDataLoaded();

View File

@@ -74,7 +74,11 @@ export class Mediator {
this.abtChromeExtensionProtocol.setOnBugAttachmentsReceived(async (attachments: File[]) => {
await this.onAbtChromeExtensionBugAttachmentsReceived(attachments);
});
this.abtChromeExtensionProtocol.run();
this.abtChromeExtensionProtocol.setOnBugAttachmentsDownloadStart(() => {
console.log("Mediator notifying onFilesDownloadStart()");
this.uploadTracesComponent?.onFilesDownloadStart();
});
}
public setUploadTracesComponent(
@@ -87,6 +91,10 @@ export class Mediator {
this.timelineComponent = timelineComponent;
}
public onWinscopeInitialized() {
this.abtChromeExtensionProtocol.run();
}
public onWinscopeTraceDataLoaded() {
this.processTraceData();
}

View File

@@ -29,7 +29,7 @@ import {ParserTransactions} from "./parser_transactions";
import {ParserWindowManager} from "./parser_window_manager";
import {ParserWindowManagerDump} from "./parser_window_manager_dump";
class ParserFactory {
export class ParserFactory {
static readonly PARSERS = [
ParserAccessibility,
ParserInputMethodClients,
@@ -52,6 +52,10 @@ class ParserFactory {
Promise<[Parser[], ParserError[]]> {
const errors: ParserError[] = [];
if (traces.length === 0) {
errors.push(new ParserError(ParserErrorType.NO_INPUT_FILES));
}
for (const [index, trace] of traces.entries()) {
let hasFoundParser = false;
@@ -72,7 +76,7 @@ class ParserFactory {
if (!hasFoundParser) {
console.log(`Failed to load trace ${trace.name}`);
errors.push(new ParserError(trace, ParserErrorType.UNSUPPORTED_FORMAT));
errors.push(new ParserError(ParserErrorType.UNSUPPORTED_FORMAT, trace));
}
onProgressUpdate(100 * (index + 1) / traces.length);
@@ -95,7 +99,7 @@ class ParserFactory {
);
errors.push(
new ParserError(
oldParser.getTrace().file, ParserErrorType.OVERRIDE, oldParser.getTraceType()
ParserErrorType.OVERRIDE, oldParser.getTrace().file, oldParser.getTraceType()
)
);
return true;
@@ -107,7 +111,7 @@ class ParserFactory {
);
errors.push(
new ParserError(
newParser.getTrace().file, ParserErrorType.OVERRIDE, newParser.getTraceType()
ParserErrorType.OVERRIDE, newParser.getTrace().file, newParser.getTraceType()
)
);
return false;
@@ -115,16 +119,15 @@ class ParserFactory {
}
export enum ParserErrorType {
NO_INPUT_FILES,
UNSUPPORTED_FORMAT,
OVERRIDE
}
export class ParserError {
constructor(
public trace: File,
public type: ParserErrorType,
public trace: File|undefined = undefined,
public traceType: TraceType|undefined = undefined) {
}
}
export {ParserFactory};