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, EventEmitter, Input, Output} from '@angular/core';
17import {UrlUtils} from 'common/url_utils';
18import {
19  proxyClient,
20  ProxyClient,
21  ProxyState,
22} from 'trace_collection/proxy_client';
23
24@Component({
25  selector: 'adb-proxy',
26  template: `
27    <ng-container [ngSwitch]="proxy.state">
28      <ng-container *ngSwitchCase="states.NO_PROXY">
29        <div class="further-adb-info-text">
30          <p class="mat-body-1">
31            Launch the Winscope ADB Connect proxy to capture traces directly from your browser.
32          </p>
33          <p class="mat-body-1">Python 3.5+ and ADB are required. Run this command:</p>
34          <mat-form-field class="proxy-command-form" appearance="outline">
35            <input matInput readonly [value]="proxyCommand" />
36            <button
37              mat-icon-button
38              matSuffix
39              [cdkCopyToClipboard]="proxyCommand"
40              matTooltip="Copy command">
41              <mat-icon>content_copy</mat-icon>
42            </button>
43          </mat-form-field>
44          <p class="mat-body-1">Or download below.</p>
45        </div>
46
47        <div class="further-adb-info-actions">
48          <button
49            class="download-proxy-btn"
50            color="primary"
51            mat-stroked-button
52            (click)="onDownloadProxyClick()">
53            Download Proxy
54          </button>
55          <button color="primary" mat-stroked-button class="retry" (click)="onRetryButtonClick()">
56            Retry
57          </button>
58        </div>
59      </ng-container>
60
61      <ng-container *ngSwitchCase="states.INVALID_VERSION">
62        <div class="further-adb-info-text">
63          <p class="icon-information mat-body-1">
64            <mat-icon class="adb-icon">update</mat-icon>
65            <span class="adb-info">Your local proxy version is incompatible with Winscope.</span>
66          </p>
67          <p class="mat-body-1">
68            Please update the proxy to version {{ proxyVersion }}. Run this command:
69          </p>
70          <mat-form-field class="proxy-command-container" appearance="outline">
71            <input matInput readonly [value]="proxyCommand" />
72            <button
73              mat-icon-button
74              matSuffix
75              [cdkCopyToClipboard]="proxyCommand"
76              matTooltip="Copy command">
77              <mat-icon>content_copy</mat-icon>
78            </button>
79          </mat-form-field>
80          <p class="mat-body-1">Or download below.</p>
81        </div>
82
83        <div class="further-adb-info-actions">
84          <button
85            class="download-proxy-btn"
86            color="primary"
87            mat-stroked-button
88            (click)="onDownloadProxyClick()">
89            Download Proxy
90          </button>
91          <button color="primary" mat-stroked-button class="retry" (click)="onRetryButtonClick()">
92            Retry
93          </button>
94        </div>
95      </ng-container>
96
97      <ng-container *ngSwitchCase="states.UNAUTH">
98        <div class="further-adb-info-text">
99          <p class="icon-information mat-body-1">
100            <mat-icon class="adb-icon">lock</mat-icon>
101            <span class="adb-info">Proxy authorisation required.</span>
102          </p>
103          <p class="mat-body-1">Enter Winscope proxy token:</p>
104          <mat-form-field
105            class="proxy-key-input-field"
106            (keydown.enter)="onKeydownEnterProxyKeyInput($event)">
107            <input matInput [(ngModel)]="proxyKeyItem" name="proxy-key" />
108          </mat-form-field>
109          <p class="mat-body-1">
110            The proxy token is printed to console on proxy launch, copy and paste it above.
111          </p>
112        </div>
113
114        <div class="further-adb-info-actions">
115          <button color="primary" mat-stroked-button class="retry" (click)="onRetryButtonClick()">
116            Connect
117          </button>
118        </div>
119      </ng-container>
120
121      <ng-container *ngSwitchDefault></ng-container>
122    </ng-container>
123  `,
124  styles: [
125    `
126      .icon-information {
127        display: flex;
128        flex-direction: row;
129        align-items: center;
130      }
131      .further-adb-info-text {
132        display: flex;
133        flex-direction: column;
134        overflow-wrap: break-word;
135        gap: 10px;
136        margin-bottom: 10px;
137      }
138      .further-adb-info-actions {
139        display: flex;
140        flex-direction: row;
141        flex-wrap: wrap;
142        gap: 10px;
143      }
144      /* TODO(b/300063426): remove after migration to angular 15, replace with subscriptSizing */
145      ::ng-deep .proxy-command-form .mat-form-field-wrapper {
146        padding: 0;
147      }
148      .proxy-command-text {
149        user-select: all;
150        overflow: auto;
151      }
152      .adb-info {
153        margin-left: 5px;
154      }
155    `,
156  ],
157})
158export class AdbProxyComponent {
159  @Input()
160  proxy: ProxyClient = proxyClient;
161
162  @Output()
163  readonly proxyChange = new EventEmitter<ProxyClient>();
164
165  @Output()
166  readonly addKey = new EventEmitter<string>();
167
168  states = ProxyState;
169  proxyKeyItem = '';
170  readonly proxyVersion = this.proxy.VERSION;
171  readonly downloadProxyUrl: string =
172    UrlUtils.getRootUrl() + 'winscope_proxy.py';
173  readonly proxyCommand: string =
174    'python3 $ANDROID_BUILD_TOP/development/tools/winscope/src/adb/winscope_proxy.py';
175
176  async onRetryButtonClick() {
177    if (this.proxyKeyItem.length > 0) {
178      this.addKey.emit(this.proxyKeyItem);
179    }
180    await this.proxy.setState(this.states.CONNECTING);
181    this.proxyChange.emit(this.proxy);
182  }
183
184  async onKeydownEnterProxyKeyInput(event: MouseEvent) {
185    (event.target as HTMLInputElement).blur();
186    await this.onRetryButtonClick();
187  }
188
189  onDownloadProxyClick() {
190    window.open(this.downloadProxyUrl, '_blank')?.focus();
191  }
192}
193