Skip to content

Commit

Permalink
feat: 🎉 Add gnirehtet reverse tethering function
Browse files Browse the repository at this point in the history
  • Loading branch information
viarotel committed Oct 30, 2023
1 parent f9a32d6 commit 2c97189
Show file tree
Hide file tree
Showing 19 changed files with 293 additions and 39 deletions.
10 changes: 8 additions & 2 deletions README-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -135,7 +141,7 @@
8. 添加 macOS 及 linux 操作系统的支持 ✅
9. 支持国际化 ✅
10. 对深色模式的支持 ✅
11. 添加 Gnirehtet 反向供网功能 🚧
11. 添加 Gnirehtet 反向供网功能
12. 添加对游戏的增强功能,如游戏键位映射 🚧

## 常见问题
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
19 changes: 19 additions & 0 deletions electron/configs/gnirehtet/index.js
Original file line number Diff line number Diff line change
@@ -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')
2 changes: 2 additions & 0 deletions electron/configs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
5 changes: 5 additions & 0 deletions electron/exposes/adbkit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -167,6 +171,7 @@ export default () => {
tcpip,
screencap,
install,
isInstalled,
version,
display,
watch,
Expand Down
139 changes: 139 additions & 0 deletions electron/exposes/gnirehtet/index.js
Original file line number Diff line number Diff line change
@@ -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,
}
}
14 changes: 10 additions & 4 deletions electron/exposes/index.js
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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)

Expand All @@ -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 }))
},
}
Binary file not shown.
Binary file not shown.
2 changes: 2 additions & 0 deletions electron/resources/extra/win/gnirehtet/gnirehtet-run.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@gnirehtet.exe run
@pause
Binary file not shown.
Binary file not shown.
15 changes: 3 additions & 12 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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(
`<div>该软件依赖与
<a class="hover:underline text-primary-500" href="https://developer.android.com/studio/releases/platform-tools?hl=zh-cn" target="_blank">adb</a>
以及
`<div>
该软件依赖与
<a class="hover:underline text-primary-500" href="https://github.com/Genymobile/scrcpy" target="_blank">scrcpy</a>
,请确保已正确安装所述依赖项,或者在偏好设置中手动配置依赖项所在位置。
<div>`,
Expand Down
Loading

0 comments on commit 2c97189

Please sign in to comment.