Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit d1e3c771 authored by Simon Chan's avatar Simon Chan
Browse files

feat(bin): support bugreport/bugreportz

fix #361
parent d96b4d2b
Loading
Loading
Loading
Loading
+1 −7
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ export class AdbTerminal extends AutoDisposable {

            this._socketAbortController = new AbortController();

            // pty mode only has one stream
            value.stdout.pipeTo(new WritableStream<Uint8Array>({
                write: (chunk) => {
                    this.terminal.write(chunk);
@@ -43,13 +44,6 @@ export class AdbTerminal extends AutoDisposable {
            }), {
                signal: this._socketAbortController.signal,
            });
            value.stderr.pipeTo(new WritableStream<Uint8Array>({
                write: (chunk) => {
                    this.terminal.write(chunk);
                },
            }), {
                signal: this._socketAbortController.signal,
            });

            const _writer = value.stdin.getWriter();
            this.addDisposable(this.terminal.onData(data => {
+1 −1
Original line number Diff line number Diff line
@@ -18,7 +18,7 @@ const withMDX = require('@next/mdx')({
module.exports = withMDX({
    basePath: process.env.BASE_PATH || '',
    pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'],
    reactStrictMode: true,
    reactStrictMode: false,
    productionBrowserSourceMaps: true,
    experimental: {
        // Workaround https://github.com/vercel/next.js/issues/33914
+5 −0
Original line number Diff line number Diff line
@@ -57,6 +57,11 @@ const ROUTES = [
        icon: Icons.Power,
        name: 'Power Menu',
    },
    {
        url: '/bug-report',
        icon: Icons.Bug,
        name: 'Bug Report',
    },
];

function NavLink({ link, defaultRender: DefaultRender, ...props }: IComponentAsProps<INavButtonProps>) {
+149 −0
Original line number Diff line number Diff line
// cspell: ignore bugreport
// cspell: ignore bugreportz

import { MessageBar, MessageBarType, PrimaryButton, Stack, StackItem } from "@fluentui/react";
import { BugReport, BugReportZ, BugReportZVersion } from "@yume-chan/android-bin";
import { action, autorun, makeAutoObservable, observable, runInAction } from "mobx";
import { observer } from "mobx-react-lite";
import { NextPage } from "next";
import Head from "next/head";
import { globalState } from "../state";
import { RouteStackProps, saveFile } from "../utils";

class BugReportState {
    bugReport: BugReport | undefined = undefined;

    bugReportZ: BugReportZ | undefined = undefined;

    bugReportZVersion: BugReportZVersion | undefined = undefined;

    bugReportZInProgress = false;

    bugReportZProgress: string | undefined = undefined;

    bugReportZTotalSize: string | undefined = undefined;

    constructor() {
        makeAutoObservable(this, {
            bugReportZVersion: observable.deep,
            generateBugReport: action.bound,
            generateBugReportZStream: action.bound,
            generateBugReportZ: action.bound,
        });

        autorun(() => {
            if (globalState.device) {
                runInAction(() => {
                    this.bugReport = new BugReport(globalState.device!);
                    this.bugReportZ = new BugReportZ(globalState.device!);

                    this.bugReportZ.version().then(action(version => {
                        this.bugReportZVersion = version;
                    }));
                });
            } else {
                runInAction(() => {
                    this.bugReport = undefined;
                    this.bugReportZ = undefined;
                    this.bugReportZVersion = undefined;
                });
            }
        });
    }

    async generateBugReport() {
        await this.bugReport!.generate()
            .pipeTo(saveFile('bugreport.txt'));
    }

    async generateBugReportZStream() {
        await this.bugReportZ!.stream()
            .pipeTo(saveFile('bugreport.zip'));
    }

    async generateBugReportZ() {
        runInAction(() => {
            this.bugReportZInProgress = true;
        });

        const filename = await this.bugReportZ!.generate(
            this.bugReportZVersion!.supportProgress
                ? action((progress, total) => {
                    this.bugReportZProgress = progress;
                    this.bugReportZTotalSize = total;
                })
                : undefined
        );

        const sync = await globalState.device!.sync();
        await sync.read(filename)
            .pipeTo(saveFile('bugreport.zip'));

        sync.dispose();

        runInAction(() => {
            this.bugReportZInProgress = false;
            this.bugReportZProgress = undefined;
            this.bugReportZTotalSize = undefined;
        });
    }
}

const state = new BugReportState();

const BugReportPage: NextPage = () => {
    return (
        <Stack {...RouteStackProps}>
            <Head>
                <title>BugReport - WebADB</title>
            </Head>

            <MessageBar messageBarType={MessageBarType.info}>This is the `bugreport`/`bugreportz` tool in Android</MessageBar>

            <StackItem>
                <PrimaryButton
                    disabled={!state.bugReport}
                    text="Generate BugReport"
                    onClick={state.generateBugReport}
                />
            </StackItem>

            <StackItem>
                <PrimaryButton
                    disabled={!state.bugReportZVersion?.supportStream}
                    text="Generate Zipped BugReport (Streaming)"
                    onClick={state.generateBugReportZStream}
                />
            </StackItem>

            <StackItem>
                <Stack horizontal verticalAlign="center" tokens={{ childrenGap: 8 }}>
                    <StackItem>
                        <PrimaryButton
                            disabled={!state.bugReportZVersion || state.bugReportZInProgress}
                            text="Generate Zipped BugReport"
                            onClick={state.generateBugReportZ}
                        />
                    </StackItem>

                    {state.bugReportZInProgress && (
                        <StackItem>
                            {state.bugReportZTotalSize ? (
                                <span>
                                    Progress: {state.bugReportZProgress} / {state.bugReportZTotalSize}
                                </span>
                            ) : (
                                <span>
                                    Generating... Please wait
                                    {!state.bugReportZVersion!.supportProgress && ' (this device does not support progress)'}
                                </span>
                            )}
                        </StackItem>
                    )}
                </Stack>
            </StackItem>
        </Stack>
    );
};

export default observer(BugReportPage);
+4 −19
Original line number Diff line number Diff line
@@ -2,30 +2,20 @@ import { Breadcrumb, concatStyleSets, ContextualMenu, ContextualMenuItem, Detail
import { FileIconType, getFileTypeIconProps, initializeFileTypeIcons } from "@fluentui/react-file-type-icons";
import { useConst } from '@fluentui/react-hooks';
import { getIcon } from '@fluentui/style-utilities';
import { AdbSyncEntryResponse, ADB_SYNC_MAX_PACKET_SIZE, ChunkStream, LinuxFileType, ReadableStream, WritableStream } from '@yume-chan/adb';
import { AdbSyncEntryResponse, ADB_SYNC_MAX_PACKET_SIZE, ChunkStream, LinuxFileType, ReadableStream } from '@yume-chan/adb';
import { action, autorun, makeAutoObservable, observable, runInAction } from "mobx";
import { observer } from "mobx-react-lite";
import { NextPage } from "next";
import getConfig from "next/config";
import Head from "next/head";
import Router, { useRouter } from "next/router";
import path from 'path';
import { useCallback, useEffect, useState } from 'react';
import { CommandBar, NoSsr } from '../components';
import { globalState } from '../state';
import { asyncEffect, formatSize, formatSpeed, Icons, pickFile, ProgressStream, RouteStackProps } from '../utils';
import { asyncEffect, formatSize, formatSpeed, Icons, pickFile, ProgressStream, RouteStackProps, saveFile } from '../utils';

initializeFileTypeIcons();

let StreamSaver: typeof import('streamsaver');
if (typeof window !== 'undefined') {
    const { publicRuntimeConfig } = getConfig();
    // Can't use `import` here because ESM is read-only (can't set `mitm` field)
    // Add `await` here because top-level await is on, so every import can be a `Promise`
    StreamSaver = await require('streamsaver');
    StreamSaver.mitm = publicRuntimeConfig.basePath + '/StreamSaver/mitm.html';
}

interface ListItem extends AdbSyncEntryResponse {
    key: string;
}
@@ -148,13 +138,8 @@ class FileManagerState {
                                try {
                                    const item = this.selectedItems[0];
                                    const itemPath = path.resolve(this.path, item.name);
                                    const readable = sync.read(itemPath);

                                    const writeable: WritableStream<Uint8Array> = StreamSaver!.createWriteStream(
                                        item.name,
                                        { size: item.size }
                                    ) as any;
                                    await readable.pipeTo(writeable);
                                    await sync.read(itemPath)
                                        .pipeTo(saveFile(item.name, item.size));
                                } catch (e) {
                                    globalState.showErrorDialog(e instanceof Error ? e.message : `${e}`);
                                } finally {
Loading