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

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

feat(adb): add common device list observer for both transports

parent c91baa91
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@
    "dependencies": {
        "@types/w3c-web-usb": "^1.0.10",
        "@yume-chan/adb": "workspace:^",
        "@yume-chan/event": "workspace:^",
        "@yume-chan/stream-extra": "workspace:^",
        "@yume-chan/struct": "workspace:^"
    },
+1 −1
Original line number Diff line number Diff line
export * from "./device.js";
export * from "./manager.js";
export * from "./utils.js";
export * from "./watcher.js";
export * from "./observer.js";
+7 −35
Original line number Diff line number Diff line
import { AdbDaemonWebUsbDevice, toAdbDeviceFilters } from "./device.js";
import {
    findUsbAlternateInterface,
    getSerialNumber,
    isErrorName,
} from "./utils.js";
import { AdbDaemonWebUsbDeviceObserver } from "./observer.js";
import { isErrorName, matchesFilters } from "./utils.js";

export namespace AdbDaemonWebUsbDeviceManager {
    export interface RequestDeviceOptions {
@@ -76,36 +73,7 @@ export class AdbDaemonWebUsbDeviceManager {

        const devices = await this.#usbManager.getDevices();
        return devices
            .filter((device) => {
                for (const filter of filters) {
                    if (
                        filter.vendorId !== undefined &&
                        device.vendorId !== filter.vendorId
                    ) {
                        continue;
                    }
                    if (
                        filter.productId !== undefined &&
                        device.productId !== filter.productId
                    ) {
                        continue;
                    }
                    if (
                        filter.serialNumber !== undefined &&
                        getSerialNumber(device) !== filter.serialNumber
                    ) {
                        continue;
                    }

                    try {
                        findUsbAlternateInterface(device, filters);
                        return true;
                    } catch {
                        continue;
                    }
                }
                return false;
            })
            .filter((device) => matchesFilters(device, filters))
            .map(
                (device) =>
                    new AdbDaemonWebUsbDevice(
@@ -115,4 +83,8 @@ export class AdbDaemonWebUsbDeviceManager {
                    ),
            );
    }

    trackDevices(filters?: USBDeviceFilter[]): AdbDaemonWebUsbDeviceObserver {
        return new AdbDaemonWebUsbDeviceObserver(this.#usbManager, filters);
    }
}
+85 −0
Original line number Diff line number Diff line
import type { DeviceObserver } from "@yume-chan/adb";
import { EventEmitter } from "@yume-chan/event";

import { AdbDaemonWebUsbDevice, toAdbDeviceFilters } from "./device.js";
import type { UsbInterfaceFilter } from "./utils.js";
import { matchesFilters } from "./utils.js";

/**
 * A watcher that listens for new WebUSB devices and notifies the callback when
 * a new device is connected or disconnected.
 *
 * [Online Documentation](https://docs.tangoapp.dev/tango/daemon/usb/watch-devices/)
 */
export class AdbDaemonWebUsbDeviceObserver
    implements DeviceObserver<AdbDaemonWebUsbDevice>
{
    #filters: UsbInterfaceFilter[];
    #usbManager: USB;

    #deviceAdded = new EventEmitter<AdbDaemonWebUsbDevice[]>();
    deviceAdded = this.#deviceAdded.event;

    #deviceRemoved = new EventEmitter<AdbDaemonWebUsbDevice[]>();
    deviceRemoved = this.#deviceRemoved.event;

    #deviceChanged = new EventEmitter<AdbDaemonWebUsbDevice[]>();
    deviceChanged = this.#deviceChanged.event;

    #listChanged = new EventEmitter<AdbDaemonWebUsbDevice[]>();
    listChanged = this.#listChanged.event;

    current: AdbDaemonWebUsbDevice[] = [];

    constructor(usb: USB, filters?: USBDeviceFilter[]) {
        this.#filters = toAdbDeviceFilters(filters);
        this.#usbManager = usb;

        this.#usbManager.addEventListener("connect", this.#handleConnect);
        this.#usbManager.addEventListener("disconnect", this.#handleDisconnect);
    }

    dispose(): void {
        this.#usbManager.removeEventListener("connect", this.#handleConnect);
        this.#usbManager.removeEventListener(
            "disconnect",
            this.#handleDisconnect,
        );
    }

    #handleConnect = (e: USBConnectionEvent) => {
        if (!matchesFilters(e.device, this.#filters)) {
            return;
        }

        const device = new AdbDaemonWebUsbDevice(
            e.device,
            this.#filters,
            this.#usbManager,
        );
        this.#deviceAdded.fire([device]);
        this.current.push(device);
        this.#listChanged.fire(this.current);
    };

    #handleDisconnect = (e: USBConnectionEvent) => {
        const index = this.current.findIndex(
            (device) => device.raw === e.device,
        );
        if (index !== -1) {
            const device = this.current[index]!;
            this.#deviceRemoved.fire([device]);
            this.current[index] = this.current[this.current.length - 1]!;
            this.current.length -= 1;
            this.#listChanged.fire(this.current);
        }
    };

    stop(): void {
        this.#usbManager.removeEventListener("connect", this.#handleConnect);
        this.#usbManager.removeEventListener(
            "disconnect",
            this.#handleDisconnect,
        );
    }
}
+34 −0
Original line number Diff line number Diff line
@@ -99,3 +99,37 @@ export function findUsbEndpoints(endpoints: USBEndpoint[]) {
    }
    throw new Error("unreachable");
}

export function matchesFilters(
    device: USBDevice,
    filters: (USBDeviceFilter & UsbInterfaceFilter)[],
) {
    for (const filter of filters) {
        if (
            filter.vendorId !== undefined &&
            device.vendorId !== filter.vendorId
        ) {
            continue;
        }
        if (
            filter.productId !== undefined &&
            device.productId !== filter.productId
        ) {
            continue;
        }
        if (
            filter.serialNumber !== undefined &&
            getSerialNumber(device) !== filter.serialNumber
        ) {
            continue;
        }

        try {
            findUsbAlternateInterface(device, filters);
            return true;
        } catch {
            continue;
        }
    }
    return false;
}
Loading