1/* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16import {Component, ElementRef, Inject, Input} from '@angular/core'; 17import {DomSanitizer, SafeUrl} from '@angular/platform-browser'; 18import {ScreenRecordingTraceEntry} from 'trace/screen_recording'; 19 20@Component({ 21 selector: 'viewer-screen-recording', 22 template: ` 23 <mat-card class="container"> 24 <mat-card-title class="header"> 25 <button mat-button class="button-drag" cdkDragHandle> 26 <mat-icon class="drag-icon">drag_indicator</mat-icon> 27 <span class="mat-body-2">Screen recording</span> 28 </button> 29 30 <button mat-button class="button-minimize" (click)="onMinimizeButtonClick()"> 31 <mat-icon> 32 {{ isMinimized ? 'maximize' : 'minimize' }} 33 </mat-icon> 34 </button> 35 </mat-card-title> 36 <div class="video-container" [style.height]="isMinimized ? '0px' : ''"> 37 <ng-container *ngIf="hasFrameToShow; then video; else noVideo"> </ng-container> 38 </div> 39 </mat-card> 40 41 <ng-template #video> 42 <video 43 *ngIf="hasFrameToShow" 44 [currentTime]="videoCurrentTime" 45 [src]="videoUrl" 46 cdkDragHandle></video> 47 </ng-template> 48 49 <ng-template #noVideo> 50 <div class="no-video"> 51 <p class="mat-body-2">No screen recording frame to show.</p> 52 <p class="mat-body-1">Current timestamp is still before first frame.</p> 53 </div> 54 </ng-template> 55 `, 56 styles: [ 57 ` 58 .container { 59 width: fit-content; 60 height: fit-content; 61 display: flex; 62 flex-direction: column; 63 padding: 0; 64 } 65 66 .header { 67 display: flex; 68 flex-direction: row; 69 margin: 0px; 70 border: 1px solid var(--border-color); 71 border-radius: 4px; 72 } 73 74 .button-drag { 75 flex-grow: 1; 76 cursor: grab; 77 } 78 79 .drag-icon { 80 float: left; 81 margin: 5px 0; 82 } 83 84 .button-minimize { 85 flex-grow: 0; 86 } 87 88 .video-container, 89 video { 90 border: 1px solid var(--default-border); 91 max-width: max(250px, 15vw); 92 cursor: grab; 93 overflow: hidden; 94 } 95 96 .no-video { 97 padding: 1rem; 98 text-align: center; 99 } 100 `, 101 ], 102}) 103class ViewerScreenRecordingComponent { 104 constructor( 105 @Inject(ElementRef) elementRef: ElementRef, 106 @Inject(DomSanitizer) sanitizer: DomSanitizer 107 ) { 108 this.elementRef = elementRef; 109 this.sanitizer = sanitizer; 110 } 111 112 @Input() 113 set currentTraceEntry(entry: undefined | ScreenRecordingTraceEntry) { 114 if (entry === undefined) { 115 this.videoCurrentTime = undefined; 116 return; 117 } 118 119 if (this.videoUrl === undefined) { 120 this.videoUrl = this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(entry.videoData)); 121 } 122 123 this.videoCurrentTime = entry.videoTimeSeconds; 124 } 125 126 onMinimizeButtonClick() { 127 this.isMinimized = !this.isMinimized; 128 } 129 130 videoUrl: undefined | SafeUrl = undefined; 131 videoCurrentTime: number | undefined = undefined; 132 isMinimized = false; 133 134 private elementRef: ElementRef; 135 private sanitizer: DomSanitizer; 136 137 get hasFrameToShow() { 138 return this.videoCurrentTime !== undefined; 139 } 140} 141 142export {ViewerScreenRecordingComponent}; 143