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 {CdkVirtualScrollViewport} from '@angular/cdk/scrolling'; 17import {Component, ElementRef, Inject, Input, ViewChild} from '@angular/core'; 18import {MatSelectChange} from '@angular/material/select'; 19import {Events} from './events'; 20import {UiData} from './ui_data'; 21 22@Component({ 23 selector: 'viewer-transactions', 24 template: ` 25 <div class="card-grid"> 26 <div class="entries"> 27 <div class="filters"> 28 <div class="time"></div> 29 <div class="id"> 30 <mat-form-field appearance="fill"> 31 <mat-label>TX ID</mat-label> 32 <input matInput [(ngModel)]="idString" (input)="onIdSearchStringChanged()" /> 33 </mat-form-field> 34 </div> 35 <div class="vsyncid"> 36 <mat-form-field appearance="fill"> 37 <mat-label>VSYNC ID</mat-label> 38 <mat-select (selectionChange)="onVSyncIdFilterChanged($event)" multiple> 39 <mat-option *ngFor="let vsyncId of uiData.allVSyncIds" [value]="vsyncId"> 40 {{ vsyncId }} 41 </mat-option> 42 </mat-select> 43 </mat-form-field> 44 </div> 45 <div class="pid"> 46 <mat-form-field appearance="fill"> 47 <mat-label>PID</mat-label> 48 <mat-select (selectionChange)="onPidFilterChanged($event)" multiple> 49 <mat-option *ngFor="let pid of uiData.allPids" [value]="pid"> 50 {{ pid }} 51 </mat-option> 52 </mat-select> 53 </mat-form-field> 54 </div> 55 <div class="uid"> 56 <mat-form-field appearance="fill"> 57 <mat-label>UID</mat-label> 58 <mat-select (selectionChange)="onUidFilterChanged($event)" multiple> 59 <mat-option *ngFor="let uid of uiData.allUids" [value]="uid"> 60 {{ uid }} 61 </mat-option> 62 </mat-select> 63 </mat-form-field> 64 </div> 65 <div class="type"> 66 <mat-form-field appearance="fill"> 67 <mat-label>Type</mat-label> 68 <mat-select (selectionChange)="onTypeFilterChanged($event)" multiple> 69 <mat-option *ngFor="let type of uiData.allTypes" [value]="type"> 70 {{ type }} 71 </mat-option> 72 </mat-select> 73 </mat-form-field> 74 </div> 75 <div class="id"> 76 <mat-form-field appearance="fill"> 77 <mat-label>LAYER/DISP ID</mat-label> 78 <mat-select (selectionChange)="onLayerIdFilterChanged($event)" multiple> 79 <mat-option *ngFor="let id of uiData.allLayerAndDisplayIds" [value]="id"> 80 {{ id }} 81 </mat-option> 82 </mat-select> 83 </mat-form-field> 84 </div> 85 <div class="what"> 86 <mat-form-field appearance="fill"> 87 <mat-label>Search text</mat-label> 88 <input matInput [(ngModel)]="whatSearchString" (input)="onWhatSearchStringChange()" /> 89 </mat-form-field> 90 </div> 91 </div> 92 93 <cdk-virtual-scroll-viewport itemSize="24" class="scroll"> 94 <div 95 *cdkVirtualFor="let entry of uiData.entries; let i = index" 96 class="entry" 97 [class.current-entry]="isCurrentEntry(i)" 98 [class.selected-entry]="isSelectedEntry(i)" 99 (click)="onEntryClicked(i)"> 100 <div class="time"> 101 <span class="mat-body-1">{{ entry.time }}</span> 102 </div> 103 <div class="id"> 104 <span class="mat-body-1">{{ entry.transactionId }}</span> 105 </div> 106 <div class="vsyncid"> 107 <span class="mat-body-1">{{ entry.vsyncId }}</span> 108 </div> 109 <div class="pid"> 110 <span class="mat-body-1">{{ entry.pid }}</span> 111 </div> 112 <div class="uid"> 113 <span class="mat-body-1">{{ entry.uid }}</span> 114 </div> 115 <div class="type"> 116 <span class="mat-body-1">{{ entry.type }}</span> 117 </div> 118 <div class="id"> 119 <span class="mat-body-1">{{ entry.layerOrDisplayId }}</span> 120 </div> 121 <div class="what"> 122 <span class="mat-body-1">{{ entry.what }}</span> 123 </div> 124 </div> 125 </cdk-virtual-scroll-viewport> 126 </div> 127 128 <mat-divider [vertical]="true"></mat-divider> 129 130 <div class="container-properties"> 131 <h3 class="properties-title mat-title">Properties - Proto Dump</h3> 132 <tree-view 133 *ngIf="uiData.currentPropertiesTree" 134 class="properties-tree" 135 [item]="uiData.currentPropertiesTree"></tree-view> 136 </div> 137 </div> 138 `, 139 styles: [ 140 ` 141 .entries { 142 flex: 3; 143 display: flex; 144 flex-direction: column; 145 padding: 16px; 146 } 147 148 .container-properties { 149 flex: 1; 150 padding: 16px; 151 } 152 153 .entries .filters { 154 display: flex; 155 flex-direction: row; 156 } 157 158 .entries .scroll { 159 flex: 1; 160 height: 100%; 161 } 162 163 .scroll .entry { 164 display: flex; 165 flex-direction: row; 166 } 167 168 .filters div { 169 flex: 1; 170 padding: 4px; 171 } 172 173 .filters .vsyncid mat-form-field { 174 width: 120px; 175 } 176 177 .filters div.time { 178 flex: 2; 179 } 180 181 .filters div.what { 182 flex: 3; 183 } 184 185 .filters .id mat-form-field { 186 width: 150px; 187 } 188 189 .filters .what { 190 margin-right: 16px; 191 } 192 193 .filters .what mat-form-field { 194 width: 250px; 195 } 196 197 .entry div { 198 flex: 1; 199 padding: 4px; 200 } 201 202 .entry div.time { 203 flex: 2; 204 } 205 206 .entry div.what { 207 flex: 3; 208 } 209 210 .entry.current-entry { 211 color: white; 212 background-color: #365179; 213 } 214 215 .entry.selected-entry { 216 color: white; 217 background-color: #98aecd; 218 } 219 220 mat-form-field { 221 width: 100px; 222 } 223 224 ::ng-deep .mat-select-panel-wrap { 225 overflow: scroll; 226 overflow-x: hidden; 227 max-height: 75vh; 228 } 229 `, 230 ], 231}) 232class ViewerTransactionsComponent { 233 uiData: UiData = UiData.EMPTY; 234 idString = ''; 235 whatSearchString = ''; 236 237 @ViewChild(CdkVirtualScrollViewport) private scrollComponent?: CdkVirtualScrollViewport; 238 private elementRef: ElementRef; 239 240 constructor(@Inject(ElementRef) elementRef: ElementRef) { 241 this.elementRef = elementRef; 242 } 243 244 @Input() 245 set inputData(data: UiData) { 246 this.uiData = data; 247 if (this.uiData.scrollToIndex !== undefined && this.scrollComponent) { 248 this.scrollComponent.scrollToIndex(this.uiData.scrollToIndex); 249 } 250 } 251 252 onVSyncIdFilterChanged(event: MatSelectChange) { 253 this.emitEvent(Events.VSyncIdFilterChanged, event.value); 254 } 255 256 onPidFilterChanged(event: MatSelectChange) { 257 this.emitEvent(Events.PidFilterChanged, event.value); 258 } 259 260 onUidFilterChanged(event: MatSelectChange) { 261 this.emitEvent(Events.UidFilterChanged, event.value); 262 } 263 264 onTypeFilterChanged(event: MatSelectChange) { 265 this.emitEvent(Events.TypeFilterChanged, event.value); 266 } 267 268 onLayerIdFilterChanged(event: MatSelectChange) { 269 this.emitEvent(Events.LayerIdFilterChanged, event.value); 270 } 271 272 onWhatSearchStringChange() { 273 this.emitEvent(Events.WhatSearchStringChanged, this.whatSearchString); 274 } 275 276 onIdSearchStringChanged() { 277 this.emitEvent(Events.IdFilterChanges, this.idString); 278 } 279 280 onEntryClicked(index: number) { 281 this.emitEvent(Events.EntryClicked, index); 282 } 283 284 isCurrentEntry(index: number): boolean { 285 return index === this.uiData.currentEntryIndex; 286 } 287 288 isSelectedEntry(index: number): boolean { 289 return index === this.uiData.selectedEntryIndex; 290 } 291 292 private emitEvent(event: string, data: any) { 293 const customEvent = new CustomEvent(event, { 294 bubbles: true, 295 detail: data, 296 }); 297 this.elementRef.nativeElement.dispatchEvent(customEvent); 298 } 299} 300 301export {ViewerTransactionsComponent}; 302