1/* 2 * Copyright (C) 2023 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 */ 16 17import {Component, ElementRef, Inject, Input} from '@angular/core'; 18import {TraceType} from 'trace/trace_type'; 19import {Transition} from 'trace/transition'; 20import {PropertyTreeNode} from 'trace/tree_node/property_tree_node'; 21import {CollapsibleSections} from 'viewers/common/collapsible_sections'; 22import {CollapsibleSectionType} from 'viewers/common/collapsible_section_type'; 23import {TimestampClickDetail, ViewerEvents} from 'viewers/common/viewer_events'; 24import {timeButtonStyle} from 'viewers/components/styles/clickable_property.styles'; 25import {selectedElementStyle} from 'viewers/components/styles/selected_element.styles'; 26import {viewerCardStyle} from 'viewers/components/styles/viewer_card.styles'; 27import {UiData} from './ui_data'; 28 29@Component({ 30 selector: 'viewer-transitions', 31 template: ` 32 <div class="card-grid container"> 33 <collapsed-sections 34 [class.empty]="sections.areAllSectionsExpanded()" 35 [sections]="sections" 36 (sectionChange)="sections.onCollapseStateChange($event, false)"> 37 </collapsed-sections> 38 <div class="log-view entries"> 39 <div class="table-header table-row"> 40 <div class="id mat-body-2">Id</div> 41 <div class="type mat-body-2">Type</div> 42 <div class="send-time mat-body-2">Send Time</div> 43 <div class="dispatch-time mat-body-2">Dispatch Time</div> 44 <div class="duration mat-body-2">Duration</div> 45 <div class="status mat-body-2">Status</div> 46 </div> 47 <cdk-virtual-scroll-viewport itemSize="53" class="scroll"> 48 <div 49 *cdkVirtualFor="let transition of uiData.entries; let i = index" 50 class="entry table-row" 51 [class.selected]="isSelectedTransition(transition)" 52 (click)="onTransitionClicked(transition)"> 53 <div class="id"> 54 <span class="mat-body-1">{{ transition.id }}</span> 55 </div> 56 <div class="type"> 57 <span class="mat-body-1">{{ transition.type }}</span> 58 </div> 59 <div class="send-time time"> 60 <button 61 mat-button 62 color="primary" 63 *ngIf="transition.sendTime" 64 (click)="onSendTimeClicked(transition)"> 65 {{ transition.sendTime.formattedValue() }} 66 </button> 67 <span *ngIf="!transition.sendTime" class="mat-body-1"> n/a </span> 68 </div> 69 <div class="dispatch-time time"> 70 <button 71 mat-button 72 color="primary" 73 *ngIf="transition.dispatchTime" 74 (click)="onDispatchTimeClicked(transition)"> 75 {{ transition.dispatchTime.formattedValue() }} 76 </button> 77 <span *ngIf="!transition.dispatchTime" class="mat-body-1"> n/a </span> 78 </div> 79 <div class="duration"> 80 <span *ngIf="transition.duration" class="mat-body-1">{{ transition.duration }}</span> 81 <span *ngIf="!transition.duration" class="mat-body-1"> n/a </span> 82 </div> 83 <div class="status"> 84 <div *ngIf="transition.merged"> 85 <span class="mat-body-1">MERGED</span> 86 <mat-icon aria-hidden="false" fontIcon="merge" matTooltip="merged" icon-gray> 87 </mat-icon> 88 </div> 89 90 <div *ngIf="transition.aborted && !transition.merged"> 91 <span class="mat-body-1">ABORTED</span> 92 <mat-icon 93 aria-hidden="false" 94 fontIcon="close" 95 matTooltip="aborted" 96 style="color: red" 97 icon-red></mat-icon> 98 </div> 99 100 <div *ngIf="transition.played && !transition.aborted && !transition.merged"> 101 <span class="mat-body-1">PLAYED</span> 102 <mat-icon 103 aria-hidden="false" 104 fontIcon="check" 105 matTooltip="played" 106 style="color: green" 107 *ngIf="transition.played && !transition.aborted && !transition.merged"></mat-icon> 108 </div> 109 </div> 110 </div> 111 </cdk-virtual-scroll-viewport> 112 </div> 113 114 <properties-view 115 class="properties-view" 116 [title]="propertiesTitle" 117 [showFilter]="false" 118 [propertiesTree]="uiData.selectedTransition" 119 [traceType]="${TraceType.TRANSITION}" 120 [isProtoDump]="false" 121 placeholderText="No selected transition." 122 (collapseButtonClicked)="sections.onCollapseStateChange(CollapsibleSectionType.PROPERTIES, true)" 123 [class.collapsed]="sections.isSectionCollapsed(CollapsibleSectionType.PROPERTIES)"></properties-view> 124 </div> 125 `, 126 styles: [ 127 ` 128 .container { 129 display: flex; 130 flex-grow: 1; 131 flex-direction: row; 132 } 133 134 .entries { 135 flex: 3; 136 display: flex; 137 flex-direction: column; 138 padding: 16px; 139 } 140 141 .entries .scroll { 142 height: 100%; 143 } 144 145 .entries .table-header { 146 flex: 1; 147 } 148 149 .table-row { 150 display: flex; 151 flex-direction: row; 152 cursor: pointer; 153 border-bottom: solid 1px rgba(0, 0, 0, 0.12); 154 } 155 156 .table-header.table-row { 157 font-weight: bold; 158 border-bottom: solid 1px rgba(0, 0, 0, 0.5); 159 } 160 161 .table-row > div { 162 padding: 16px; 163 } 164 165 .table-row .id { 166 flex: 1; 167 } 168 169 .table-row .type { 170 flex: 2; 171 } 172 173 .table-row .dispatch-time { 174 flex: 4; 175 } 176 177 .table-row .send-time { 178 flex: 4; 179 } 180 181 .table-row .duration { 182 flex: 3; 183 } 184 185 .table-row .status { 186 flex: 2; 187 } 188 189 .status > div { 190 display: flex; 191 justify-content: center; 192 align-items: center; 193 gap: 5px; 194 } 195 196 .transition-timeline .row svg rect { 197 cursor: pointer; 198 } 199 200 .label { 201 width: 300px; 202 padding: 1rem; 203 } 204 205 .lines { 206 flex-grow: 1; 207 padding: 0.5rem; 208 } 209 .properties-view { 210 flex: 1; 211 } 212 `, 213 selectedElementStyle, 214 timeButtonStyle, 215 viewerCardStyle, 216 ], 217}) 218export class ViewerTransitionsComponent { 219 propertiesTitle = 'SELECTED TRANSITION'; 220 CollapsibleSectionType = CollapsibleSectionType; 221 sections = new CollapsibleSections([ 222 { 223 type: CollapsibleSectionType.PROPERTIES, 224 label: this.propertiesTitle, 225 isCollapsed: false, 226 }, 227 ]); 228 229 constructor(@Inject(ElementRef) private elementRef: ElementRef) {} 230 231 @Input() 232 set inputData(data: UiData) { 233 this.uiData = data; 234 } 235 236 onTransitionClicked(transition: Transition): void { 237 this.emitEvent(ViewerEvents.TransitionSelected, transition.propertiesTree); 238 } 239 240 isSelectedTransition(transition: Transition): boolean { 241 return ( 242 transition.id === 243 this.uiData.selectedTransition 244 ?.getChildByName('wmData') 245 ?.getChildByName('id') 246 ?.getValue() || 247 transition.id === 248 this.uiData.selectedTransition 249 ?.getChildByName('shellData') 250 ?.getChildByName('id') 251 ?.getValue() 252 ); 253 } 254 255 onDispatchTimeClicked(transition: Transition) { 256 this.emitEvent( 257 ViewerEvents.TimestampClick, 258 new TimestampClickDetail( 259 transition.dispatchTime?.getValue(), 260 transition.traceIndex, 261 ), 262 ); 263 } 264 265 onSendTimeClicked(transition: Transition) { 266 this.emitEvent( 267 ViewerEvents.TimestampClick, 268 new TimestampClickDetail(transition.sendTime?.getValue(), undefined), 269 ); 270 } 271 272 emitEvent(event: string, data: PropertyTreeNode | TimestampClickDetail) { 273 const customEvent = new CustomEvent(event, { 274 bubbles: true, 275 detail: data, 276 }); 277 this.elementRef.nativeElement.dispatchEvent(customEvent); 278 } 279 280 uiData: UiData = UiData.EMPTY; 281} 282