feat: add back install apk page

This commit is contained in:
Simon Chan 2021-10-13 17:05:41 +08:00
parent ab32a8ee87
commit 20b914fe6f
10 changed files with 192 additions and 69 deletions

View file

@ -4,7 +4,7 @@ import AdbWebUsbBackend, { AdbWebCredentialStore, AdbWebUsbBackendWatcher } from
import AdbWsBackend from '@yume-chan/adb-backend-ws'; import AdbWsBackend from '@yume-chan/adb-backend-ws';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { device, logger } from '../state'; import { global, logger } from '../state';
import { CommonStackTokens } from '../utils'; import { CommonStackTokens } from '../utils';
import { ErrorDialogContext } from './error-dialog'; import { ErrorDialogContext } from './error-dialog';
@ -58,7 +58,7 @@ function _Connect(): JSX.Element | null {
const [wsBackendList, setWsBackendList] = useState<AdbBackend[]>([]); const [wsBackendList, setWsBackendList] = useState<AdbBackend[]>([]);
useEffect(() => { useEffect(() => {
const intervalId = setInterval(async () => { const intervalId = setInterval(async () => {
if (connecting || device.current) { if (connecting || global.device) {
return; return;
} }
@ -99,7 +99,7 @@ function _Connect(): JSX.Element | null {
try { try {
setConnecting(true); setConnecting(true);
await adb.connect(CredentialStore); await adb.connect(CredentialStore);
device.setCurrent(adb); global.setCurrent(adb);
} catch (e) { } catch (e) {
adb.dispose(); adb.dispose();
throw e; throw e;
@ -113,8 +113,8 @@ function _Connect(): JSX.Element | null {
}, [showErrorDialog, selectedBackend]); }, [showErrorDialog, selectedBackend]);
const disconnect = useCallback(async () => { const disconnect = useCallback(async () => {
try { try {
await device.current!.dispose(); await global.device!.dispose();
device.setCurrent(undefined); global.setCurrent(undefined);
} catch (e: any) { } catch (e: any) {
showErrorDialog(e.message); showErrorDialog(e.message);
} }
@ -151,7 +151,7 @@ function _Connect(): JSX.Element | null {
tokens={{ childrenGap: 8, padding: '0 0 8px 8px' }} tokens={{ childrenGap: 8, padding: '0 0 8px 8px' }}
> >
<Dropdown <Dropdown
disabled={!!device.current || backendOptions.length === 0} disabled={!!global.device || backendOptions.length === 0}
label="Available devices" label="Available devices"
placeholder="No available devices" placeholder="No available devices"
options={backendOptions} options={backendOptions}
@ -161,7 +161,7 @@ function _Connect(): JSX.Element | null {
onChange={handleSelectedBackendChange} onChange={handleSelectedBackendChange}
/> />
{!device.current ? ( {!global.device ? (
<Stack horizontal tokens={CommonStackTokens}> <Stack horizontal tokens={CommonStackTokens}>
<StackItem grow shrink> <StackItem grow shrink>
<PrimaryButton <PrimaryButton

View file

@ -3,7 +3,7 @@ import { AdbDemoModeMobileDataType, AdbDemoModeMobileDataTypes, AdbDemoModeSigna
import { autorun, makeAutoObservable, reaction, runInAction } from "mobx"; import { autorun, makeAutoObservable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { CSSProperties, useCallback } from 'react'; import { CSSProperties, useCallback } from 'react';
import { device } from "../state"; import { global } from "../state";
const SignalStrengthOptions = const SignalStrengthOptions =
Object.values(AdbDemoModeSignalStrength) Object.values(AdbDemoModeSignalStrength)
@ -65,7 +65,7 @@ class DemoModeState {
makeAutoObservable(this); makeAutoObservable(this);
reaction( reaction(
() => device.current, () => global.device,
async (device) => { async (device) => {
if (device) { if (device) {
const allowed = await device.demoMode.getAllowed(); const allowed = await device.demoMode.getAllowed();
@ -120,21 +120,21 @@ const FEATURES: FeatureDefinition[][] = [
max: 100, max: 100,
step: 1, step: 1,
initial: 100, initial: 100,
onChange: (value) => device.current!.demoMode.setBatteryLevel(value as number), onChange: (value) => global.device!.demoMode.setBatteryLevel(value as number),
}, },
{ {
key: 'batteryCharging', key: 'batteryCharging',
label: 'Battery Charging', label: 'Battery Charging',
type: 'boolean', type: 'boolean',
initial: false, initial: false,
onChange: (value) => device.current!.demoMode.setBatteryCharging(value as boolean), onChange: (value) => global.device!.demoMode.setBatteryCharging(value as boolean),
}, },
{ {
key: 'powerSaveMode', key: 'powerSaveMode',
label: 'Power Save Mode', label: 'Power Save Mode',
type: 'boolean', type: 'boolean',
initial: false, initial: false,
onChange: (value) => device.current!.demoMode.setPowerSaveMode(value as boolean), onChange: (value) => global.device!.demoMode.setPowerSaveMode(value as boolean),
}, },
], ],
[ [
@ -144,14 +144,14 @@ const FEATURES: FeatureDefinition[][] = [
type: 'select', type: 'select',
options: SignalStrengthOptions, options: SignalStrengthOptions,
initial: AdbDemoModeSignalStrength.Level4, initial: AdbDemoModeSignalStrength.Level4,
onChange: (value) => device.current!.demoMode.setWifiSignalStrength(value as AdbDemoModeSignalStrength), onChange: (value) => global.device!.demoMode.setWifiSignalStrength(value as AdbDemoModeSignalStrength),
}, },
{ {
key: 'airplaneMode', key: 'airplaneMode',
label: 'Airplane Mode', label: 'Airplane Mode',
type: 'boolean', type: 'boolean',
initial: false, initial: false,
onChange: (value) => device.current!.demoMode.setAirplaneMode(value as boolean), onChange: (value) => global.device!.demoMode.setAirplaneMode(value as boolean),
}, },
{ {
key: 'mobileDataType', key: 'mobileDataType',
@ -159,7 +159,7 @@ const FEATURES: FeatureDefinition[][] = [
type: 'select', type: 'select',
options: MobileDataTypeOptions, options: MobileDataTypeOptions,
initial: 'lte', initial: 'lte',
onChange: (value) => device.current!.demoMode.setMobileDataType(value as AdbDemoModeMobileDataType), onChange: (value) => global.device!.demoMode.setMobileDataType(value as AdbDemoModeMobileDataType),
}, },
{ {
key: 'mobileSignalStrength', key: 'mobileSignalStrength',
@ -167,7 +167,7 @@ const FEATURES: FeatureDefinition[][] = [
type: 'select', type: 'select',
options: SignalStrengthOptions, options: SignalStrengthOptions,
initial: AdbDemoModeSignalStrength.Level4, initial: AdbDemoModeSignalStrength.Level4,
onChange: (value) => device.current!.demoMode.setMobileSignalStrength(value as AdbDemoModeSignalStrength), onChange: (value) => global.device!.demoMode.setMobileSignalStrength(value as AdbDemoModeSignalStrength),
}, },
], ],
[ [
@ -177,42 +177,42 @@ const FEATURES: FeatureDefinition[][] = [
type: 'select', type: 'select',
options: StatusBarModeOptions, options: StatusBarModeOptions,
initial: 'transparent', initial: 'transparent',
onChange: (value) => device.current!.demoMode.setStatusBarMode(value as AdbDemoModeStatusBarMode), onChange: (value) => global.device!.demoMode.setStatusBarMode(value as AdbDemoModeStatusBarMode),
}, },
{ {
key: 'vibrateMode', key: 'vibrateMode',
label: 'Vibrate Mode Indicator', label: 'Vibrate Mode Indicator',
type: 'boolean', type: 'boolean',
initial: false, initial: false,
onChange: (value) => device.current!.demoMode.setVibrateModeEnabled(value as boolean), onChange: (value) => global.device!.demoMode.setVibrateModeEnabled(value as boolean),
}, },
{ {
key: 'bluetoothConnected', key: 'bluetoothConnected',
label: 'Bluetooth Indicator', label: 'Bluetooth Indicator',
type: 'boolean', type: 'boolean',
initial: false, initial: false,
onChange: (value) => device.current!.demoMode.setBluetoothConnected(value as boolean), onChange: (value) => global.device!.demoMode.setBluetoothConnected(value as boolean),
}, },
{ {
key: 'locatingIcon', key: 'locatingIcon',
label: 'Locating Icon', label: 'Locating Icon',
type: 'boolean', type: 'boolean',
initial: false, initial: false,
onChange: (value) => device.current!.demoMode.setLocatingIcon(value as boolean), onChange: (value) => global.device!.demoMode.setLocatingIcon(value as boolean),
}, },
{ {
key: 'alarmIcon', key: 'alarmIcon',
label: 'Alarm Icon', label: 'Alarm Icon',
type: 'boolean', type: 'boolean',
initial: false, initial: false,
onChange: (value) => device.current!.demoMode.setAlarmIcon(value as boolean), onChange: (value) => global.device!.demoMode.setAlarmIcon(value as boolean),
}, },
{ {
key: 'notificationsVisibility', key: 'notificationsVisibility',
label: 'Notifications Visibility', label: 'Notifications Visibility',
type: 'boolean', type: 'boolean',
initial: true, initial: true,
onChange: (value) => device.current!.demoMode.setNotificationsVisibility(value as boolean), onChange: (value) => global.device!.demoMode.setNotificationsVisibility(value as boolean),
}, },
{ {
key: 'hour', key: 'hour',
@ -222,7 +222,7 @@ const FEATURES: FeatureDefinition[][] = [
max: 23, max: 23,
step: 1, step: 1,
initial: 12, initial: 12,
onChange: (value) => device.current!.demoMode.setTime(value as number, state.features.get('minute') as number | undefined ?? 34) onChange: (value) => global.device!.demoMode.setTime(value as number, state.features.get('minute') as number | undefined ?? 34)
}, },
{ {
key: 'minute', key: 'minute',
@ -232,7 +232,7 @@ const FEATURES: FeatureDefinition[][] = [
max: 59, max: 59,
step: 1, step: 1,
initial: 34, initial: 34,
onChange: (value) => device.current!.demoMode.setTime(state.features.get('hour') as number | undefined ?? 34, value as number) onChange: (value) => global.device!.demoMode.setTime(state.features.get('hour') as number | undefined ?? 34, value as number)
}, },
], ],
]; ];
@ -306,7 +306,7 @@ const DemoModeBase = ({
style, style,
}: DemoModeProps) => { }: DemoModeProps) => {
const handleAllowedChange = useCallback(async (e, value?: boolean) => { const handleAllowedChange = useCallback(async (e, value?: boolean) => {
await device.current!.demoMode.setAllowed(value!); await global.device!.demoMode.setAllowed(value!);
runInAction(() => { runInAction(() => {
state.allowed = value!; state.allowed = value!;
state.enabled = false; state.enabled = false;
@ -314,7 +314,7 @@ const DemoModeBase = ({
}, []); }, []);
const handleEnabledChange = useCallback(async (e, value?: boolean) => { const handleEnabledChange = useCallback(async (e, value?: boolean) => {
await device.current!.demoMode.setEnabled(value!); await global.device!.demoMode.setEnabled(value!);
runInAction(() => state.enabled = value!); runInAction(() => state.enabled = value!);
}, []); }, []);
@ -322,7 +322,7 @@ const DemoModeBase = ({
<div style={{ padding: 12, overflow: 'hidden auto', ...style }}> <div style={{ padding: 12, overflow: 'hidden auto', ...style }}>
<Toggle <Toggle
label="Allowed" label="Allowed"
disabled={!device.current} disabled={!global.device}
checked={state.allowed} checked={state.allowed}
onChange={handleAllowedChange} onChange={handleAllowedChange}
/> />

View file

@ -5,7 +5,7 @@ import type { NextPage } from 'next';
import Head from 'next/head'; import Head from 'next/head';
import React from "react"; import React from "react";
import { ExternalLink } from "../components"; import { ExternalLink } from "../components";
import { device } from '../state'; import { global } from '../state';
import { RouteStackProps } from "../utils"; import { RouteStackProps } from "../utils";
const KNOWN_FEATURES: Record<string, string> = { const KNOWN_FEATURES: Record<string, string> = {
@ -52,7 +52,7 @@ const DeviceInfo: NextPage = () => {
</MessageBar> </MessageBar>
<span> <span>
<span>Protocol Version: </span> <span>Protocol Version: </span>
<code>{device.current?.protocolVersion?.toString(16).padStart(8, '0')}</code> <code>{global.device?.protocolVersion?.toString(16).padStart(8, '0')}</code>
</span> </span>
<Separator /> <Separator />
@ -60,21 +60,21 @@ const DeviceInfo: NextPage = () => {
<code>ro.product.name</code> <code>ro.product.name</code>
<span> field in Android Build Props</span> <span> field in Android Build Props</span>
</MessageBar> </MessageBar>
<span>Product Name: {device.current?.product}</span> <span>Product Name: {global.device?.product}</span>
<Separator /> <Separator />
<MessageBar> <MessageBar>
<code>ro.product.model</code> <code>ro.product.model</code>
<span> field in Android Build Props</span> <span> field in Android Build Props</span>
</MessageBar> </MessageBar>
<span>Model Name: {device.current?.model}</span> <span>Model Name: {global.device?.model}</span>
<Separator /> <Separator />
<MessageBar> <MessageBar>
<code>ro.product.device</code> <code>ro.product.device</code>
<span> field in Android Build Props</span> <span> field in Android Build Props</span>
</MessageBar> </MessageBar>
<span>Device Name: {device.current?.device}</span> <span>Device Name: {global.device?.device}</span>
<Separator /> <Separator />
<MessageBar> <MessageBar>
@ -87,7 +87,7 @@ const DeviceInfo: NextPage = () => {
</MessageBar> </MessageBar>
<span> <span>
<span>Features: </span> <span>Features: </span>
{device.current?.features?.map((feature, index) => ( {global.device?.features?.map((feature, index) => (
<span key={feature}> <span key={feature}>
{index !== 0 && (<span>, </span>)} {index !== 0 && (<span>, </span>)}
<span>{feature}</span> <span>{feature}</span>

View file

@ -10,7 +10,7 @@ import Router, { useRouter } from "next/router";
import path from 'path'; import path from 'path';
import React, { useCallback, useContext, useEffect, useLayoutEffect, useRef, useState } from 'react'; import React, { useCallback, useContext, useEffect, useLayoutEffect, useRef, useState } from 'react';
import { CommandBar, ErrorDialogContext } from '../components'; import { CommandBar, ErrorDialogContext } from '../components';
import { device } from '../state'; import { global } from '../state';
import { asyncEffect, chunkFile, formatSize, formatSpeed, pickFile, RouteStackProps, useSpeed } from '../utils'; import { asyncEffect, chunkFile, formatSize, formatSpeed, pickFile, RouteStackProps, useSpeed } from '../utils';
let StreamSaver: typeof import('streamsaver'); let StreamSaver: typeof import('streamsaver');
@ -96,7 +96,7 @@ class FileManagerState {
items: observable.shallow, items: observable.shallow,
}); });
reaction( reaction(
() => device.current, () => global.device,
() => this.loadFiles(), () => this.loadFiles(),
{ fireImmediately: true }, { fireImmediately: true },
); );
@ -264,13 +264,13 @@ class FileManagerState {
runInAction(() => this.items = []); runInAction(() => this.items = []);
if (!device.current) { if (!global.device) {
return; return;
} }
runInAction(() => this.loading = true); runInAction(() => this.loading = true);
const sync = await device.current.sync(); const sync = await global.device.sync();
const items: ListItem[] = []; const items: ListItem[] = [];
const linkItems: AdbSyncEntryResponse[] = []; const linkItems: AdbSyncEntryResponse[] = [];
@ -365,7 +365,7 @@ const FileManager: NextPage = (): JSX.Element | null => {
const [previewUrl, setPreviewUrl] = useState<string | undefined>(); const [previewUrl, setPreviewUrl] = useState<string | undefined>();
const previewImage = useCallback(async (path: string) => { const previewImage = useCallback(async (path: string) => {
const sync = await device.current!.sync(); const sync = await global.device!.sync();
try { try {
const readableStream = createReadableStreamFromBufferIterator(sync.read(path)); const readableStream = createReadableStreamFromBufferIterator(sync.read(path));
const response = new Response(readableStream); const response = new Response(readableStream);
@ -413,7 +413,7 @@ const FileManager: NextPage = (): JSX.Element | null => {
const [uploadTotalSize, setUploadTotalSize] = useState(0); const [uploadTotalSize, setUploadTotalSize] = useState(0);
const [debouncedUploadedSize, uploadSpeed] = useSpeed(uploadedSize, uploadTotalSize); const [debouncedUploadedSize, uploadSpeed] = useSpeed(uploadedSize, uploadTotalSize);
const upload = useCallback(async (file: File) => { const upload = useCallback(async (file: File) => {
const sync = await device.current!.sync(); const sync = await global.device!.sync();
try { try {
const itemPath = path.resolve(state.path!, file.name); const itemPath = path.resolve(state.path!, file.name);
setUploading(true); setUploading(true);
@ -445,7 +445,7 @@ const FileManager: NextPage = (): JSX.Element | null => {
key: 'upload', key: 'upload',
text: 'Upload', text: 'Upload',
iconProps: { iconName: 'Upload' }, iconProps: { iconName: 'Upload' },
disabled: !device.current, disabled: !global.device,
onClick() { onClick() {
(async () => { (async () => {
const files = await pickFile({ multiple: true }); const files = await pickFile({ multiple: true });
@ -467,7 +467,7 @@ const FileManager: NextPage = (): JSX.Element | null => {
iconProps: { iconName: 'Download' }, iconProps: { iconName: 'Download' },
onClick() { onClick() {
(async () => { (async () => {
const sync = await device.current!.sync(); const sync = await global.device!.sync();
try { try {
const itemPath = path.resolve(state.path, selectedItems[0].name!); const itemPath = path.resolve(state.path, selectedItems[0].name!);
const readableStream = createReadableStreamFromBufferIterator(sync.read(itemPath)); const readableStream = createReadableStreamFromBufferIterator(sync.read(itemPath));
@ -495,7 +495,7 @@ const FileManager: NextPage = (): JSX.Element | null => {
(async () => { (async () => {
try { try {
for (const item of selectedItems) { for (const item of selectedItems) {
const output = await device.current!.rm(path.resolve(state.path, item.name!)); const output = await global.device!.rm(path.resolve(state.path, item.name!));
if (output) { if (output) {
showErrorDialog(output); showErrorDialog(output);
return; return;

View file

@ -6,7 +6,7 @@ import { NextPage } from "next";
import Head from "next/head"; import Head from "next/head";
import React, { useCallback, useContext, useEffect, useRef } from 'react'; import React, { useCallback, useContext, useEffect, useRef } from 'react';
import { CommandBar, DemoMode, DeviceView, ErrorDialogContext } from '../components'; import { CommandBar, DemoMode, DeviceView, ErrorDialogContext } from '../components';
import { device } from "../state"; import { global } from "../state";
import { RouteStackProps } from "../utils"; import { RouteStackProps } from "../utils";
class FrameBufferState { class FrameBufferState {
@ -38,13 +38,13 @@ const FrameBuffer: NextPage = (): JSX.Element | null => {
const canvasRef = useRef<HTMLCanvasElement | null>(null); const canvasRef = useRef<HTMLCanvasElement | null>(null);
const capture = useCallback(async () => { const capture = useCallback(async () => {
if (!device.current) { if (!global.device) {
return; return;
} }
try { try {
const start = window.performance.now(); const start = window.performance.now();
const framebuffer = await device.current.framebuffer(); const framebuffer = await global.device.framebuffer();
const end = window.performance.now(); const end = window.performance.now();
console.log('time', end - start); console.log('time', end - start);
state.setImage(framebuffer); state.setImage(framebuffer);
@ -68,7 +68,7 @@ const FrameBuffer: NextPage = (): JSX.Element | null => {
const commandBarItems = computed(() => [ const commandBarItems = computed(() => [
{ {
key: 'start', key: 'start',
disabled: !device.current, disabled: !global.device,
iconProps: { iconName: 'Camera' }, iconProps: { iconName: 'Camera' },
text: 'Capture', text: 'Capture',
onClick: capture, onClick: capture,
@ -87,7 +87,7 @@ const FrameBuffer: NextPage = (): JSX.Element | null => {
const url = canvas.toDataURL(); const url = canvas.toDataURL();
const a = document.createElement('a'); const a = document.createElement('a');
a.href = url; a.href = url;
a.download = `Screenshot of ${device.current!.name}.png`; a.download = `Screenshot of ${global.device!.name}.png`;
a.click(); a.click();
}, },
}, },

123
apps/demo/pages/install.tsx Normal file
View file

@ -0,0 +1,123 @@
import { DefaultButton, ProgressIndicator, Stack } from "@fluentui/react";
import { AdbSyncMaxPacketSize } from "@yume-chan/adb";
import { makeAutoObservable, observable, runInAction } from "mobx";
import { observer } from "mobx-react-lite";
import { NextPage } from "next";
import Head from "next/head";
import React from "react";
import { global } from "../state";
import { chunkFile, pickFile, RouteStackProps } from "../utils";
enum Stage {
Uploading,
Installing,
Completed,
}
interface Progress {
filename: string;
stage: Stage;
uploadedSize: number;
totalSize: number;
value: number | undefined;
}
class InstallPageState {
installing = false;
progress: Progress | undefined;
constructor() {
makeAutoObservable(this, {
progress: observable.ref,
});
}
async install() {
const file = await pickFile({ accept: '.apk' });
if (!file) {
return;
}
runInAction(() => {
this.installing = true;
this.progress = {
filename: file.name,
stage: Stage.Uploading,
uploadedSize: 0,
totalSize: file.size,
value: 0,
};
});
await global.device!.install(chunkFile(file, AdbSyncMaxPacketSize), uploaded => {
runInAction(() => {
if (uploaded !== file.size) {
this.progress = {
filename: file.name,
stage: Stage.Uploading,
uploadedSize: uploaded,
totalSize: file.size,
value: uploaded / file.size * 0.8,
};
} else {
this.progress = {
filename: file.name,
stage: Stage.Installing,
uploadedSize: uploaded,
totalSize: file.size,
value: 0.8,
};
}
});
});
runInAction(() => {
this.progress = {
filename: file.name,
stage: Stage.Completed,
uploadedSize: file.size,
totalSize: file.size,
value: 1,
};
this.installing = false;
});
}
}
const state = new InstallPageState();
const Install: NextPage = () => {
return (
<Stack {...RouteStackProps}>
<Head>
<title>Install APK - WebADB</title>
</Head>
<Stack horizontal>
<DefaultButton
disabled={!global.device || state.installing}
text="Open"
onClick={() => state.install()}
/>
</Stack>
{state.progress && (
<ProgressIndicator
styles={{ root: { width: 300 } }}
label={state.progress.filename}
percentComplete={state.progress.value}
description={Stage[state.progress.stage]}
/>
)}
</Stack>
);
};
export default observer(Install);

View file

@ -6,7 +6,7 @@ import Head from "next/head";
import React, { CSSProperties, useCallback, useContext, useEffect, useRef, useState } from 'react'; import React, { CSSProperties, useCallback, useContext, useEffect, useRef, useState } from 'react';
import 'xterm/css/xterm.css'; import 'xterm/css/xterm.css';
import { ErrorDialogContext } from '../components/error-dialog'; import { ErrorDialogContext } from '../components/error-dialog';
import { device } from "../state"; import { global } from "../state";
import { ResizeObserver, RouteStackProps } from '../utils'; import { ResizeObserver, RouteStackProps } from '../utils';
let terminal: import('../components/terminal').AdbTerminal; let terminal: import('../components/terminal').AdbTerminal;
@ -43,9 +43,9 @@ const Shell: NextPage = (): JSX.Element | null => {
const connectingRef = useRef(false); const connectingRef = useRef(false);
useEffect(() => { useEffect(() => {
return reaction( return reaction(
() => device.current, () => global.device,
async () => { async () => {
if (!device.current) { if (!global.device) {
terminal.socket = undefined; terminal.socket = undefined;
return; return;
} }
@ -56,7 +56,7 @@ const Shell: NextPage = (): JSX.Element | null => {
try { try {
connectingRef.current = true; connectingRef.current = true;
const socket = await device.current.childProcess.shell(); const socket = await global.device.childProcess.shell();
terminal.socket = socket; terminal.socket = socket;
} catch (e) { } catch (e) {
showErrorDialog(e instanceof Error ? e.message : `${e}`); showErrorDialog(e instanceof Error ? e.message : `${e}`);

View file

@ -5,7 +5,7 @@ import { NextPage } from "next";
import Head from "next/head"; import Head from "next/head";
import React, { useCallback } from "react"; import React, { useCallback } from "react";
import { ExternalLink } from "../components"; import { ExternalLink } from "../components";
import { device } from "../state"; import { global } from "../state";
import { asyncEffect, RouteStackProps } from "../utils"; import { asyncEffect, RouteStackProps } from "../utils";
class TcpIpState { class TcpIpState {
@ -18,7 +18,7 @@ class TcpIpState {
constructor() { constructor() {
makeAutoObservable(this); makeAutoObservable(this);
reaction( reaction(
() => device.current, () => global.device,
() => this.queryInfo(), () => this.queryInfo(),
{ fireImmediately: true } { fireImmediately: true }
); );
@ -28,14 +28,14 @@ class TcpIpState {
return [ return [
{ {
key: 'refresh', key: 'refresh',
disabled: !device.current, disabled: !global.device,
iconProps: { iconName: 'Refresh' }, iconProps: { iconName: 'Refresh' },
text: 'Refresh', text: 'Refresh',
onClick: () => { this.queryInfo(); }, onClick: () => { this.queryInfo(); },
}, },
{ {
key: 'apply', key: 'apply',
disabled: !device.current, disabled: !global.device,
iconProps: { iconName: 'Save' }, iconProps: { iconName: 'Save' },
text: 'Apply', text: 'Apply',
onClick: () => { this.applyServicePort(); }, onClick: () => { this.applyServicePort(); },
@ -44,7 +44,7 @@ class TcpIpState {
} }
queryInfo = asyncEffect(async (signal) => { queryInfo = asyncEffect(async (signal) => {
if (!device.current) { if (!global.device) {
runInAction(() => { runInAction(() => {
this.serviceListenAddresses = undefined; this.serviceListenAddresses = undefined;
this.servicePortEnabled = false; this.servicePortEnabled = false;
@ -55,9 +55,9 @@ class TcpIpState {
return; return;
} }
const serviceListenAddresses = await device.current.getProp('service.adb.listen_addrs'); const serviceListenAddresses = await global.device.getProp('service.adb.listen_addrs');
const servicePort = await device.current.getProp('service.adb.tcp.port'); const servicePort = await global.device.getProp('service.adb.tcp.port');
const persistPort = await device.current.getProp('persist.adb.tcp.port'); const persistPort = await global.device.getProp('persist.adb.tcp.port');
if (signal.aborted) { if (signal.aborted) {
return; return;
@ -85,14 +85,14 @@ class TcpIpState {
}); });
async applyServicePort() { async applyServicePort() {
if (!device.current) { if (!global.device) {
return; return;
} }
if (state.servicePortEnabled) { if (state.servicePortEnabled) {
await device.current.tcpip.setPort(Number.parseInt(state.servicePort, 10)); await global.device.tcpip.setPort(Number.parseInt(state.servicePort, 10));
} else { } else {
await device.current.tcpip.disable(); await global.device.tcpip.disable();
} }
} }
} }
@ -158,12 +158,12 @@ const TcpIp: NextPage = () => {
inlineLabel inlineLabel
label="service.adb.tcp.port" label="service.adb.tcp.port"
checked={state.servicePortEnabled} checked={state.servicePortEnabled}
disabled={!device.current || !!state.serviceListenAddresses} disabled={!global.device || !!state.serviceListenAddresses}
onText="Enabled" onText="Enabled"
offText="Disabled" offText="Disabled"
onChange={handleServicePortEnabledChange} onChange={handleServicePortEnabledChange}
/> />
{device && ( {global && (
<TextField <TextField
disabled={!!state.serviceListenAddresses} disabled={!!state.serviceListenAddresses}
value={state.servicePort} value={state.servicePort}

View file

@ -1,19 +1,19 @@
import { Adb } from "@yume-chan/adb"; import { Adb } from "@yume-chan/adb";
import { makeAutoObservable } from 'mobx'; import { makeAutoObservable } from 'mobx';
export class Device { export class GlobalState {
current: Adb | undefined; device: Adb | undefined;
constructor() { constructor() {
makeAutoObservable(this); makeAutoObservable(this);
} }
setCurrent(device: Adb | undefined) { setCurrent(device: Adb | undefined) {
this.current = device; this.device = device;
device?.onDisconnected(() => { device?.onDisconnected(() => {
this.setCurrent(undefined); this.setCurrent(undefined);
}); });
} }
} }
export const device = new Device(); export const global = new GlobalState();

View file

@ -1,2 +1,2 @@
export * from './device'; export * from './global';
export * from './logger'; export * from './logger';