diff --git a/README-CN.md b/README-CN.md index c72fbe81..fe239992 100644 --- a/README-CN.md +++ b/README-CN.md @@ -54,12 +54,18 @@ ### macOS && Linux -> 注意:这些平台没有集成 [Adb](https://developer.android.com/studio/releases/platform-tools?hl=zh-cn) 及 [Scrcpy](https://github.com/Genymobile/scrcpy) 需要手动安装 +> 注意:这些平台没有集成 [Scrcpy](https://github.com/Genymobile/scrcpy) 需要手动安装 1. Linux 可参阅的 [安装文档](https://github.com/Genymobile/scrcpy/blob/master/doc/linux.md) 2. macOS 可参阅的 [安装文档](https://github.com/Genymobile/scrcpy/blob/master/doc/macos.md) 3. 安装上述依赖成功后步骤同 USB 连接 和 WIFI 连接 +### Gnirehtet 反向供网 + +> 注意: macOS 内部没有集成如需使用需要手动安装 [安装文档](https://github.com/Genymobile/gnirehtet) + +Windows 及 Linux 端内部集成了 Gnirehtet, 用于提供 PC 到安卓设备的反向供网功能。 + ## 快捷键 请参阅 [scrcpy/doc/shortcuts](https://github.com/Genymobile/scrcpy/blob/master/doc/shortcuts.md) @@ -135,7 +141,7 @@ 8. 添加 macOS 及 linux 操作系统的支持 ✅ 9. 支持国际化 ✅ 10. 对深色模式的支持 ✅ -11. 添加 Gnirehtet 反向供网功能 🚧 +11. 添加 Gnirehtet 反向供网功能 ✅ 12. 添加对游戏的增强功能,如游戏键位映射 🚧 ## 常见问题 diff --git a/README.md b/README.md index 821c9eaa..82aad97e 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,12 @@ 2. Refer to the [installation document](https://github.com/Genymobile/scrcpy/blob/master/doc/macos.md) for macOS 3. Follow steps in USB Connection and WIFI Connection after dependencies are installed successfully +### Gnirehtet Reverse Tethering + +> Note: macOS does not have Gnirehtet built-in. You need to manually install it to use this feature [Installation Guide](https://github.com/Genymobile/gnirehtet). + +Gnirehtet is built into the Windows and Linux apps to provide reverse tethering from PC to Android devices. + ## Shortcuts Refer to [scrcpy/doc/shortcuts](https://github.com/Genymobile/scrcpy/blob/master/doc/shortcuts.md) @@ -133,7 +139,7 @@ Refer to [scrcpy/doc/shortcuts](https://github.com/Genymobile/scrcpy/blob/master 8. Add support for macOS and linux operating systems ✅ 9. Support internationalization ✅ 10. Support for dark mode ✅ -11. Add Gnirehtet reverse network function 🚧 +11. Add Gnirehtet reverse network function ✅ 12. Add game enhancement features such as game keyboard mapping 🚧 ## FAQ diff --git a/electron/configs/gnirehtet/index.js b/electron/configs/gnirehtet/index.js new file mode 100644 index 00000000..fa05e935 --- /dev/null +++ b/electron/configs/gnirehtet/index.js @@ -0,0 +1,19 @@ +import { extraResolve } from '@electron/helpers/index.js' +import which from 'which' + +export const getGnirehtetPath = () => { + switch (process.platform) { + // case 'darwin': + // return extraResolve('mac/gnirehtet/gnirehtet') + case 'win32': + return extraResolve('win/gnirehtet/gnirehtet.exe') + case 'linux': + return extraResolve('linux/gnirehtet/gnirehtet') + default: + return which.sync('gnirehtet', { nothrow: true }) + } +} + +export const gnirehtetPath = getGnirehtetPath() + +export const gnirehtetApkPath = extraResolve('common/gnirehtet/gnirehtet.apk') diff --git a/electron/configs/index.js b/electron/configs/index.js index 99868ece..03bb8e6e 100644 --- a/electron/configs/index.js +++ b/electron/configs/index.js @@ -6,6 +6,8 @@ export { adbPath } from './android-platform-tools/index.js' export { scrcpyPath } from './scrcpy/index.js' +export { gnirehtetPath, gnirehtetApkPath } from './gnirehtet/index.js' + export const desktopPath = process.env.DESKTOP_PATH export const devPublishPath = resolve('dev-publish.yml') diff --git a/electron/exposes/adbkit/index.js b/electron/exposes/adbkit/index.js index 3450e1e0..faf8b17d 100644 --- a/electron/exposes/adbkit/index.js +++ b/electron/exposes/adbkit/index.js @@ -54,6 +54,8 @@ const getDeviceIP = async (id) => { const reg = /inet ([0-9.]+)\/\d+/ const match = stdout.match(reg) const value = match[1] + + console.log('adbkit.getDeviceIP', value) return value } catch (error) { @@ -97,6 +99,8 @@ const screencap = async (deviceId, options = {}) => { const install = async (id, path) => client.getDevice(id).install(path) +const isInstalled = async (id, pkg) => client.getDevice(id).isInstalled(pkg) + const version = async () => client.version() const display = async (deviceId) => { @@ -167,6 +171,7 @@ export default () => { tcpip, screencap, install, + isInstalled, version, display, watch, diff --git a/electron/exposes/gnirehtet/index.js b/electron/exposes/gnirehtet/index.js new file mode 100644 index 00000000..df8a1ade --- /dev/null +++ b/electron/exposes/gnirehtet/index.js @@ -0,0 +1,139 @@ +import { spawn } from 'node:child_process' +import appStore from '@electron/helpers/store.js' +import { + adbPath, + gnirehtetApkPath, + gnirehtetPath, +} from '@electron/configs/index.js' + +const appDebug = appStore.get('common.debug') || false + +let adbkit = null + +const shell = async (command, { debug = false, stdout, stderr } = {}) => { + const spawnPath = appStore.get('common.gnirehtet') || gnirehtetPath + const ADB = appStore.get('common.adbPath') || adbPath + + const GNIREHTET_APK = gnirehtetApkPath + + const args = command.split(' ') + + console.log('gnirehtet.shell.spawnPath', spawnPath) + console.log('gnirehtet.shell.adbPath', adbPath) + + const gnirehtetProcess = spawn(`"${spawnPath}"`, args, { + env: { ...process.env, ADB, GNIREHTET_APK }, + shell: true, + encoding: 'utf8', + }) + + gnirehtetProcess.stdout.on('data', (data) => { + const stringData = data.toString() + + if (debug) { + console.log('gnirehtetProcess.stdout.data:', stringData) + } + + if (stdout) { + stdout(stringData, gnirehtetProcess) + } + }) + + gnirehtetProcess.stderr.on('data', (data) => { + const stringData = data.toString() + + if (debug) { + console.error('gnirehtetProcess.stderr.data:', stringData) + } + + if (stderr) { + stderr(stringData, gnirehtetProcess) + } + }) + + return new Promise((resolve, reject) => { + gnirehtetProcess.on('close', (code) => { + if (code === 0) { + resolve() + } + else { + reject(new Error(`Command failed with code ${code}`)) + } + }) + + gnirehtetProcess.on('error', (err) => { + reject(err) + }) + }) +} + +let relayProcess = null +const relay = async (args) => { + if (relayProcess) { + return relayProcess + } + + return new Promise((resolve, reject) => { + shell('relay', { + ...args, + debug: appDebug, + stdout: (_, process) => { + if (!relayProcess) { + relayProcess = process + } + resolve(process) + }, + }).catch((error) => { + reject(error) + }) + }) +} + +const install = deviceId => shell(`install ${deviceId}`) +const start = deviceId => shell(`start ${deviceId}`) +const stop = deviceId => shell(`stop ${deviceId}`) +const tunnel = deviceId => shell(`tunnel ${deviceId}`) + +const installed = async (deviceId) => { + const res = await adbkit.isInstalled(deviceId, 'com.genymobile.gnirehtet') + console.log('gnirehtet.apk.installed', res) + return res +} + +const run = async (deviceId) => { + await relay().catch((e) => { + throw new Error('Gnirehtet Relay fail') + }) + console.log('run.relay.success') + await install(deviceId).catch((e) => { + throw new Error('Gnirehtet Install Client fail') + }) + console.log('run.install.success') + await start(deviceId).catch((e) => { + throw new Error('Gnirehtet Start fail') + }) + console.log('run.start.success') +} + +window.addEventListener('beforeunload', () => { + stop() + + if (relayProcess) { + relayProcess.kill() + } +}) + +export default (options = {}) => { + adbkit = options.adbkit + + return { + shell, + relay, + install, + installed, + start, + stop, + tunnel, + run, + } +} diff --git a/electron/exposes/index.js b/electron/exposes/index.js index b796143e..0ebc81d6 100644 --- a/electron/exposes/index.js +++ b/electron/exposes/index.js @@ -1,6 +1,6 @@ import path from 'node:path' -import log from '@electron/helpers/log.js' +import appLog from '@electron/helpers/log.js' import '@electron/helpers/console.js' import store from '@electron/helpers/store.js' @@ -9,12 +9,13 @@ import * as configs from '@electron/configs/index.js' import electron from './electron/index.js' import adbkit from './adbkit/index.js' import scrcpy from './scrcpy/index.js' +import gnirehtet from './gnirehtet/index.js' export default { init(expose) { expose('nodePath', path) - expose('appLog', log) + expose('appLog', appLog) expose('appStore', store) @@ -23,7 +24,12 @@ export default { configs, }) - expose('adbkit', adbkit({ log })) - expose('scrcpy', scrcpy({ log })) + const adbkitExecute = adbkit() + + expose('adbkit', adbkitExecute) + + expose('scrcpy', scrcpy()) + + expose('gnirehtet', gnirehtet({ adbkit: adbkitExecute })) }, } diff --git a/electron/resources/extra/common/gnirehtet/gnirehtet.apk b/electron/resources/extra/common/gnirehtet/gnirehtet.apk new file mode 100644 index 00000000..98ea978e Binary files /dev/null and b/electron/resources/extra/common/gnirehtet/gnirehtet.apk differ diff --git a/electron/resources/extra/linux/gnirehtet/gnirehtet b/electron/resources/extra/linux/gnirehtet/gnirehtet new file mode 100644 index 00000000..fe321830 Binary files /dev/null and b/electron/resources/extra/linux/gnirehtet/gnirehtet differ diff --git a/electron/resources/extra/win/gnirehtet/gnirehtet-run.cmd b/electron/resources/extra/win/gnirehtet/gnirehtet-run.cmd new file mode 100644 index 00000000..ffefea65 --- /dev/null +++ b/electron/resources/extra/win/gnirehtet/gnirehtet-run.cmd @@ -0,0 +1,2 @@ +@gnirehtet.exe run +@pause diff --git a/electron/resources/extra/win/gnirehtet/gnirehtet.apk b/electron/resources/extra/win/gnirehtet/gnirehtet.apk new file mode 100644 index 00000000..98ea978e Binary files /dev/null and b/electron/resources/extra/win/gnirehtet/gnirehtet.apk differ diff --git a/electron/resources/extra/win/gnirehtet/gnirehtet.exe b/electron/resources/extra/win/gnirehtet/gnirehtet.exe new file mode 100644 index 00000000..ccc57824 Binary files /dev/null and b/electron/resources/extra/win/gnirehtet/gnirehtet.exe differ diff --git a/src/App.vue b/src/App.vue index 83349a91..13eceeb1 100644 --- a/src/App.vue +++ b/src/App.vue @@ -53,24 +53,15 @@ export default { }, methods: { async showTips() { - if (this.$electron.process.platform === 'win32') { - return false - } - - const { adbPath, scrcpyPath } = this.$electron?.configs || {} - - if (adbPath) { - return false - } + const { scrcpyPath } = this.$electron?.configs || {} if (scrcpyPath) { return false } this.$alert( - `