Loading apps/demo/src/components/connect.tsx +5 −2 Original line number Diff line number Diff line Loading @@ -162,6 +162,9 @@ function _Connect(): JSX.Element | null { device = await Adb.authenticate({ readable, writable }, CredentialStore, undefined); device.disconnected.then(() => { globalState.setDevice(undefined, undefined); }, (e) => { globalState.showErrorDialog(e); globalState.setDevice(undefined, undefined); }); globalState.setDevice(selectedBackend, device); } catch (e) { Loading @@ -170,7 +173,7 @@ function _Connect(): JSX.Element | null { } } } catch (e: any) { globalState.showErrorDialog(e.message); globalState.showErrorDialog(e); } finally { setConnecting(false); } Loading @@ -180,7 +183,7 @@ function _Connect(): JSX.Element | null { await globalState.device!.dispose(); globalState.setDevice(undefined, undefined); } catch (e: any) { globalState.showErrorDialog(e.message); globalState.showErrorDialog(e); } }, []); Loading apps/demo/src/pages/file-manager.tsx +6 −6 Original line number Diff line number Diff line Loading @@ -140,8 +140,8 @@ class FileManagerState { const itemPath = path.resolve(this.path, item.name); await sync.read(itemPath) .pipeTo(saveFile(item.name, Number(item.size))); } catch (e) { globalState.showErrorDialog(e instanceof Error ? e.message : `${e}`); } catch (e: any) { globalState.showErrorDialog(e); } finally { sync.dispose(); } Loading Loading @@ -169,8 +169,8 @@ class FileManagerState { return; } } } catch (e) { globalState.showErrorDialog(e instanceof Error ? e.message : `${e}`); } catch (e: any) { globalState.showErrorDialog(e); } finally { this.loadFiles(); } Loading Loading @@ -481,8 +481,8 @@ class FileManagerState { } finally { clearInterval(intervalId); } } catch (e) { globalState.showErrorDialog(e instanceof Error ? e.message : `${e}`); } catch (e: any) { globalState.showErrorDialog(e); } finally { sync.dispose(); this.loadFiles(); Loading apps/demo/src/pages/framebuffer.tsx +2 −2 Original line number Diff line number Diff line Loading @@ -45,8 +45,8 @@ const FrameBuffer: NextPage = (): JSX.Element | null => { try { const framebuffer = await globalState.device.framebuffer(); state.setImage(framebuffer); } catch (e) { globalState.showErrorDialog(e instanceof Error ? e.message : `${e}`); } catch (e: any) { globalState.showErrorDialog(e); } }, []); Loading apps/demo/src/pages/logcat.tsx 0 → 100644 +301 −0 Original line number Diff line number Diff line // cspell: ignore logcat import { ICommandBarItemProps, Stack, StackItem } from "@fluentui/react"; import { makeStyles, mergeClasses, shorthands } from "@griffel/react"; import { AbortController, decodeUtf8, ReadableStream, WritableStream } from '@yume-chan/adb'; import { Logcat, LogMessage, LogPriority } from '@yume-chan/android-bin'; import { autorun, makeAutoObservable, observable, runInAction } from "mobx"; import { observer } from "mobx-react-lite"; import { NextPage } from "next"; import Head from "next/head"; import { CommandBar, Grid, GridColumn, GridHeaderProps, GridRowProps } from "../components"; import { globalState } from "../state"; import { Icons, RouteStackProps, useCallbackRef } from "../utils"; const LINE_HEIGHT = 32; const useClasses = makeStyles({ grid: { height: '100%', marginLeft: '-16px', marginRight: '-16px', }, header: { textAlign: 'center', lineHeight: `${LINE_HEIGHT}px`, }, row: { '&:hover': { backgroundColor: '#f3f2f1', }, }, selected: { backgroundColor: '#edebe9', }, code: { fontFamily: 'monospace', textOverflow: 'ellipsis', whiteSpace: 'nowrap', lineHeight: LINE_HEIGHT + 'px', cursor: 'default', ...shorthands.overflow('hidden'), }, }); export interface Column extends GridColumn { title: string; } export interface LogRow extends LogMessage { timeString?: string; payloadString?: string; } const state = makeAutoObservable({ logcat: undefined as Logcat | undefined, running: false, list: [] as LogRow[], stream: undefined as ReadableStream<LogMessage> | undefined, stopSignal: undefined as AbortController | undefined, selectedCount: 0, start() { if (this.running) { return; } this.running = true; this.stream = this.logcat!.binary(); this.stopSignal = new AbortController(); this.stream .pipeTo( new WritableStream({ write: (chunk) => { runInAction(() => { this.list.push(chunk); }); }, }), { signal: this.stopSignal.signal } ) .catch(() => { }); }, stop() { this.running = false; this.stopSignal!.abort(); }, clear() { this.list = []; this.selectedCount = 0; }, get empty() { return this.list.length === 0; }, get commandBar(): ICommandBarItemProps[] { return [ this.running ? { key: "stop", text: "Stop", iconProps: { iconName: Icons.Stop }, onClick: () => this.stop(), } : { key: "start", text: "Start", disabled: this.logcat === undefined, iconProps: { iconName: Icons.Play }, onClick: () => this.start(), }, { key: 'clear', text: 'Clear', disabled: this.empty, iconProps: { iconName: Icons.Delete }, onClick: () => this.clear(), }, { key: 'copyAll', text: 'Copy Rows', disabled: this.selectedCount === 0, iconProps: { iconName: Icons.Copy }, onClick: () => { } }, { key: 'copyText', text: 'Copy Messages', disabled: this.selectedCount === 0, iconProps: { iconName: Icons.Copy }, onClick: () => { } } ]; }, get columns(): Column[] { return [ { width: 200, title: 'Time', CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => { const item = this.list[rowIndex]; if (!item.timeString) { item.timeString = new Date(item.second * 1000).toISOString(); } const classes = useClasses(); return ( <div className={mergeClasses(classes.code, className)} {...rest}> {item.timeString} </div> ); } }, { width: 80, title: 'PID', CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => { const item = this.list[rowIndex]; const classes = useClasses(); return ( <div className={mergeClasses(classes.code, className)} {...rest}> {item.pid} </div> ); } }, { width: 80, title: 'TID', CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => { const item = this.list[rowIndex]; const classes = useClasses(); return ( <div className={mergeClasses(classes.code, className)} {...rest}> {item.tid} </div> ); } }, { width: 100, title: 'Priority', CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => { const item = this.list[rowIndex]; const classes = useClasses(); return ( <div className={mergeClasses(classes.code, className)} {...rest}> {LogPriority[item.priority]} </div> ); } }, { width: 300, flexGrow: 1, title: 'Payload', CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => { const item = this.list[rowIndex]; if (!item.payloadString) { item.payloadString = decodeUtf8(item.payload); } const classes = useClasses(); return ( <div className={mergeClasses(classes.code, className)} {...rest}> {item.payloadString} </div> ); } }, ]; }, }, { list: observable.shallow, }); console.log(state); autorun(() => { if (globalState.device) { state.logcat = new Logcat(globalState.device); } else { state.logcat = undefined; if (state.running) { state.stop(); } } }); const Header = observer(function Header({ className, columnIndex, ...rest }: GridHeaderProps) { const classes = useClasses(); return ( <div className={mergeClasses(className, classes.header)} {...rest}> {state.columns[columnIndex].title} </div> ); }); const Row = observer(function Row({ className, rowIndex, ...rest }: GridRowProps) { const item = state.list[rowIndex]; const classes = useClasses(); const handleClick = useCallbackRef(() => { runInAction(() => { }); }); return ( <div className={mergeClasses( className, classes.row, )} onClick={handleClick} {...rest} /> ); }); const LogcatPage: NextPage = () => { const classes = useClasses(); return ( <Stack {...RouteStackProps}> <Head> <title>Logcat - Android Web Toolbox</title> </Head> <CommandBar items={state.commandBar} /> <StackItem grow> <Grid className={classes.grid} rowCount={state.list.length} rowHeight={LINE_HEIGHT} columns={state.columns} HeaderComponent={Header} RowComponent={Row} /> </StackItem> </Stack> ); }; export default observer(LogcatPage); apps/demo/src/pages/packet-log.tsx +141 −140 Original line number Diff line number Diff line Loading @@ -5,7 +5,6 @@ import { autorun, makeAutoObservable, observable, runInAction } from "mobx"; import { observer } from "mobx-react-lite"; import { NextPage } from "next"; import Head from "next/head"; import { useMemo } from "react"; import { CommandBar, Grid, GridCellProps, GridColumn, GridHeaderProps, GridRowProps, HexViewer, toText } from "../components"; import { globalState, PacketLogItem } from "../state"; import { Icons, RouteStackProps, useCallbackRef, withDisplayName } from "../utils"; Loading Loading @@ -89,17 +88,15 @@ const useClasses = makeStyles({ }, }); const PacketLog: NextPage = () => { const classes = useClasses(); const columns: Column[] = useMemo(() => [ const columns: Column[] = [ { key: 'direction', title: 'Direction', width: 100, CellComponent: withDisplayName('Direction')(({ className, rowIndex, ...rest }: GridCellProps) => { const item = globalState.logs[rowIndex]; const classes = useClasses(); return ( <div className={mergeClasses(className, classes.code)} Loading @@ -111,7 +108,6 @@ const PacketLog: NextPage = () => { }), }, { key: 'command', title: 'Command', width: 100, CellComponent: withDisplayName('Command')(({ className, rowIndex, ...rest }: GridCellProps) => { Loading @@ -123,6 +119,8 @@ const PacketLog: NextPage = () => { decodeUtf8(new Uint32Array([item.command])); } const classes = useClasses(); return ( <div className={mergeClasses(className, classes.code)} Loading @@ -134,7 +132,6 @@ const PacketLog: NextPage = () => { }), }, { key: 'arg0', title: 'Arg0', width: 100, CellComponent: withDisplayName('Command')(({ className, rowIndex, ...rest }: GridCellProps) => { Loading @@ -144,6 +141,8 @@ const PacketLog: NextPage = () => { item.arg0String = item.arg0.toString(16).padStart(8, '0'); } const classes = useClasses(); return ( <div className={mergeClasses(className, classes.code)} Loading @@ -155,7 +154,6 @@ const PacketLog: NextPage = () => { }), }, { key: 'arg1', title: 'Arg1', width: 100, CellComponent: withDisplayName('Command')(({ className, rowIndex, ...rest }: GridCellProps) => { Loading @@ -165,6 +163,8 @@ const PacketLog: NextPage = () => { item.arg1String = item.arg0.toString(16).padStart(8, '0'); } const classes = useClasses(); return ( <div className={mergeClasses(className, classes.code)} Loading @@ -176,7 +176,6 @@ const PacketLog: NextPage = () => { }), }, { key: 'payload', title: 'Payload', width: 200, flexGrow: 1, Loading @@ -187,6 +186,8 @@ const PacketLog: NextPage = () => { item.payloadString = toText(item.payload.subarray(0, 100)); } const classes = useClasses(); return ( <div className={mergeClasses(className, classes.code)} Loading @@ -197,30 +198,29 @@ const PacketLog: NextPage = () => { ); }), }, ], [classes.code]); ]; const Header = useMemo( () => withDisplayName('Header')(({ const Header = withDisplayName('Header')(({ className, columnIndex, ...rest }: GridHeaderProps) => { const classes = useClasses(); return ( <div className={mergeClasses(className, classes.header)} {...rest}> {columns[columnIndex].title} </div> ); }), [classes.header, columns] ); }); const Row = useMemo( () => observer(function Row({ const Row = observer(function Row({ className, rowIndex, ...rest }: GridRowProps) { /* eslint-disable-next-line */ const classes = useClasses(); const handleClick = useCallbackRef(() => { runInAction(() => { state.selectedPacket = globalState.logs[rowIndex]; Loading @@ -238,9 +238,10 @@ const PacketLog: NextPage = () => { {...rest} /> ); }), [classes] ); }); const PacketLog: NextPage = () => { const classes = useClasses(); return ( <Stack {...RouteStackProps} tokens={{}}> Loading Loading
apps/demo/src/components/connect.tsx +5 −2 Original line number Diff line number Diff line Loading @@ -162,6 +162,9 @@ function _Connect(): JSX.Element | null { device = await Adb.authenticate({ readable, writable }, CredentialStore, undefined); device.disconnected.then(() => { globalState.setDevice(undefined, undefined); }, (e) => { globalState.showErrorDialog(e); globalState.setDevice(undefined, undefined); }); globalState.setDevice(selectedBackend, device); } catch (e) { Loading @@ -170,7 +173,7 @@ function _Connect(): JSX.Element | null { } } } catch (e: any) { globalState.showErrorDialog(e.message); globalState.showErrorDialog(e); } finally { setConnecting(false); } Loading @@ -180,7 +183,7 @@ function _Connect(): JSX.Element | null { await globalState.device!.dispose(); globalState.setDevice(undefined, undefined); } catch (e: any) { globalState.showErrorDialog(e.message); globalState.showErrorDialog(e); } }, []); Loading
apps/demo/src/pages/file-manager.tsx +6 −6 Original line number Diff line number Diff line Loading @@ -140,8 +140,8 @@ class FileManagerState { const itemPath = path.resolve(this.path, item.name); await sync.read(itemPath) .pipeTo(saveFile(item.name, Number(item.size))); } catch (e) { globalState.showErrorDialog(e instanceof Error ? e.message : `${e}`); } catch (e: any) { globalState.showErrorDialog(e); } finally { sync.dispose(); } Loading Loading @@ -169,8 +169,8 @@ class FileManagerState { return; } } } catch (e) { globalState.showErrorDialog(e instanceof Error ? e.message : `${e}`); } catch (e: any) { globalState.showErrorDialog(e); } finally { this.loadFiles(); } Loading Loading @@ -481,8 +481,8 @@ class FileManagerState { } finally { clearInterval(intervalId); } } catch (e) { globalState.showErrorDialog(e instanceof Error ? e.message : `${e}`); } catch (e: any) { globalState.showErrorDialog(e); } finally { sync.dispose(); this.loadFiles(); Loading
apps/demo/src/pages/framebuffer.tsx +2 −2 Original line number Diff line number Diff line Loading @@ -45,8 +45,8 @@ const FrameBuffer: NextPage = (): JSX.Element | null => { try { const framebuffer = await globalState.device.framebuffer(); state.setImage(framebuffer); } catch (e) { globalState.showErrorDialog(e instanceof Error ? e.message : `${e}`); } catch (e: any) { globalState.showErrorDialog(e); } }, []); Loading
apps/demo/src/pages/logcat.tsx 0 → 100644 +301 −0 Original line number Diff line number Diff line // cspell: ignore logcat import { ICommandBarItemProps, Stack, StackItem } from "@fluentui/react"; import { makeStyles, mergeClasses, shorthands } from "@griffel/react"; import { AbortController, decodeUtf8, ReadableStream, WritableStream } from '@yume-chan/adb'; import { Logcat, LogMessage, LogPriority } from '@yume-chan/android-bin'; import { autorun, makeAutoObservable, observable, runInAction } from "mobx"; import { observer } from "mobx-react-lite"; import { NextPage } from "next"; import Head from "next/head"; import { CommandBar, Grid, GridColumn, GridHeaderProps, GridRowProps } from "../components"; import { globalState } from "../state"; import { Icons, RouteStackProps, useCallbackRef } from "../utils"; const LINE_HEIGHT = 32; const useClasses = makeStyles({ grid: { height: '100%', marginLeft: '-16px', marginRight: '-16px', }, header: { textAlign: 'center', lineHeight: `${LINE_HEIGHT}px`, }, row: { '&:hover': { backgroundColor: '#f3f2f1', }, }, selected: { backgroundColor: '#edebe9', }, code: { fontFamily: 'monospace', textOverflow: 'ellipsis', whiteSpace: 'nowrap', lineHeight: LINE_HEIGHT + 'px', cursor: 'default', ...shorthands.overflow('hidden'), }, }); export interface Column extends GridColumn { title: string; } export interface LogRow extends LogMessage { timeString?: string; payloadString?: string; } const state = makeAutoObservable({ logcat: undefined as Logcat | undefined, running: false, list: [] as LogRow[], stream: undefined as ReadableStream<LogMessage> | undefined, stopSignal: undefined as AbortController | undefined, selectedCount: 0, start() { if (this.running) { return; } this.running = true; this.stream = this.logcat!.binary(); this.stopSignal = new AbortController(); this.stream .pipeTo( new WritableStream({ write: (chunk) => { runInAction(() => { this.list.push(chunk); }); }, }), { signal: this.stopSignal.signal } ) .catch(() => { }); }, stop() { this.running = false; this.stopSignal!.abort(); }, clear() { this.list = []; this.selectedCount = 0; }, get empty() { return this.list.length === 0; }, get commandBar(): ICommandBarItemProps[] { return [ this.running ? { key: "stop", text: "Stop", iconProps: { iconName: Icons.Stop }, onClick: () => this.stop(), } : { key: "start", text: "Start", disabled: this.logcat === undefined, iconProps: { iconName: Icons.Play }, onClick: () => this.start(), }, { key: 'clear', text: 'Clear', disabled: this.empty, iconProps: { iconName: Icons.Delete }, onClick: () => this.clear(), }, { key: 'copyAll', text: 'Copy Rows', disabled: this.selectedCount === 0, iconProps: { iconName: Icons.Copy }, onClick: () => { } }, { key: 'copyText', text: 'Copy Messages', disabled: this.selectedCount === 0, iconProps: { iconName: Icons.Copy }, onClick: () => { } } ]; }, get columns(): Column[] { return [ { width: 200, title: 'Time', CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => { const item = this.list[rowIndex]; if (!item.timeString) { item.timeString = new Date(item.second * 1000).toISOString(); } const classes = useClasses(); return ( <div className={mergeClasses(classes.code, className)} {...rest}> {item.timeString} </div> ); } }, { width: 80, title: 'PID', CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => { const item = this.list[rowIndex]; const classes = useClasses(); return ( <div className={mergeClasses(classes.code, className)} {...rest}> {item.pid} </div> ); } }, { width: 80, title: 'TID', CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => { const item = this.list[rowIndex]; const classes = useClasses(); return ( <div className={mergeClasses(classes.code, className)} {...rest}> {item.tid} </div> ); } }, { width: 100, title: 'Priority', CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => { const item = this.list[rowIndex]; const classes = useClasses(); return ( <div className={mergeClasses(classes.code, className)} {...rest}> {LogPriority[item.priority]} </div> ); } }, { width: 300, flexGrow: 1, title: 'Payload', CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => { const item = this.list[rowIndex]; if (!item.payloadString) { item.payloadString = decodeUtf8(item.payload); } const classes = useClasses(); return ( <div className={mergeClasses(classes.code, className)} {...rest}> {item.payloadString} </div> ); } }, ]; }, }, { list: observable.shallow, }); console.log(state); autorun(() => { if (globalState.device) { state.logcat = new Logcat(globalState.device); } else { state.logcat = undefined; if (state.running) { state.stop(); } } }); const Header = observer(function Header({ className, columnIndex, ...rest }: GridHeaderProps) { const classes = useClasses(); return ( <div className={mergeClasses(className, classes.header)} {...rest}> {state.columns[columnIndex].title} </div> ); }); const Row = observer(function Row({ className, rowIndex, ...rest }: GridRowProps) { const item = state.list[rowIndex]; const classes = useClasses(); const handleClick = useCallbackRef(() => { runInAction(() => { }); }); return ( <div className={mergeClasses( className, classes.row, )} onClick={handleClick} {...rest} /> ); }); const LogcatPage: NextPage = () => { const classes = useClasses(); return ( <Stack {...RouteStackProps}> <Head> <title>Logcat - Android Web Toolbox</title> </Head> <CommandBar items={state.commandBar} /> <StackItem grow> <Grid className={classes.grid} rowCount={state.list.length} rowHeight={LINE_HEIGHT} columns={state.columns} HeaderComponent={Header} RowComponent={Row} /> </StackItem> </Stack> ); }; export default observer(LogcatPage);
apps/demo/src/pages/packet-log.tsx +141 −140 Original line number Diff line number Diff line Loading @@ -5,7 +5,6 @@ import { autorun, makeAutoObservable, observable, runInAction } from "mobx"; import { observer } from "mobx-react-lite"; import { NextPage } from "next"; import Head from "next/head"; import { useMemo } from "react"; import { CommandBar, Grid, GridCellProps, GridColumn, GridHeaderProps, GridRowProps, HexViewer, toText } from "../components"; import { globalState, PacketLogItem } from "../state"; import { Icons, RouteStackProps, useCallbackRef, withDisplayName } from "../utils"; Loading Loading @@ -89,17 +88,15 @@ const useClasses = makeStyles({ }, }); const PacketLog: NextPage = () => { const classes = useClasses(); const columns: Column[] = useMemo(() => [ const columns: Column[] = [ { key: 'direction', title: 'Direction', width: 100, CellComponent: withDisplayName('Direction')(({ className, rowIndex, ...rest }: GridCellProps) => { const item = globalState.logs[rowIndex]; const classes = useClasses(); return ( <div className={mergeClasses(className, classes.code)} Loading @@ -111,7 +108,6 @@ const PacketLog: NextPage = () => { }), }, { key: 'command', title: 'Command', width: 100, CellComponent: withDisplayName('Command')(({ className, rowIndex, ...rest }: GridCellProps) => { Loading @@ -123,6 +119,8 @@ const PacketLog: NextPage = () => { decodeUtf8(new Uint32Array([item.command])); } const classes = useClasses(); return ( <div className={mergeClasses(className, classes.code)} Loading @@ -134,7 +132,6 @@ const PacketLog: NextPage = () => { }), }, { key: 'arg0', title: 'Arg0', width: 100, CellComponent: withDisplayName('Command')(({ className, rowIndex, ...rest }: GridCellProps) => { Loading @@ -144,6 +141,8 @@ const PacketLog: NextPage = () => { item.arg0String = item.arg0.toString(16).padStart(8, '0'); } const classes = useClasses(); return ( <div className={mergeClasses(className, classes.code)} Loading @@ -155,7 +154,6 @@ const PacketLog: NextPage = () => { }), }, { key: 'arg1', title: 'Arg1', width: 100, CellComponent: withDisplayName('Command')(({ className, rowIndex, ...rest }: GridCellProps) => { Loading @@ -165,6 +163,8 @@ const PacketLog: NextPage = () => { item.arg1String = item.arg0.toString(16).padStart(8, '0'); } const classes = useClasses(); return ( <div className={mergeClasses(className, classes.code)} Loading @@ -176,7 +176,6 @@ const PacketLog: NextPage = () => { }), }, { key: 'payload', title: 'Payload', width: 200, flexGrow: 1, Loading @@ -187,6 +186,8 @@ const PacketLog: NextPage = () => { item.payloadString = toText(item.payload.subarray(0, 100)); } const classes = useClasses(); return ( <div className={mergeClasses(className, classes.code)} Loading @@ -197,30 +198,29 @@ const PacketLog: NextPage = () => { ); }), }, ], [classes.code]); ]; const Header = useMemo( () => withDisplayName('Header')(({ const Header = withDisplayName('Header')(({ className, columnIndex, ...rest }: GridHeaderProps) => { const classes = useClasses(); return ( <div className={mergeClasses(className, classes.header)} {...rest}> {columns[columnIndex].title} </div> ); }), [classes.header, columns] ); }); const Row = useMemo( () => observer(function Row({ const Row = observer(function Row({ className, rowIndex, ...rest }: GridRowProps) { /* eslint-disable-next-line */ const classes = useClasses(); const handleClick = useCallbackRef(() => { runInAction(() => { state.selectedPacket = globalState.logs[rowIndex]; Loading @@ -238,9 +238,10 @@ const PacketLog: NextPage = () => { {...rest} /> ); }), [classes] ); }); const PacketLog: NextPage = () => { const classes = useClasses(); return ( <Stack {...RouteStackProps} tokens={{}}> Loading