Update: Something spicy for recipe viewing only but called it editor lol
This commit is contained in:
parent
72187f348b
commit
7dd075dd59
8 changed files with 450 additions and 346 deletions
165
client-electron/electron/adb-native.ts
Normal file
165
client-electron/electron/adb-native.ts
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
import { Adb, AdbDaemonTransport } from '@yume-chan/adb'
|
||||
import { type AdbDaemonWebUsbDevice, AdbDaemonWebUsbDeviceManager } from '@yume-chan/adb-daemon-webusb'
|
||||
import { type BrowserWindow } from 'electron'
|
||||
import { WebUSB } from 'usb'
|
||||
|
||||
import { type AdbCredentialStore, adbGeneratePublicKey } from '@yume-chan/adb'
|
||||
import { webcrypto } from 'node:crypto'
|
||||
import { readFile, writeFile } from 'node:fs/promises'
|
||||
import { homedir, hostname, userInfo } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
|
||||
class AdbNodeJsCredentialStore implements AdbCredentialStore {
|
||||
#name: string
|
||||
|
||||
constructor(name: string) {
|
||||
this.#name = name
|
||||
}
|
||||
|
||||
#privateKeyPath() {
|
||||
return join(homedir(), '.android', 'adbkey')
|
||||
}
|
||||
|
||||
#publicKeyPath() {
|
||||
return join(homedir(), '.android', 'adbkey.pub')
|
||||
}
|
||||
|
||||
async generateKey() {
|
||||
const { privateKey: cryptoKey } = await webcrypto.subtle.generateKey(
|
||||
{
|
||||
name: 'RSASSA-PKCS1-v1_5',
|
||||
modulusLength: 2048,
|
||||
// 65537
|
||||
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
||||
hash: 'SHA-1'
|
||||
},
|
||||
true,
|
||||
['sign', 'verify']
|
||||
)
|
||||
|
||||
const privateKey = new Uint8Array(await crypto.subtle.exportKey('pkcs8', cryptoKey))
|
||||
await writeFile(this.#privateKeyPath(), Buffer.from(privateKey).toString('utf8'))
|
||||
await writeFile(
|
||||
this.#publicKeyPath(),
|
||||
`${Buffer.from(adbGeneratePublicKey(privateKey)).toString('base64')} ${this.#name}\n`
|
||||
)
|
||||
|
||||
return {
|
||||
buffer: privateKey,
|
||||
name: this.#name
|
||||
}
|
||||
}
|
||||
|
||||
async #readPubKeyName() {
|
||||
const content = await readFile(this.#publicKeyPath(), 'utf8')
|
||||
const pubKeyName = content.split(' ')[1]
|
||||
return pubKeyName || `${userInfo().username}@${hostname()}`
|
||||
}
|
||||
|
||||
async *iterateKeys() {
|
||||
const content = await readFile(this.#privateKeyPath(), 'utf8')
|
||||
const privateKey = Buffer.from(content.split('\n').slice(1, -2).join(''), 'base64')
|
||||
yield {
|
||||
buffer: privateKey,
|
||||
name: await this.#readPubKeyName()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type ConnectedDevice = {
|
||||
[serial: string]: {
|
||||
adb: Adb
|
||||
metadata: {
|
||||
venderId?: number
|
||||
productId?: number
|
||||
manufacturerNam?: string
|
||||
productName?: string
|
||||
serialNumber?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class AdbUsbNative {
|
||||
private _manager: AdbDaemonWebUsbDeviceManager
|
||||
private _win: BrowserWindow
|
||||
|
||||
private _connectedDevices: ConnectedDevice = {}
|
||||
|
||||
constructor(win: BrowserWindow) {
|
||||
const webUsb: WebUSB = new WebUSB({ allowedDevices: [{ serialNumber: 'd' }] })
|
||||
this._manager = new AdbDaemonWebUsbDeviceManager(webUsb)
|
||||
this._win = win
|
||||
}
|
||||
|
||||
getAvailableDevices(responseChannel: string) {
|
||||
this._manager.getDevices().then(devices => {
|
||||
this._win.webContents.send(
|
||||
responseChannel,
|
||||
devices.map(d => ({
|
||||
vendorId: d.raw.vendorId,
|
||||
productId: d.raw.productId,
|
||||
manufacturerNam: d.raw.manufacturerName,
|
||||
productName: d.raw.productName,
|
||||
serialNumber: d.raw.serialNumber
|
||||
}))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
getConnectedDevices(responseChannel: string) {
|
||||
this._win.webContents.send(
|
||||
responseChannel,
|
||||
Object.entries(this._connectedDevices).map(([serial, { metadata }]) => ({
|
||||
serial: serial,
|
||||
productId: metadata.productId,
|
||||
venderId: metadata.venderId,
|
||||
manufacturerNam: metadata.manufacturerNam,
|
||||
productName: metadata.productName
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
||||
connectDevice(responseChanel: string, serial: string) {
|
||||
this._manager.getDevices().then(devices => {
|
||||
let device: AdbDaemonWebUsbDevice | null = null
|
||||
for (const d of devices) {
|
||||
if (d.serial === serial) {
|
||||
device = d
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!device) {
|
||||
this._win.webContents.send(responseChanel, {
|
||||
error: 'Device with serialNumber: ' + serial + ' not found'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
device.connect().then(connection => {
|
||||
const credentialStore = new AdbNodeJsCredentialStore(`${userInfo().username}@${hostname()}`)
|
||||
|
||||
AdbDaemonTransport.authenticate({
|
||||
serial: device!.serial,
|
||||
connection,
|
||||
credentialStore
|
||||
}).then(transport => {
|
||||
this._connectedDevices[device!.serial] = {
|
||||
adb: new Adb(transport),
|
||||
metadata: {
|
||||
venderId: device!.raw.vendorId,
|
||||
productId: device!.raw.productId,
|
||||
manufacturerNam: device!.raw.manufacturerName,
|
||||
productName: device!.raw.productName,
|
||||
serialNumber: device!.raw.serialNumber
|
||||
}
|
||||
}
|
||||
|
||||
this._win.webContents.send(responseChanel, {
|
||||
serial: device!.serial
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue