mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-06 03:50:18 +02:00
feat: add back install apk page
This commit is contained in:
parent
ab32a8ee87
commit
20b914fe6f
10 changed files with 192 additions and 69 deletions
|
@ -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
|
||||||
|
|
|
@ -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}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
123
apps/demo/pages/install.tsx
Normal 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);
|
|
@ -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}`);
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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();
|
|
@ -1,2 +1,2 @@
|
||||||
export * from './device';
|
export * from './global';
|
||||||
export * from './logger';
|
export * from './logger';
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue