Skip to content

Commit 7adedb4

Browse files
committed
Show Frida setup progress bar and fix host icons
1 parent 926de88 commit 7adedb4

File tree

2 files changed

+112
-33
lines changed

2 files changed

+112
-33
lines changed

src/components/intercept/config/frida-config.tsx

Lines changed: 89 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as React from 'react';
33
import { computed, observable, action, autorun, flow } from 'mobx';
44
import { observer, inject, disposeOnUnmount } from 'mobx-react';
55

6+
import { delay } from '../../../util/promise';
67
import { styled } from '../../../styles';
78

89
import { Interceptor } from '../../../model/interception/interceptors';
@@ -160,9 +161,17 @@ class FridaConfig extends React.Component<{
160161
) ?? [];
161162
}.bind(this));
162163

163-
164-
@observable private inProgressHostIds: string[] = [];
165164
@observable private inProgressTargetIds: string[] = [];
165+
@observable private hostProgress: { [hostId: string]: number } = {};
166+
167+
@action
168+
setHostProgress(hostId: string, progress: number | undefined) {
169+
if (progress === undefined) {
170+
delete this.hostProgress[hostId];
171+
} else {
172+
this.hostProgress[hostId] = progress;
173+
}
174+
}
166175

167176
async componentDidMount() {
168177
if (this.fridaHosts.length === 1 && this.fridaHosts[0].state === 'available') {
@@ -209,30 +218,25 @@ class FridaConfig extends React.Component<{
209218
}
210219

211220
@action.bound
212-
selectHost(hostId: string) {
221+
async selectHost(hostId: string) {
213222
const host = this.getHost(hostId);
214223

215224
if (host?.state === 'available') {
216-
this.selectedHostId = hostId;
217-
this.searchInput = '';
218-
this.updateTargets();
219-
} else if (host?.state === 'launch-required') {
220-
this.inProgressHostIds.push(hostId);
221-
this.props.activateInterceptor({
222-
action: 'launch',
223-
hostId
224-
}).finally(action(() => {
225-
_.pull(this.inProgressHostIds, hostId);
226-
}));
225+
this.viewHostTargets(hostId);
226+
return;
227+
}
228+
229+
// Do nothing if the host is already busy:
230+
if (this.hostProgress[hostId] !== undefined) return;
231+
232+
this.hostProgress[hostId] = 10;
233+
234+
if (host?.state === 'launch-required') {
235+
await this.launchInterceptor(hostId);
227236
} else if (host?.state === 'setup-required') {
228-
this.inProgressHostIds.push(hostId);
229-
this.props.activateInterceptor({
230-
action: 'setup',
231-
hostId
232-
}).finally(action(() => {
233-
_.pull(this.inProgressHostIds, hostId);
234-
}));
237+
await this.setupInterceptor(hostId);
235238
} else {
239+
// Should probably never happen, but maybe in some race conditions
236240
return;
237241
}
238242
}
@@ -242,6 +246,62 @@ class FridaConfig extends React.Component<{
242246
this.selectedHostId = undefined;
243247
}
244248

249+
@action
250+
viewHostTargets(hostId: string) {
251+
this.selectedHostId = hostId;
252+
this.searchInput = '';
253+
this.updateTargets();
254+
}
255+
256+
async setupInterceptor(hostId: string) {
257+
// Logarithmically move towards 75% while setup runs:
258+
let interval = setInterval(() => {
259+
const currentProgress = this.hostProgress[hostId];
260+
const remaining = 74 - currentProgress;
261+
this.setHostProgress(hostId,
262+
currentProgress + Math.floor(remaining / 10)
263+
);
264+
}, 100);
265+
266+
try {
267+
await this.props.activateInterceptor({
268+
action: 'setup',
269+
hostId
270+
});
271+
272+
this.setHostProgress(hostId, 75);
273+
await this.launchInterceptor(hostId);
274+
} finally {
275+
clearInterval(interval);
276+
this.setHostProgress(hostId, undefined);
277+
}
278+
}
279+
280+
async launchInterceptor(hostId: string) {
281+
// Logarithmically move towards 100% while setup runs:
282+
let interval = setInterval(() => {
283+
const currentProgress = this.hostProgress[hostId];
284+
const remaining = 99 - currentProgress;
285+
this.setHostProgress(hostId,
286+
currentProgress + Math.floor(remaining / 5)
287+
);
288+
}, 100);
289+
290+
try {
291+
await this.props.activateInterceptor({
292+
action: 'launch',
293+
hostId
294+
});
295+
296+
this.setHostProgress(hostId, 100);
297+
await delay(10); // Tiny delay purely for nice UI purposes
298+
this.viewHostTargets(hostId);
299+
} finally {
300+
clearInterval(interval);
301+
this.setHostProgress(hostId, undefined);
302+
}
303+
}
304+
245305
@action.bound
246306
interceptTarget(targetId: string) {
247307
const host = this.selectedHost;
@@ -317,28 +377,24 @@ class FridaConfig extends React.Component<{
317377
spinnerText={`Waiting for ${this.deviceClassName} devices to attach to...`}
318378
targets={this.fridaHosts.map(host => {
319379
const { id, name, state } = host;
320-
const activating = this.inProgressHostIds.includes(id);
380+
const activating = this.hostProgress[id] !== undefined;
321381

322382
return {
323383
id,
324384
title: `${this.deviceClassName} device ${name} in state ${state}`,
385+
icon: id.includes("emulator-")
386+
? <Icon icon={['far', 'window-maximize']} />
387+
: id.match(/\d+\.\d+\.\d+\.\d+:\d+/)
388+
? <Icon icon={['fas', 'network-wired']} />
389+
: <Icon icon={['fas', 'mobile-alt']} />,
325390
status: activating
326391
? 'activating'
327392
: state === 'unavailable'
328393
? 'unavailable'
329394
// Available here means clickable - interceptable/setupable/launchable
330395
: 'available',
331-
content: <p>
332-
{
333-
activating
334-
? <Icon icon={['fas', 'spinner']} spin />
335-
: id.includes("emulator-")
336-
? <Icon icon={['far', 'window-maximize']} />
337-
: id.match(/\d+\.\d+\.\d+\.\d+:\d+/)
338-
? <Icon icon={['fas', 'network-wired']} />
339-
: <Icon icon={['fas', 'mobile-alt']} />
340-
} { name }<br />{ state }
341-
</p>
396+
progress: this.hostProgress[id],
397+
content: <p>{ name }<br />{ state }</p>
342398
};
343399
})}
344400
interceptTarget={this.selectHost}

src/components/intercept/config/intercept-target-list.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,22 @@ const TargetButton = styled(Button)<{
7777
margin-right: 10px;
7878
width: 15px;
7979
}
80+
81+
position: relative;
82+
`;
83+
84+
const ProgressBar = styled.div<{ progress: number }>`
85+
position: absolute;
86+
87+
top: 0;
88+
bottom: 0;
89+
left: 0;
90+
width: ${p => p.progress}%;
91+
transition: width 0.1s linear;
92+
93+
background-color: ${p => p.theme.primaryInputBackground};
94+
mix-blend-mode: overlay;
95+
border-radius: 4px;
8096
`;
8197

8298
const TargetText = styled.div<{ ellipseDirection: 'left' | 'right' }>`
@@ -110,6 +126,7 @@ type TargetItem = {
110126
title: string,
111127
content: React.ReactNode,
112128
icon?: React.ReactNode,
129+
progress?: number; // 0 - 100
113130
status: 'active' | 'available' | 'activating' | 'unavailable',
114131
};
115132

@@ -172,6 +189,10 @@ const Target = (props: {
172189
? target.icon
173190
: null;
174191

192+
const progress = target.progress !== undefined
193+
? <ProgressBar progress={target.progress} />
194+
: null
195+
175196
return <TargetItem>
176197
<TargetButton
177198
title={target.title}
@@ -182,6 +203,8 @@ const Target = (props: {
182203
: _.noop
183204
}
184205
>
206+
{ progress }
207+
185208
{ icon }
186209

187210
<TargetText ellipseDirection={ellipseDirection}>

0 commit comments

Comments
 (0)