Merge changes I7dddbb10,I47e6dc88
* changes: Add link to legacy Winscope Show progress bar while downloading from ABT chrome extension
This commit is contained in:
committed by
Android (Google) Code Review
commit
757e55ef0d
@@ -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 => {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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...";
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -15,5 +15,6 @@
|
||||
*/
|
||||
|
||||
export interface UploadTracesComponentDependencyInversion {
|
||||
onFilesDownloadStart(): void;
|
||||
processFiles(files: File[]): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
Reference in New Issue
Block a user