mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-04 10:19:17 +02:00
feat(adb): add power related api
This commit is contained in:
parent
1ea248d57e
commit
45d784c8a5
19 changed files with 196 additions and 106 deletions
|
@ -6,7 +6,7 @@ import AdbWsBackend from '@yume-chan/adb-backend-ws';
|
|||
import AdbWebCredentialStore from '@yume-chan/adb-credential-web';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { global, logger } from '../state';
|
||||
import { globalState, logger } from '../state';
|
||||
import { CommonStackTokens, Icons } from '../utils';
|
||||
|
||||
const DropdownStyles = { dropdown: { width: '100%' } };
|
||||
|
@ -33,7 +33,7 @@ function _Connect(): JSX.Element | null {
|
|||
setSupported(supported);
|
||||
|
||||
if (!supported) {
|
||||
global.showErrorDialog('Your browser does not support WebUSB standard, which is required for this site to work.\n\nLatest version of Google Chrome, Microsoft Edge, or other Chromium-based browsers are required.');
|
||||
globalState.showErrorDialog('Your browser does not support WebUSB standard, which is required for this site to work.\n\nLatest version of Google Chrome, Microsoft Edge, or other Chromium-based browsers are required.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -134,24 +134,24 @@ function _Connect(): JSX.Element | null {
|
|||
try {
|
||||
setConnecting(true);
|
||||
await device.connect(CredentialStore);
|
||||
global.setDevice(device);
|
||||
globalState.setDevice(device);
|
||||
} catch (e) {
|
||||
device.dispose();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} catch (e: any) {
|
||||
global.showErrorDialog(e.message);
|
||||
globalState.showErrorDialog(e.message);
|
||||
} finally {
|
||||
setConnecting(false);
|
||||
}
|
||||
}, [selectedBackend]);
|
||||
const disconnect = useCallback(async () => {
|
||||
try {
|
||||
await global.device!.dispose();
|
||||
global.setDevice(undefined);
|
||||
await globalState.device!.dispose();
|
||||
globalState.setDevice(undefined);
|
||||
} catch (e: any) {
|
||||
global.showErrorDialog(e.message);
|
||||
globalState.showErrorDialog(e.message);
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
@ -214,7 +214,7 @@ function _Connect(): JSX.Element | null {
|
|||
tokens={{ childrenGap: 8, padding: '0 0 8px 8px' }}
|
||||
>
|
||||
<Dropdown
|
||||
disabled={!!global.device || backendOptions.length === 0}
|
||||
disabled={!!globalState.device || backendOptions.length === 0}
|
||||
label="Available devices"
|
||||
placeholder="No available devices"
|
||||
options={backendOptions}
|
||||
|
@ -224,7 +224,7 @@ function _Connect(): JSX.Element | null {
|
|||
onChange={handleSelectedBackendChange}
|
||||
/>
|
||||
|
||||
{!global.device
|
||||
{!globalState.device
|
||||
? (
|
||||
<Stack horizontal tokens={CommonStackTokens}>
|
||||
<StackItem grow shrink>
|
||||
|
|
|
@ -3,7 +3,7 @@ import { AdbDemoModeMobileDataType, AdbDemoModeMobileDataTypes, AdbDemoModeSigna
|
|||
import { autorun, makeAutoObservable, reaction, runInAction } from "mobx";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { CSSProperties, useCallback } from 'react';
|
||||
import { global } from "../state";
|
||||
import { globalState } from "../state";
|
||||
|
||||
const SignalStrengthOptions =
|
||||
Object.values(AdbDemoModeSignalStrength)
|
||||
|
@ -65,7 +65,7 @@ class DemoModeState {
|
|||
makeAutoObservable(this);
|
||||
|
||||
reaction(
|
||||
() => global.device,
|
||||
() => globalState.device,
|
||||
async (device) => {
|
||||
if (device) {
|
||||
const allowed = await device.demoMode.getAllowed();
|
||||
|
@ -120,21 +120,21 @@ const FEATURES: FeatureDefinition[][] = [
|
|||
max: 100,
|
||||
step: 1,
|
||||
initial: 100,
|
||||
onChange: (value) => global.device!.demoMode.setBatteryLevel(value as number),
|
||||
onChange: (value) => globalState.device!.demoMode.setBatteryLevel(value as number),
|
||||
},
|
||||
{
|
||||
key: 'batteryCharging',
|
||||
label: 'Battery Charging',
|
||||
type: 'boolean',
|
||||
initial: false,
|
||||
onChange: (value) => global.device!.demoMode.setBatteryCharging(value as boolean),
|
||||
onChange: (value) => globalState.device!.demoMode.setBatteryCharging(value as boolean),
|
||||
},
|
||||
{
|
||||
key: 'powerSaveMode',
|
||||
label: 'Power Save Mode',
|
||||
type: 'boolean',
|
||||
initial: false,
|
||||
onChange: (value) => global.device!.demoMode.setPowerSaveMode(value as boolean),
|
||||
onChange: (value) => globalState.device!.demoMode.setPowerSaveMode(value as boolean),
|
||||
},
|
||||
],
|
||||
[
|
||||
|
@ -144,14 +144,14 @@ const FEATURES: FeatureDefinition[][] = [
|
|||
type: 'select',
|
||||
options: SignalStrengthOptions,
|
||||
initial: AdbDemoModeSignalStrength.Level4,
|
||||
onChange: (value) => global.device!.demoMode.setWifiSignalStrength(value as AdbDemoModeSignalStrength),
|
||||
onChange: (value) => globalState.device!.demoMode.setWifiSignalStrength(value as AdbDemoModeSignalStrength),
|
||||
},
|
||||
{
|
||||
key: 'airplaneMode',
|
||||
label: 'Airplane Mode',
|
||||
type: 'boolean',
|
||||
initial: false,
|
||||
onChange: (value) => global.device!.demoMode.setAirplaneMode(value as boolean),
|
||||
onChange: (value) => globalState.device!.demoMode.setAirplaneMode(value as boolean),
|
||||
},
|
||||
{
|
||||
key: 'mobileDataType',
|
||||
|
@ -159,7 +159,7 @@ const FEATURES: FeatureDefinition[][] = [
|
|||
type: 'select',
|
||||
options: MobileDataTypeOptions,
|
||||
initial: 'lte',
|
||||
onChange: (value) => global.device!.demoMode.setMobileDataType(value as AdbDemoModeMobileDataType),
|
||||
onChange: (value) => globalState.device!.demoMode.setMobileDataType(value as AdbDemoModeMobileDataType),
|
||||
},
|
||||
{
|
||||
key: 'mobileSignalStrength',
|
||||
|
@ -167,7 +167,7 @@ const FEATURES: FeatureDefinition[][] = [
|
|||
type: 'select',
|
||||
options: SignalStrengthOptions,
|
||||
initial: AdbDemoModeSignalStrength.Level4,
|
||||
onChange: (value) => global.device!.demoMode.setMobileSignalStrength(value as AdbDemoModeSignalStrength),
|
||||
onChange: (value) => globalState.device!.demoMode.setMobileSignalStrength(value as AdbDemoModeSignalStrength),
|
||||
},
|
||||
],
|
||||
[
|
||||
|
@ -177,42 +177,42 @@ const FEATURES: FeatureDefinition[][] = [
|
|||
type: 'select',
|
||||
options: StatusBarModeOptions,
|
||||
initial: 'transparent',
|
||||
onChange: (value) => global.device!.demoMode.setStatusBarMode(value as AdbDemoModeStatusBarMode),
|
||||
onChange: (value) => globalState.device!.demoMode.setStatusBarMode(value as AdbDemoModeStatusBarMode),
|
||||
},
|
||||
{
|
||||
key: 'vibrateMode',
|
||||
label: 'Vibrate Mode Indicator',
|
||||
type: 'boolean',
|
||||
initial: false,
|
||||
onChange: (value) => global.device!.demoMode.setVibrateModeEnabled(value as boolean),
|
||||
onChange: (value) => globalState.device!.demoMode.setVibrateModeEnabled(value as boolean),
|
||||
},
|
||||
{
|
||||
key: 'bluetoothConnected',
|
||||
label: 'Bluetooth Indicator',
|
||||
type: 'boolean',
|
||||
initial: false,
|
||||
onChange: (value) => global.device!.demoMode.setBluetoothConnected(value as boolean),
|
||||
onChange: (value) => globalState.device!.demoMode.setBluetoothConnected(value as boolean),
|
||||
},
|
||||
{
|
||||
key: 'locatingIcon',
|
||||
label: 'Locating Icon',
|
||||
type: 'boolean',
|
||||
initial: false,
|
||||
onChange: (value) => global.device!.demoMode.setLocatingIcon(value as boolean),
|
||||
onChange: (value) => globalState.device!.demoMode.setLocatingIcon(value as boolean),
|
||||
},
|
||||
{
|
||||
key: 'alarmIcon',
|
||||
label: 'Alarm Icon',
|
||||
type: 'boolean',
|
||||
initial: false,
|
||||
onChange: (value) => global.device!.demoMode.setAlarmIcon(value as boolean),
|
||||
onChange: (value) => globalState.device!.demoMode.setAlarmIcon(value as boolean),
|
||||
},
|
||||
{
|
||||
key: 'notificationsVisibility',
|
||||
label: 'Notifications Visibility',
|
||||
type: 'boolean',
|
||||
initial: true,
|
||||
onChange: (value) => global.device!.demoMode.setNotificationsVisibility(value as boolean),
|
||||
onChange: (value) => globalState.device!.demoMode.setNotificationsVisibility(value as boolean),
|
||||
},
|
||||
{
|
||||
key: 'hour',
|
||||
|
@ -222,7 +222,7 @@ const FEATURES: FeatureDefinition[][] = [
|
|||
max: 23,
|
||||
step: 1,
|
||||
initial: 12,
|
||||
onChange: (value) => global.device!.demoMode.setTime(value as number, state.features.get('minute') as number | undefined ?? 34)
|
||||
onChange: (value) => globalState.device!.demoMode.setTime(value as number, state.features.get('minute') as number | undefined ?? 34)
|
||||
},
|
||||
{
|
||||
key: 'minute',
|
||||
|
@ -232,7 +232,7 @@ const FEATURES: FeatureDefinition[][] = [
|
|||
max: 59,
|
||||
step: 1,
|
||||
initial: 34,
|
||||
onChange: (value) => global.device!.demoMode.setTime(state.features.get('hour') as number | undefined ?? 34, value as number)
|
||||
onChange: (value) => globalState.device!.demoMode.setTime(state.features.get('hour') as number | undefined ?? 34, value as number)
|
||||
},
|
||||
],
|
||||
];
|
||||
|
@ -306,7 +306,7 @@ const DemoModeBase = ({
|
|||
style,
|
||||
}: DemoModeProps) => {
|
||||
const handleAllowedChange = useCallback(async (e, value?: boolean) => {
|
||||
await global.device!.demoMode.setAllowed(value!);
|
||||
await globalState.device!.demoMode.setAllowed(value!);
|
||||
runInAction(() => {
|
||||
state.allowed = value!;
|
||||
state.enabled = false;
|
||||
|
@ -314,7 +314,7 @@ const DemoModeBase = ({
|
|||
}, []);
|
||||
|
||||
const handleEnabledChange = useCallback(async (e, value?: boolean) => {
|
||||
await global.device!.demoMode.setEnabled(value!);
|
||||
await globalState.device!.demoMode.setEnabled(value!);
|
||||
runInAction(() => state.enabled = value!);
|
||||
}, []);
|
||||
|
||||
|
@ -322,7 +322,7 @@ const DemoModeBase = ({
|
|||
<div style={{ padding: 12, overflow: 'hidden auto', ...style }}>
|
||||
<Toggle
|
||||
label="Allowed"
|
||||
disabled={!global.device}
|
||||
disabled={!globalState.device}
|
||||
checked={state.allowed}
|
||||
onChange={handleAllowedChange}
|
||||
/>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Dialog, DialogFooter, DialogType, PrimaryButton } from '@fluentui/react';
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { global } from '../state';
|
||||
import { globalState } from '../state';
|
||||
|
||||
export const ErrorDialogProvider = observer((props: PropsWithChildren<{}>) => {
|
||||
return (
|
||||
|
@ -9,15 +9,15 @@ export const ErrorDialogProvider = observer((props: PropsWithChildren<{}>) => {
|
|||
{props.children}
|
||||
|
||||
<Dialog
|
||||
hidden={!global.errorDialogVisible}
|
||||
hidden={!globalState.errorDialogVisible}
|
||||
dialogContentProps={{
|
||||
type: DialogType.normal,
|
||||
title: 'Error',
|
||||
subText: global.errorDialogMessage,
|
||||
subText: globalState.errorDialogMessage,
|
||||
}}
|
||||
>
|
||||
<DialogFooter>
|
||||
<PrimaryButton text="OK" onClick={global.hideErrorDialog} />
|
||||
<PrimaryButton text="OK" onClick={globalState.hideErrorDialog} />
|
||||
</DialogFooter>
|
||||
</Dialog>
|
||||
</>
|
||||
|
|
|
@ -3,7 +3,7 @@ import { AdbPacketInit, decodeUtf8 } from '@yume-chan/adb';
|
|||
import { DisposableList } from '@yume-chan/event';
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
import { global, logger } from "../state";
|
||||
import { globalState, logger } from "../state";
|
||||
import { Icons, withDisplayName } from '../utils';
|
||||
import { CommandBar } from './command-bar';
|
||||
|
||||
|
@ -57,10 +57,10 @@ const LogLine = withDisplayName('LoggerLine')(({ packet }: { packet: [string, Ad
|
|||
export const ToggleLogView = observer(() => {
|
||||
return (
|
||||
<IconButton
|
||||
checked={global.logVisible}
|
||||
checked={globalState.logVisible}
|
||||
iconProps={{ iconName: Icons.TextGrammarError }}
|
||||
title="Toggle Log"
|
||||
onClick={global.toggleLog}
|
||||
onClick={globalState.toggleLog}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -142,7 +142,7 @@ export const LogView = observer(({
|
|||
classNames['logger-container'],
|
||||
), [className]);
|
||||
|
||||
if (!global.logVisible) {
|
||||
if (!globalState.logVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,11 @@ const ROUTES = [
|
|||
icon: Icons.Box,
|
||||
name: 'Install APK',
|
||||
},
|
||||
{
|
||||
url: '/power',
|
||||
icon: Icons.Power,
|
||||
name: 'Power Menu',
|
||||
},
|
||||
];
|
||||
|
||||
function NavLink({ link, defaultRender: DefaultRender, ...props }: IComponentAsProps<INavButtonProps>) {
|
||||
|
|
|
@ -5,7 +5,7 @@ import type { NextPage } from 'next';
|
|||
import Head from 'next/head';
|
||||
import React from "react";
|
||||
import { ExternalLink } from "../components";
|
||||
import { global } from '../state';
|
||||
import { globalState } from '../state';
|
||||
import { Icons, RouteStackProps } from "../utils";
|
||||
|
||||
const KNOWN_FEATURES: Record<string, string> = {
|
||||
|
@ -52,7 +52,7 @@ const DeviceInfo: NextPage = () => {
|
|||
</MessageBar>
|
||||
<span>
|
||||
<span>Protocol Version: </span>
|
||||
<code>{global.device?.protocolVersion?.toString(16).padStart(8, '0')}</code>
|
||||
<code>{globalState.device?.protocolVersion?.toString(16).padStart(8, '0')}</code>
|
||||
</span>
|
||||
<Separator />
|
||||
|
||||
|
@ -60,21 +60,21 @@ const DeviceInfo: NextPage = () => {
|
|||
<code>ro.product.name</code>
|
||||
<span> field in Android Build Props</span>
|
||||
</MessageBar>
|
||||
<span>Product Name: {global.device?.product}</span>
|
||||
<span>Product Name: {globalState.device?.product}</span>
|
||||
<Separator />
|
||||
|
||||
<MessageBar>
|
||||
<code>ro.product.model</code>
|
||||
<span> field in Android Build Props</span>
|
||||
</MessageBar>
|
||||
<span>Model Name: {global.device?.model}</span>
|
||||
<span>Model Name: {globalState.device?.model}</span>
|
||||
<Separator />
|
||||
|
||||
<MessageBar>
|
||||
<code>ro.product.device</code>
|
||||
<span> field in Android Build Props</span>
|
||||
</MessageBar>
|
||||
<span>Device Name: {global.device?.device}</span>
|
||||
<span>Device Name: {globalState.device?.device}</span>
|
||||
<Separator />
|
||||
|
||||
<MessageBar>
|
||||
|
@ -87,7 +87,7 @@ const DeviceInfo: NextPage = () => {
|
|||
</MessageBar>
|
||||
<span>
|
||||
<span>Features: </span>
|
||||
{global.device?.features?.map((feature, index) => (
|
||||
{globalState.device?.features?.map((feature, index) => (
|
||||
<span key={feature}>
|
||||
{index !== 0 && (<span>, </span>)}
|
||||
<span>{feature}</span>
|
||||
|
|
|
@ -12,7 +12,7 @@ import Router, { useRouter } from "next/router";
|
|||
import path from 'path';
|
||||
import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
||||
import { CommandBar } from '../components';
|
||||
import { global } from '../state';
|
||||
import { globalState } from '../state';
|
||||
import { asyncEffect, chunkFile, formatSize, formatSpeed, Icons, pickFile, RouteStackProps } from '../utils';
|
||||
|
||||
let StreamSaver: typeof import('streamsaver');
|
||||
|
@ -140,7 +140,7 @@ class FileManagerState {
|
|||
iconName: Icons.CloudArrowUp,
|
||||
style: { height: 20, fontSize: 20, lineHeight: 1.5 }
|
||||
},
|
||||
disabled: !global.device,
|
||||
disabled: !globalState.device,
|
||||
onClick: () => {
|
||||
(async () => {
|
||||
const files = await pickFile({ multiple: true });
|
||||
|
@ -167,7 +167,7 @@ class FileManagerState {
|
|||
},
|
||||
onClick: () => {
|
||||
(async () => {
|
||||
const sync = await global.device!.sync();
|
||||
const sync = await globalState.device!.sync();
|
||||
try {
|
||||
const itemPath = path.resolve(this.path, this.selectedItems[0].name!);
|
||||
const readableStream = createReadableStreamFromBufferIterator(sync.read(itemPath));
|
||||
|
@ -177,7 +177,7 @@ class FileManagerState {
|
|||
});
|
||||
await readableStream.pipeTo(writeableStream);
|
||||
} catch (e) {
|
||||
global.showErrorDialog(e instanceof Error ? e.message : `${e}`);
|
||||
globalState.showErrorDialog(e instanceof Error ? e.message : `${e}`);
|
||||
} finally {
|
||||
sync.dispose();
|
||||
}
|
||||
|
@ -199,14 +199,14 @@ class FileManagerState {
|
|||
(async () => {
|
||||
try {
|
||||
for (const item of this.selectedItems) {
|
||||
const output = await global.device!.rm(path.resolve(this.path, item.name!));
|
||||
const output = await globalState.device!.rm(path.resolve(this.path, item.name!));
|
||||
if (output) {
|
||||
global.showErrorDialog(output);
|
||||
globalState.showErrorDialog(output);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
global.showErrorDialog(e instanceof Error ? e.message : `${e}`);
|
||||
globalState.showErrorDialog(e instanceof Error ? e.message : `${e}`);
|
||||
} finally {
|
||||
this.loadFiles();
|
||||
}
|
||||
|
@ -359,7 +359,7 @@ class FileManagerState {
|
|||
});
|
||||
|
||||
autorun(() => {
|
||||
if (global.device) {
|
||||
if (globalState.device) {
|
||||
if (this.initial && this.visible) {
|
||||
this.initial = false;
|
||||
this.loadFiles();
|
||||
|
@ -381,7 +381,7 @@ class FileManagerState {
|
|||
|
||||
this.path = path;
|
||||
|
||||
if (!global.device) {
|
||||
if (!globalState.device) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -393,13 +393,13 @@ class FileManagerState {
|
|||
|
||||
runInAction(() => this.items = []);
|
||||
|
||||
if (!global.device) {
|
||||
if (!globalState.device) {
|
||||
return;
|
||||
}
|
||||
|
||||
runInAction(() => this.loading = true);
|
||||
|
||||
const sync = await global.device.sync();
|
||||
const sync = await globalState.device.sync();
|
||||
|
||||
const items: ListItem[] = [];
|
||||
const linkItems: AdbSyncEntryResponse[] = [];
|
||||
|
@ -456,7 +456,7 @@ class FileManagerState {
|
|||
});
|
||||
|
||||
upload = async (file: File) => {
|
||||
const sync = await global.device!.sync();
|
||||
const sync = await globalState.device!.sync();
|
||||
try {
|
||||
const itemPath = path.resolve(this.path!, file.name);
|
||||
runInAction(() => {
|
||||
|
@ -491,7 +491,7 @@ class FileManagerState {
|
|||
clearInterval(intervalId);
|
||||
}
|
||||
} catch (e) {
|
||||
global.showErrorDialog(e instanceof Error ? e.message : `${e}`);
|
||||
globalState.showErrorDialog(e instanceof Error ? e.message : `${e}`);
|
||||
} finally {
|
||||
sync.dispose();
|
||||
this.loadFiles();
|
||||
|
@ -566,7 +566,7 @@ const FileManager: NextPage = (): JSX.Element | null => {
|
|||
|
||||
const [previewUrl, setPreviewUrl] = useState<string | undefined>();
|
||||
const previewImage = useCallback(async (path: string) => {
|
||||
const sync = await global.device!.sync();
|
||||
const sync = await globalState.device!.sync();
|
||||
try {
|
||||
const readableStream = createReadableStreamFromBufferIterator(sync.read(path));
|
||||
const response = new Response(readableStream);
|
||||
|
|
|
@ -6,7 +6,7 @@ import { NextPage } from "next";
|
|||
import Head from "next/head";
|
||||
import React, { useCallback, useEffect, useRef } from 'react';
|
||||
import { CommandBar, DemoMode, DeviceView } from '../components';
|
||||
import { global } from "../state";
|
||||
import { globalState } from "../state";
|
||||
import { Icons, RouteStackProps } from "../utils";
|
||||
|
||||
class FrameBufferState {
|
||||
|
@ -38,15 +38,15 @@ const FrameBuffer: NextPage = (): JSX.Element | null => {
|
|||
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||
|
||||
const capture = useCallback(async () => {
|
||||
if (!global.device) {
|
||||
if (!globalState.device) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const framebuffer = await global.device.framebuffer();
|
||||
const framebuffer = await globalState.device.framebuffer();
|
||||
state.setImage(framebuffer);
|
||||
} catch (e) {
|
||||
global.showErrorDialog(e instanceof Error ? e.message : `${e}`);
|
||||
globalState.showErrorDialog(e instanceof Error ? e.message : `${e}`);
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
@ -65,7 +65,7 @@ const FrameBuffer: NextPage = (): JSX.Element | null => {
|
|||
const commandBarItems = computed(() => [
|
||||
{
|
||||
key: 'start',
|
||||
disabled: !global.device,
|
||||
disabled: !globalState.device,
|
||||
iconProps: { iconName: Icons.Camera, style: { height: 20, fontSize: 20, lineHeight: 1.5 } },
|
||||
text: 'Capture',
|
||||
onClick: capture,
|
||||
|
@ -84,7 +84,7 @@ const FrameBuffer: NextPage = (): JSX.Element | null => {
|
|||
const url = canvas.toDataURL();
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `Screenshot of ${global.device!.name}.png`;
|
||||
a.download = `Screenshot of ${globalState.device!.name}.png`;
|
||||
a.click();
|
||||
},
|
||||
},
|
||||
|
|
|
@ -5,7 +5,7 @@ import { observer } from "mobx-react-lite";
|
|||
import { NextPage } from "next";
|
||||
import Head from "next/head";
|
||||
import React from "react";
|
||||
import { global } from "../state";
|
||||
import { globalState } from "../state";
|
||||
import { chunkFile, pickFile, RouteStackProps } from "../utils";
|
||||
|
||||
enum Stage {
|
||||
|
@ -57,7 +57,7 @@ class InstallPageState {
|
|||
};
|
||||
});
|
||||
|
||||
await global.device!.install(chunkFile(file, AdbSyncMaxPacketSize), uploaded => {
|
||||
await globalState.device!.install(chunkFile(file, AdbSyncMaxPacketSize), uploaded => {
|
||||
runInAction(() => {
|
||||
if (uploaded !== file.size) {
|
||||
this.progress = {
|
||||
|
@ -103,7 +103,7 @@ const Install: NextPage = () => {
|
|||
|
||||
<Stack horizontal>
|
||||
<DefaultButton
|
||||
disabled={!global.device || state.installing}
|
||||
disabled={!globalState.device || state.installing}
|
||||
text="Open"
|
||||
onClick={state.install}
|
||||
/>
|
||||
|
|
48
apps/demo/pages/power.tsx
Normal file
48
apps/demo/pages/power.tsx
Normal file
|
@ -0,0 +1,48 @@
|
|||
import { DefaultButton, Icon, MessageBar, TooltipHost } from "@fluentui/react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { NextPage } from "next";
|
||||
import { globalState } from "../state";
|
||||
import { Icons } from "../utils";
|
||||
|
||||
const Power: NextPage = () => {
|
||||
return (
|
||||
<div style={{ padding: 20 }}>
|
||||
<div>
|
||||
<DefaultButton text="Reboot" disabled={!globalState.device} onClick={() => globalState.device!.power.reboot()} />
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 20 }}>
|
||||
<DefaultButton text="Reboot to Bootloader" disabled={!globalState.device} onClick={() => globalState.device!.power.bootloader()} />
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 20 }}>
|
||||
<DefaultButton text="Reboot to Fastboot" disabled={!globalState.device} onClick={() => globalState.device!.power.fastboot()} />
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 20 }}>
|
||||
<DefaultButton text="Reboot to Recovery" disabled={!globalState.device} onClick={() => globalState.device!.power.recovery()} />
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 20 }}>
|
||||
<DefaultButton text="Reboot to Sideload" disabled={!globalState.device} onClick={() => globalState.device!.power.sideload()} />
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 20 }}>
|
||||
<DefaultButton text="Reboot to Qualcomm EDL Mode" disabled={!globalState.device} onClick={() => globalState.device!.power.qualcommEdlMode()} />
|
||||
<TooltipHost content={<span>Only works on some Qualcomm devices.</span>}>
|
||||
<Icon style={{ verticalAlign: 'middle', marginLeft: 4, fontSize: 18 }} iconName={Icons.Info} />
|
||||
</TooltipHost>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 20 }}>
|
||||
<DefaultButton text="Power Off" disabled={!globalState.device} onClick={() => globalState.device!.power.powerOff()} />
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 20 }}>
|
||||
<DefaultButton text="Press Power Button" disabled={!globalState.device} onClick={() => globalState.device!.power.powerButton()} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default observer(Power);
|
|
@ -8,7 +8,7 @@ import { NextPage } from "next";
|
|||
import Head from "next/head";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { DemoMode, DeviceView, DeviceViewRef, ExternalLink } from "../components";
|
||||
import { global } from "../state";
|
||||
import { globalState } from "../state";
|
||||
import { CommonStackTokens, formatSpeed, Icons, RouteStackProps } from "../utils";
|
||||
import SCRCPY_SERVER_VERSION from '@yume-chan/scrcpy/bin/version';
|
||||
|
||||
|
@ -201,7 +201,7 @@ class ScrcpyPageState {
|
|||
if (!this.running) {
|
||||
result.push({
|
||||
key: 'start',
|
||||
disabled: !global.device,
|
||||
disabled: !globalState.device,
|
||||
iconProps: { iconName: Icons.Play },
|
||||
text: 'Start',
|
||||
onClick: this.start as VoidFunction,
|
||||
|
@ -310,7 +310,7 @@ class ScrcpyPageState {
|
|||
});
|
||||
|
||||
autorun(() => {
|
||||
if (global.device) {
|
||||
if (globalState.device) {
|
||||
runInAction(() => {
|
||||
this.encoders = [];
|
||||
this.selectedEncoder = undefined;
|
||||
|
@ -351,7 +351,7 @@ class ScrcpyPageState {
|
|||
}
|
||||
|
||||
start = async () => {
|
||||
if (!global.device) {
|
||||
if (!globalState.device) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -395,7 +395,7 @@ class ScrcpyPageState {
|
|||
}), 1000);
|
||||
|
||||
try {
|
||||
await pushServer(global.device, serverBuffer, {
|
||||
await pushServer(globalState.device, serverBuffer, {
|
||||
onProgress: action((progress) => {
|
||||
this.serverUploadedSize = progress;
|
||||
}),
|
||||
|
@ -409,7 +409,7 @@ class ScrcpyPageState {
|
|||
}
|
||||
|
||||
const encoders = await ScrcpyClient.getEncoders(
|
||||
global.device,
|
||||
globalState.device,
|
||||
DEFAULT_SERVER_PATH,
|
||||
SCRCPY_SERVER_VERSION,
|
||||
new ScrcpyOptions1_21({
|
||||
|
@ -428,7 +428,7 @@ class ScrcpyPageState {
|
|||
|
||||
// Run scrcpy once will delete the server file
|
||||
// Re-push it
|
||||
await pushServer(global.device, serverBuffer);
|
||||
await pushServer(globalState.device, serverBuffer);
|
||||
|
||||
const factory = this.selectedDecoder.factory;
|
||||
const decoder = new factory();
|
||||
|
@ -436,7 +436,7 @@ class ScrcpyPageState {
|
|||
this.decoder = decoder;
|
||||
});
|
||||
|
||||
const client = new ScrcpyClient(global.device);
|
||||
const client = new ScrcpyClient(globalState.device);
|
||||
runInAction(() => this.log = []);
|
||||
client.onOutput(action(line => this.log.push(line)));
|
||||
client.onClose(this.stop);
|
||||
|
@ -487,7 +487,7 @@ class ScrcpyPageState {
|
|||
this.running = true;
|
||||
});
|
||||
} catch (e: any) {
|
||||
global.showErrorDialog(e.message);
|
||||
globalState.showErrorDialog(e.message);
|
||||
} finally {
|
||||
runInAction(() => {
|
||||
this.connecting = false;
|
||||
|
|
|
@ -5,7 +5,7 @@ import { NextPage } from "next";
|
|||
import Head from "next/head";
|
||||
import React, { CSSProperties, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import 'xterm/css/xterm.css';
|
||||
import { global } from "../state";
|
||||
import { globalState } from "../state";
|
||||
import { Icons, ResizeObserver, RouteStackProps } from '../utils';
|
||||
|
||||
let terminal: import('../components/terminal').AdbTerminal;
|
||||
|
@ -40,9 +40,9 @@ const Shell: NextPage = (): JSX.Element | null => {
|
|||
const connectingRef = useRef(false);
|
||||
useEffect(() => {
|
||||
return reaction(
|
||||
() => global.device,
|
||||
() => globalState.device,
|
||||
async () => {
|
||||
if (!global.device) {
|
||||
if (!globalState.device) {
|
||||
terminal.socket = undefined;
|
||||
return;
|
||||
}
|
||||
|
@ -53,10 +53,10 @@ const Shell: NextPage = (): JSX.Element | null => {
|
|||
|
||||
try {
|
||||
connectingRef.current = true;
|
||||
const socket = await global.device.childProcess.shell();
|
||||
const socket = await globalState.device.childProcess.shell();
|
||||
terminal.socket = socket;
|
||||
} catch (e) {
|
||||
global.showErrorDialog(e instanceof Error ? e.message : `${e}`);
|
||||
globalState.showErrorDialog(e instanceof Error ? e.message : `${e}`);
|
||||
} finally {
|
||||
connectingRef.current = false;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import { NextPage } from "next";
|
|||
import Head from "next/head";
|
||||
import React, { useCallback, useEffect } from "react";
|
||||
import { ExternalLink } from "../components";
|
||||
import { global } from "../state";
|
||||
import { globalState } from "../state";
|
||||
import { asyncEffect, Icons, RouteStackProps } from "../utils";
|
||||
|
||||
class TcpIpState {
|
||||
|
@ -26,7 +26,7 @@ class TcpIpState {
|
|||
|
||||
|
||||
autorun(() => {
|
||||
if (global.device) {
|
||||
if (globalState.device) {
|
||||
if (this.initial && this.visible) {
|
||||
this.initial = false;
|
||||
this.queryInfo();
|
||||
|
@ -41,14 +41,14 @@ class TcpIpState {
|
|||
return [
|
||||
{
|
||||
key: 'refresh',
|
||||
disabled: !global.device,
|
||||
disabled: !globalState.device,
|
||||
iconProps: { iconName: Icons.ArrowClockwise },
|
||||
text: 'Refresh',
|
||||
onClick: this.queryInfo as VoidFunction,
|
||||
},
|
||||
{
|
||||
key: 'apply',
|
||||
disabled: !global.device,
|
||||
disabled: !globalState.device,
|
||||
iconProps: { iconName: Icons.Save },
|
||||
text: 'Apply',
|
||||
onClick: this.applyServicePort,
|
||||
|
@ -57,7 +57,7 @@ class TcpIpState {
|
|||
}
|
||||
|
||||
queryInfo = asyncEffect(async (signal) => {
|
||||
if (!global.device) {
|
||||
if (!globalState.device) {
|
||||
runInAction(() => {
|
||||
this.serviceListenAddresses = undefined;
|
||||
this.servicePortEnabled = false;
|
||||
|
@ -68,9 +68,9 @@ class TcpIpState {
|
|||
return;
|
||||
}
|
||||
|
||||
const serviceListenAddresses = await global.device.getProp('service.adb.listen_addrs');
|
||||
const servicePort = await global.device.getProp('service.adb.tcp.port');
|
||||
const persistPort = await global.device.getProp('persist.adb.tcp.port');
|
||||
const serviceListenAddresses = await globalState.device.getProp('service.adb.listen_addrs');
|
||||
const servicePort = await globalState.device.getProp('service.adb.tcp.port');
|
||||
const persistPort = await globalState.device.getProp('persist.adb.tcp.port');
|
||||
|
||||
if (signal.aborted) {
|
||||
return;
|
||||
|
@ -98,14 +98,14 @@ class TcpIpState {
|
|||
});
|
||||
|
||||
applyServicePort = async () => {
|
||||
if (!global.device) {
|
||||
if (!globalState.device) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.servicePortEnabled) {
|
||||
await global.device.tcpip.setPort(Number.parseInt(state.servicePort, 10));
|
||||
await globalState.device.tcpip.setPort(Number.parseInt(state.servicePort, 10));
|
||||
} else {
|
||||
await global.device.tcpip.disable();
|
||||
await globalState.device.tcpip.disable();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -183,12 +183,12 @@ const TcpIp: NextPage = () => {
|
|||
inlineLabel
|
||||
label="service.adb.tcp.port"
|
||||
checked={state.servicePortEnabled}
|
||||
disabled={!global.device || !!state.serviceListenAddresses}
|
||||
disabled={!globalState.device || !!state.serviceListenAddresses}
|
||||
onText="Enabled"
|
||||
offText="Disabled"
|
||||
onChange={handleServicePortEnabledChange}
|
||||
/>
|
||||
{global && (
|
||||
{globalState && (
|
||||
<TextField
|
||||
disabled={!!state.serviceListenAddresses}
|
||||
value={state.servicePort}
|
||||
|
|
|
@ -36,4 +36,4 @@ export class GlobalState {
|
|||
}
|
||||
}
|
||||
|
||||
export const global = new GlobalState();
|
||||
export const globalState = new GlobalState();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { registerIcons } from "@fluentui/react";
|
||||
import { MoreHorizontalRegular, AddCircleRegular, ArrowClockwiseRegular, ArrowSortDownRegular, ArrowSortUpRegular, BookmarkRegular, BoxRegular, CameraRegular, CheckmarkRegular, ChevronDownRegular, ChevronRightRegular, ChevronUpRegular, CircleRegular, CloudArrowDownRegular, CloudArrowUpRegular, CopyRegular, DeleteRegular, DocumentRegular, FolderRegular, FullScreenMaximizeRegular, InfoRegular, NavigationRegular, PersonFeedbackRegular, PhoneLaptopRegular, PhoneRegular, PlayRegular, PlugConnectedRegular, PlugDisconnectedRegular, SaveRegular, SearchRegular, SettingsRegular, StopRegular, TextGrammarErrorRegular, WandRegular, WifiSettingsRegular, WindowConsoleRegular } from '@fluentui/react-icons';
|
||||
import { AddCircleRegular, ArrowClockwiseRegular, ArrowSortDownRegular, ArrowSortUpRegular, BookmarkRegular, BoxRegular, CameraRegular, CheckmarkRegular, ChevronDownRegular, ChevronRightRegular, ChevronUpRegular, CircleRegular, CloudArrowDownRegular, CloudArrowUpRegular, CopyRegular, DeleteRegular, DocumentRegular, FolderRegular, FullScreenMaximizeRegular, InfoRegular, MoreHorizontalRegular, NavigationRegular, PersonFeedbackRegular, PhoneLaptopRegular, PhoneRegular, PlayRegular, PlugConnectedRegular, PlugDisconnectedRegular, PowerRegular, SaveRegular, SearchRegular, SettingsRegular, StopRegular, TextGrammarErrorRegular, WandRegular, WifiSettingsRegular, WindowConsoleRegular } from '@fluentui/react-icons';
|
||||
|
||||
const STYLE = {};
|
||||
|
||||
|
@ -30,6 +30,7 @@ export function register() {
|
|||
Play: <PlayRegular style={STYLE} />,
|
||||
PlugConnected: <PlugConnectedRegular style={STYLE} />,
|
||||
PlugDisconnected: <PlugDisconnectedRegular style={STYLE} />,
|
||||
Power: <PowerRegular style={STYLE} />,
|
||||
Save: <SaveRegular style={STYLE} />,
|
||||
Settings: <SettingsRegular style={STYLE} />,
|
||||
Stop: <StopRegular style={STYLE} />,
|
||||
|
@ -77,6 +78,7 @@ export default {
|
|||
Play: 'Play',
|
||||
PlugConnected: 'PlugConnected',
|
||||
PlugDisconnected: 'PlugDisconnected',
|
||||
Power: 'Power',
|
||||
Save: 'Save',
|
||||
Settings: 'Settings',
|
||||
Stop: 'Stop',
|
||||
|
|
|
@ -2,7 +2,7 @@ import { PromiseResolver } from '@yume-chan/async';
|
|||
import { DisposableList } from '@yume-chan/event';
|
||||
import { AdbAuthenticationHandler, AdbCredentialStore, AdbDefaultAuthenticators } from './auth';
|
||||
import { AdbBackend } from './backend';
|
||||
import { AdbChildProcess, AdbDemoMode, AdbFrameBuffer, AdbReverseCommand, AdbSync, AdbTcpIpCommand, escapeArg, framebuffer, install } from './commands';
|
||||
import { AdbChildProcess, AdbDemoMode, AdbFrameBuffer, AdbPower, AdbReverseCommand, AdbSync, AdbTcpIpCommand, escapeArg, framebuffer, install } from './commands';
|
||||
import { AdbFeatures } from './features';
|
||||
import { AdbCommand } from './packet';
|
||||
import { AdbLogger, AdbPacketDispatcher, AdbSocket } from './socket';
|
||||
|
@ -44,22 +44,21 @@ export class Adb {
|
|||
private _features: AdbFeatures[] | undefined;
|
||||
public get features() { return this._features; }
|
||||
|
||||
public readonly tcpip: AdbTcpIpCommand;
|
||||
|
||||
public readonly reverse: AdbReverseCommand;
|
||||
|
||||
public readonly demoMode: AdbDemoMode;
|
||||
|
||||
public readonly childProcess: AdbChildProcess;
|
||||
public readonly demoMode: AdbDemoMode;
|
||||
public readonly power: AdbPower;
|
||||
public readonly reverse: AdbReverseCommand;
|
||||
public readonly tcpip: AdbTcpIpCommand;
|
||||
|
||||
public constructor(backend: AdbBackend, logger?: AdbLogger) {
|
||||
this._backend = backend;
|
||||
this.packetDispatcher = new AdbPacketDispatcher(backend, logger);
|
||||
|
||||
this.tcpip = new AdbTcpIpCommand(this);
|
||||
this.reverse = new AdbReverseCommand(this.packetDispatcher);
|
||||
this.demoMode = new AdbDemoMode(this);
|
||||
this.childProcess = new AdbChildProcess(this);
|
||||
this.demoMode = new AdbDemoMode(this);
|
||||
this.power = new AdbPower(this);
|
||||
this.reverse = new AdbReverseCommand(this.packetDispatcher);
|
||||
this.tcpip = new AdbTcpIpCommand(this);
|
||||
|
||||
backend.onDisconnected(this.dispose, this);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ export * from './base';
|
|||
export * from './demo-mode';
|
||||
export * from './framebuffer';
|
||||
export * from './install';
|
||||
export * from './power';
|
||||
export * from './reverse';
|
||||
export * from './shell';
|
||||
export * from './sync';
|
||||
|
|
35
libraries/adb/src/commands/power.ts
Normal file
35
libraries/adb/src/commands/power.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { AdbCommandBase } from "./base";
|
||||
|
||||
export class AdbPower extends AdbCommandBase {
|
||||
public reboot(name: string = '') {
|
||||
return this.adb.createSocketAndReadAll(`reboot:${name}`);
|
||||
}
|
||||
|
||||
public bootloader() {
|
||||
return this.reboot('bootloader');
|
||||
}
|
||||
|
||||
public fastboot() {
|
||||
return this.reboot('fastboot');
|
||||
}
|
||||
|
||||
public recovery() {
|
||||
return this.reboot('recovery');
|
||||
}
|
||||
|
||||
public sideload() {
|
||||
return this.reboot('sideload');
|
||||
}
|
||||
|
||||
public qualcommEdlMode() {
|
||||
return this.reboot('edl');
|
||||
}
|
||||
|
||||
public powerOff() {
|
||||
return this.adb.childProcess.exec('reboot', '-p');
|
||||
}
|
||||
|
||||
public powerButton(longPress: boolean = false) {
|
||||
return this.adb.childProcess.exec('input', 'keyevent', longPress ? '--longpress POWER' : 'POWER');
|
||||
}
|
||||
}
|
|
@ -51,7 +51,7 @@ export class AdbReverseCommand extends AutoDisposable {
|
|||
}
|
||||
|
||||
const address = decodeUtf8(e.packet.payload!);
|
||||
// tcp:1234\0
|
||||
// tcp:12345\0
|
||||
const port = Number.parseInt(address.substring(4));
|
||||
if (this.localPortToHandler.has(port)) {
|
||||
this.localPortToHandler.get(port)!.onSocket(e.packet, e.socket);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue