/* * 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 { ChangeDetectorRef, Component, EventEmitter, Inject, Input, NgZone, Output, } from '@angular/core'; import {TracePipeline} from 'app/trace_pipeline'; import {ProgressListener} from 'messaging/progress_listener'; import {Trace} from 'trace/trace'; import {TRACE_INFO} from 'trace/trace_info'; import {TraceTypeUtils} from 'trace/trace_type'; import {LoadProgressComponent} from './load_progress_component'; @Component({ selector: 'upload-traces', template: `
Upload Traces
{{ TRACE_INFO[trace.type].icon }}

{{ TRACE_INFO[trace.type].name }}

{{ descriptor }}

info warning

Drag your .winscope file(s) or click to upload

`, styles: [ ` .upload-card { height: 100%; display: flex; flex-direction: column; overflow: auto; margin: 10px; padding-top: 0px; } .card-header { justify-content: space-between; align-items: center; display: flex; flex-direction: row; } .title { padding-top: 16px; text-align: center; } .trace-actions-container { display: flex; flex-direction: row; flex-wrap: wrap; gap: 10px; } .drop-box { display: flex; flex-direction: column; overflow: auto; border: 2px dashed var(--border-color); cursor: pointer; } .uploaded-files { flex: 400px; padding: 0; } .drop-info { flex: 400px; display: flex; flex-direction: column; justify-content: center; align-items: center; pointer-events: none; } .drop-info p { opacity: 0.6; font-size: 1.2rem; } .drop-info .icon { font-size: 3rem; margin: 0; } .div-progress { display: flex; height: 100%; flex-direction: column; justify-content: center; align-content: center; align-items: center; } .div-progress p { opacity: 0.6; } .div-progress mat-icon { font-size: 3rem; width: unset; height: unset; } .div-progress mat-progress-bar { max-width: 250px; } mat-card-content { flex-grow: 1; } .no-visualization { background-color: var(--warning-background-color); } .info-icon, .warning-icon { flex-shrink: 0; } `, ], }) export class UploadTracesComponent implements ProgressListener { TRACE_INFO = TRACE_INFO; isLoadingFiles = false; progressMessage = ''; progressPercentage?: number; lastUiProgressUpdateTimeMs?: number; @Input() tracePipeline!: TracePipeline; @Output() filesUploaded = new EventEmitter(); @Output() viewTracesButtonClick = new EventEmitter(); constructor( @Inject(ChangeDetectorRef) private changeDetectorRef: ChangeDetectorRef, @Inject(NgZone) private ngZone: NgZone, ) {} ngOnInit() { this.tracePipeline.clear(); } onProgressUpdate( message: string | undefined, progressPercentage: number | undefined, ) { if ( !LoadProgressComponent.canUpdateComponent(this.lastUiProgressUpdateTimeMs) ) { return; } this.isLoadingFiles = true; this.progressMessage = message ? message : 'Loading...'; this.progressPercentage = progressPercentage; this.lastUiProgressUpdateTimeMs = Date.now(); this.changeDetectorRef.detectChanges(); } onOperationFinished() { this.isLoadingFiles = false; this.lastUiProgressUpdateTimeMs = undefined; this.changeDetectorRef.detectChanges(); } onInputFiles(event: Event) { const files = this.getInputFiles(event); this.filesUploaded.emit(files); } onViewTracesButtonClick() { this.viewTracesButtonClick.emit(); } onClearButtonClick() { this.tracePipeline.clear(); this.onOperationFinished(); } onFileDragIn(e: DragEvent) { e.preventDefault(); e.stopPropagation(); } onFileDragOut(e: DragEvent) { e.preventDefault(); e.stopPropagation(); } onFileDrop(e: DragEvent) { e.preventDefault(); e.stopPropagation(); const droppedFiles = e.dataTransfer?.files; if (!droppedFiles) return; this.filesUploaded.emit(Array.from(droppedFiles)); } onRemoveTrace(event: MouseEvent, trace: Trace) { event.preventDefault(); event.stopPropagation(); this.tracePipeline.removeTrace(trace); this.onOperationFinished(); } hasLoadedFilesWithViewers(): boolean { return this.ngZone.run(() => { let hasFilesWithViewers = false; this.tracePipeline.getTraces().forEachTrace((trace) => { if (TraceTypeUtils.isTraceTypeWithViewer(trace.type)) { hasFilesWithViewers = true; } }); return hasFilesWithViewers; }); } traceUploadInfo(trace: Trace): string | undefined { return TraceTypeUtils.traceUploadInfo(trace.type); } canVisualizeTrace(trace: Trace): boolean { return TraceTypeUtils.canVisualizeTrace(trace.type); } cannotVisualizeTraceTooltip(trace: Trace): string { return TraceTypeUtils.getReasonForNoTraceVisualization(trace.type); } private getInputFiles(event: Event): File[] { const files: FileList | null = (event?.target as HTMLInputElement)?.files; if (!files || !files[0]) { return []; } return Array.from(files); } }