调整文件目录
|
After Width: | Height: | Size: 116 KiB |
@@ -2,10 +2,11 @@
|
|||||||
chcp 65001 >nul
|
chcp 65001 >nul
|
||||||
cd /d "%~dp0"
|
cd /d "%~dp0"
|
||||||
set "ROOT=%~dp0.."
|
set "ROOT=%~dp0.."
|
||||||
set "TOOLS=%ROOT%\tools"
|
set "TOOLS=%ROOT%\tools"
|
||||||
set "NAME=Qiyuan_07_Multi-lan-installer"
|
set "NAME=Qiyuan_07_Multi-lan-installer"
|
||||||
set "SRC=Qiyuan_A07_Multi-lan-installer.py"
|
set "SRC=Qiyuan_A07_Multi-lan-installer.py"
|
||||||
title %NAME% - Build
|
set "ICON=%~dp0app.ico"
|
||||||
|
title %NAME% - Build
|
||||||
|
|
||||||
echo ============================================================
|
echo ============================================================
|
||||||
echo %NAME% - Cython Build
|
echo %NAME% - Cython Build
|
||||||
@@ -13,13 +14,18 @@ echo ============================================================
|
|||||||
echo.
|
echo.
|
||||||
|
|
||||||
where python >nul 2>&1
|
where python >nul 2>&1
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
echo [ERROR] Python not found
|
echo [ERROR] Python not found
|
||||||
pause
|
pause
|
||||||
exit /b
|
exit /b
|
||||||
)
|
)
|
||||||
for /f "delims=" %%i in ('where python') do set PY=%%i
|
if not exist "%ICON%" (
|
||||||
echo Python: %PY%
|
echo [ERROR] app.ico not found in A07 folder: %ICON%
|
||||||
|
pause
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
for /f "delims=" %%i in ('where python') do set PY=%%i
|
||||||
|
echo Python: %PY%
|
||||||
|
|
||||||
echo [1/6] Installing deps...
|
echo [1/6] Installing deps...
|
||||||
%PY% -m pip install pyinstaller cython pyzipper -q
|
%PY% -m pip install pyinstaller cython pyzipper -q
|
||||||
@@ -58,14 +64,14 @@ copy "%PYD%" _core.pyd >nul
|
|||||||
%PY% -c "open('launcher.py','w').write('# -*- coding: utf-8 -*-\nfrom _core import main\nmain()\n')"
|
%PY% -c "open('launcher.py','w').write('# -*- coding: utf-8 -*-\nfrom _core import main\nmain()\n')"
|
||||||
|
|
||||||
echo [4/6] Copy resources...
|
echo [4/6] Copy resources...
|
||||||
copy "%TOOLS%\adb.exe" . >nul
|
copy "%TOOLS%\adb.exe" . >nul
|
||||||
copy "%TOOLS%\AdbWinApi.dll" . >nul
|
copy "%TOOLS%\AdbWinApi.dll" . >nul
|
||||||
copy "%TOOLS%\AdbWinUsbApi.dll" . >nul
|
copy "%TOOLS%\AdbWinUsbApi.dll" . >nul
|
||||||
copy "%TOOLS%\7za.exe" . >nul
|
copy "%TOOLS%\7za.exe" . >nul
|
||||||
if exist "%ROOT%\app.ico" copy "%ROOT%\app.ico" . >nul
|
copy "%ICON%" . >nul
|
||||||
|
|
||||||
echo [5/6] PyInstaller...
|
echo [5/6] PyInstaller...
|
||||||
%PY% -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ROOT%\app.ico" --add-data "%TOOLS%\adb.exe;." --add-data "%TOOLS%\AdbWinApi.dll;." --add-data "%TOOLS%\AdbWinUsbApi.dll;." --add-data "%TOOLS%\7za.exe;." --add-binary "_core.pyd;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin launcher.py
|
%PY% -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ICON%" --add-data "%ICON%;." --add-data "%TOOLS%\adb.exe;." --add-data "%TOOLS%\AdbWinApi.dll;." --add-data "%TOOLS%\AdbWinUsbApi.dll;." --add-data "%TOOLS%\7za.exe;." --add-binary "_core.pyd;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin launcher.py
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
cd ..
|
cd ..
|
||||||
echo [ERROR] PyInstaller failed
|
echo [ERROR] PyInstaller failed
|
||||||
@@ -79,9 +85,9 @@ rmdir /s /q build 2>nul
|
|||||||
cd ..
|
cd ..
|
||||||
goto :DONE
|
goto :DONE
|
||||||
|
|
||||||
:NORMAL
|
:NORMAL
|
||||||
echo [INFO] Normal PyInstaller...
|
echo [INFO] Normal PyInstaller...
|
||||||
%PY% -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ROOT%\app.ico" --add-data "%TOOLS%\adb.exe;." --add-data "%TOOLS%\AdbWinApi.dll;." --add-data "%TOOLS%\AdbWinUsbApi.dll;." --add-data "%TOOLS%\7za.exe;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin %SRC%
|
%PY% -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ICON%" --add-data "%ICON%;." --add-data "%TOOLS%\adb.exe;." --add-data "%TOOLS%\AdbWinApi.dll;." --add-data "%TOOLS%\AdbWinUsbApi.dll;." --add-data "%TOOLS%\7za.exe;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin %SRC%
|
||||||
|
|
||||||
:DONE
|
:DONE
|
||||||
echo.
|
echo.
|
||||||
|
|||||||
@@ -10,14 +10,17 @@ Most tools are single-file tkinter apps with the same rough architecture: GUI, V
|
|||||||
|
|
||||||
| Tool file | Vehicle / purpose | Window title | Pack script |
|
| Tool file | Vehicle / purpose | Window title | Pack script |
|
||||||
|-----------|-------------------|--------------|-------------|
|
|-----------|-------------------|--------------|-------------|
|
||||||
| `Q07/app.py` | 启源Q07 | 长安语言安装工具 | `Q07/pack_q07.bat` |
|
| `Q07/Qiyuan_Q07-multi-lan-installer.py` | 启源Q07 | 适用于启源Q07多语言安装 | `Q07/pack_q07.bat` |
|
||||||
| `S05/S05.py` | 深蓝S05 original | 深蓝S05多语言安装 | `S05/pack_s05.bat` |
|
| `S05/S05.py` | 深蓝S05 original | 深蓝S05多语言安装 | `S05/pack_s05.bat` |
|
||||||
| `S05/S05_fixed.py` | 深蓝S05 fixed/experimental copy | 长安语言安装工具 | `S05/pack_s05_fixed.bat` |
|
| `S05/S05_fixed.py` | 深蓝S05 fixed/experimental copy | 长安语言安装工具 | `S05/pack_s05_fixed.bat` |
|
||||||
| `X5plus/X5plusTool.py` | X5plus | 适用于X5plus多语言安装 | `X5plus/pack_x5plus.bat` |
|
| `X5plus/X5plusTool.py` | X5plus | 适用于X5plus多语言安装 | `X5plus/pack_x5plus.bat` |
|
||||||
| `Yidong/app-install.py` | 长安逸动通用 | 长安语言刷入工具 | `Yidong/pack_common.bat` |
|
| `CS55-Q05/CS55-Q05_Installer.py` | CS55Plus/Q05 通用 | CS55Plus/Q05 语言刷入工具 | `CS55-Q05/pack_cs55_q05.bat` |
|
||||||
|
| `CS75Pro/CS75Pro_Installer.py` | CS75Pro | CS75Pro 语言刷入工具 | `CS75Pro/pack_cs75pro.bat` |
|
||||||
| `Yidong/app-yidong.py` | 长安逸动 | 长安逸动语言刷入工具 | `Yidong/pack_yidong.bat` |
|
| `Yidong/app-yidong.py` | 长安逸动 | 长安逸动语言刷入工具 | `Yidong/pack_yidong.bat` |
|
||||||
| `UNIZ/UNIZ.py` | UNI-Z file pusher | UNI-Z语言文件推送工具 | `UNIZ/pack_uniz.bat` |
|
| `UNIZ/UNIZ.py` | UNI-Z file pusher | UNI-Z语言文件推送工具 | `UNIZ/pack_uniz.bat` |
|
||||||
| `Mazda-EZ60/Mazda-EZ60.py` | Mazda-EZ60 OS 1.2 | Mazda-EZ60_OS-1.2适用 | `Mazda-EZ60/pack_mazda_ez60.bat` |
|
| `UNI-T/UNI-T-multi-lan-installer.py` | UNI-T | 适用于UNI-T多语言安装 | `UNI-T/pack_unit.bat` |
|
||||||
|
| `Mazda-EZ60/Mazda-EZ60_1.2.py` | Mazda-EZ60 OS 1.2 | Mazda-EZ60_OS-1.2适用 | `Mazda-EZ60/pack_mazda_ez60_1.2.bat` |
|
||||||
|
| `Mazda-EZ60/Mazda_EZ60-Language-Install_v1.0.py` | Mazda-EZ60 OS 1.0 | 马自达EZ60刷机工具_OS-1.0 | `Mazda-EZ60/pack_mazda_ez60_1.0.bat` |
|
||||||
| `Q05-Lidar/Q05-Lidar_Installer.py` | Q05_Lidar permission/bootstrap + language installer | Q05_Lidar | `Q05-Lidar/pack_q05_lidar.bat` |
|
| `Q05-Lidar/Q05-Lidar_Installer.py` | Q05_Lidar permission/bootstrap + language installer | Q05_Lidar | `Q05-Lidar/pack_q05_lidar.bat` |
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
@@ -26,8 +29,11 @@ Most tools are single-file tkinter apps with the same rough architecture: GUI, V
|
|||||||
├── Q07/ # Q07 script, pack scripts, ignored build outputs
|
├── Q07/ # Q07 script, pack scripts, ignored build outputs
|
||||||
├── S05/ # S05 original and fixed copy
|
├── S05/ # S05 original and fixed copy
|
||||||
├── X5plus/ # X5plus tool
|
├── X5plus/ # X5plus tool
|
||||||
├── Yidong/ # common/yidong tools
|
├── CS55-Q05/ # CS55Plus/Q05 common installer and icon
|
||||||
|
├── CS75Pro/ # CS75Pro installer copied from CS55-Q05 flow
|
||||||
|
├── Yidong/ # Yidong-specific tool
|
||||||
├── UNIZ/ # UNI-Z file pusher
|
├── UNIZ/ # UNI-Z file pusher
|
||||||
|
├── UNI-T/ # UNI-T installer cloned from Q07 flow
|
||||||
├── Mazda-EZ60/ # Mazda-EZ60 tool
|
├── Mazda-EZ60/ # Mazda-EZ60 tool
|
||||||
├── A07/ # Qiyuan A07 tool
|
├── A07/ # Qiyuan A07 tool
|
||||||
├── Q05-Lidar/ # Q05_Lidar tool plus resource.dat/tools
|
├── Q05-Lidar/ # Q05_Lidar tool plus resource.dat/tools
|
||||||
@@ -54,30 +60,33 @@ Most tools are single-file tkinter apps with the same rough architecture: GUI, V
|
|||||||
|
|
||||||
- `run_adb_command(command)` handles normal ADB commands such as `adb devices`, `adb push`, and non-shell install calls.
|
- `run_adb_command(command)` handles normal ADB commands such as `adb devices`, `adb push`, and non-shell install calls.
|
||||||
- `run_adb_shell(shell_command)` exists in the 逸动-family tools and Mazda copy; it shells into the device and automatically sends password `adb36987`.
|
- `run_adb_shell(shell_command)` exists in the 逸动-family tools and Mazda copy; it shells into the device and automatically sends password `adb36987`.
|
||||||
- All `adb shell` operations in `Yidong/app-install.py`, `Yidong/app-yidong.py`, and `Mazda-EZ60/Mazda-EZ60.py` should go through `run_adb_shell()`.
|
- All `adb shell` operations in `CS55-Q05/CS55-Q05_Installer.py`, `CS75Pro/CS75Pro_Installer.py`, `Yidong/app-yidong.py`, and `Mazda-EZ60/Mazda-EZ60_1.2.py` should go through `run_adb_shell()`.
|
||||||
- `UNIZ/UNIZ.py` must not use `adb shell`; it only checks devices and pushes APKs to `/storage/emulated/0/Download/`.
|
- `UNIZ/UNIZ.py` must not use `adb shell`; it only checks devices and pushes APKs to `/storage/emulated/0/Download/`.
|
||||||
- Standard auth flow uses:
|
- Standard auth flow uses:
|
||||||
- `auth-check?vin=...` for authorization.
|
- `auth-check?vin=...` for authorization.
|
||||||
- `package-key?vin=...` for `package.bin` extraction password.
|
- `package-key?vin=...` for `package.bin` extraction password.
|
||||||
- VIN keys:
|
- VIN keys:
|
||||||
- Q07/S05/X5plus: `ca_vin_info` or `VIN`.
|
- Q07/S05/X5plus: `ca_vin_info` or `VIN`.
|
||||||
|
- UNI-T: try `persist.vendor.car.VIN`, then `settings get global VIN`, then legacy system VIN keys.
|
||||||
- 逸动/Mazda: `settings get system ca.car.vin` via auto-password shell.
|
- 逸动/Mazda: `settings get system ca.car.vin` via auto-password shell.
|
||||||
- UNI-Z: user manually enters VIN.
|
- UNI-Z: user manually enters VIN.
|
||||||
|
|
||||||
## Package Extraction
|
## Package Extraction
|
||||||
|
|
||||||
- `package.bin` is extracted under `%LOCALAPPDATA%\.cache\system\.android\...`.
|
- `package.bin`-style encrypted resources are extracted under `%LOCALAPPDATA%\.cache\system\.android\...`.
|
||||||
- Current cache directories:
|
- Current cache directories:
|
||||||
- `Q07/app.py` -> `apps_cache_Q07`
|
- `Q07/Qiyuan_Q07-multi-lan-installer.py` -> `apps_cache_Q07`
|
||||||
- `S05/S05.py` / `S05/S05_fixed.py` -> `apps_cache_S05`
|
- `S05/S05.py` / `S05/S05_fixed.py` -> `apps_cache_S05`
|
||||||
- `X5plus/X5plusTool.py` -> `apps_cache_X5plus`
|
- `X5plus/X5plusTool.py` -> `apps_cache_X5plus`
|
||||||
- `Yidong/app-install.py` -> `apps_cache_common`
|
- `CS55-Q05/CS55-Q05_Installer.py` -> `apps_cache_common`
|
||||||
|
- `CS75Pro/CS75Pro_Installer.py` -> `apps_cache_CS75Pro`
|
||||||
- `Yidong/app-yidong.py` -> `apps_cache_yidong`
|
- `Yidong/app-yidong.py` -> `apps_cache_yidong`
|
||||||
- `UNIZ/UNIZ.py` -> `apps_cache_UNIZ`
|
- `UNIZ/UNIZ.py` -> `apps_cache_UNIZ`
|
||||||
- `Mazda-EZ60/Mazda-EZ60.py` -> `apps_cache_Mazda_EZ60`
|
- `UNI-T/UNI-T-multi-lan-installer.py` -> `apps_cache_UNI_T`
|
||||||
|
- `Mazda-EZ60/Mazda-EZ60_1.2.py` -> `apps_cache_Mazda_EZ60`
|
||||||
- Shared binaries are managed under root `tools/`: `adb.exe`, `AdbWinApi.dll`, `AdbWinUsbApi.dll`, `fastboot.exe`, and `7za.exe`. Vehicle pack scripts in subfolders should copy from `%ROOT%\tools`, not from each vehicle folder.
|
- Shared binaries are managed under root `tools/`: `adb.exe`, `AdbWinApi.dll`, `AdbWinUsbApi.dll`, `fastboot.exe`, and `7za.exe`. Vehicle pack scripts in subfolders should copy from `%ROOT%\tools`, not from each vehicle folder.
|
||||||
- For progress display, detect support for `-bsp1` by checking for `-bs{o|e|p}` in 7za help output.
|
- For progress display, detect support for `-bsp1` by checking for `-bs{o|e|p}` in 7za help output.
|
||||||
- If `Incorrect command line` appears, retry with the basic compatible command: `x package.bin -pPASSWORD -oDIR -y`.
|
- If `Incorrect command line` appears, retry with the basic compatible command: `x PACKAGE -pPASSWORD -oDIR -y`.
|
||||||
- Decode 7za output with GBK first, then UTF-8 fallback.
|
- Decode 7za output with GBK first, then UTF-8 fallback.
|
||||||
- 7za progress must parse streamed output cumulatively. Do not read one byte and regex that single byte; percentages such as `42%` span multiple bytes and will otherwise jump from 0 to 100.
|
- 7za progress must parse streamed output cumulatively. Do not read one byte and regex that single byte; percentages such as `42%` span multiple bytes and will otherwise jump from 0 to 100.
|
||||||
- User-facing resource extraction text should say `资源准备中` / `Preparing resources`, not `资源解压` / `Extracting package`, unless the UI is an explicit debug test.
|
- User-facing resource extraction text should say `资源准备中` / `Preparing resources`, not `资源解压` / `Extracting package`, unless the UI is an explicit debug test.
|
||||||
@@ -94,6 +103,21 @@ Most tools are single-file tkinter apps with the same rough architecture: GUI, V
|
|||||||
|
|
||||||
## Model-Specific Behavior
|
## Model-Specific Behavior
|
||||||
|
|
||||||
|
### Q07
|
||||||
|
|
||||||
|
- Main script is `Q07/Qiyuan_Q07-multi-lan-installer.py`; keep `Q07/app.py` retired unless explicitly asked to restore the old name.
|
||||||
|
- Q07 release resource package is `Q07_package.bin`, not shared root `package.bin`. The tool may keep a compatibility fallback for old local `package.bin`, but user-facing errors/logs should name `Q07_package.bin`.
|
||||||
|
- Q07 cache directory is `%LOCALAPPDATA%\.cache\system\.android\apps_cache_Q07`. Clean it before extraction, on normal window close, and through `atexit`.
|
||||||
|
- Q07 `package-key` requests must include both `vin` and `vehicleName`; `vehicleName` comes from `auth-check` (`data.vehicleName`) and should be cached when available.
|
||||||
|
- Q07 `安装App` must also verify the current VIN is authorized before installing selected APK files. Debug mode may skip this check; normal mode must not.
|
||||||
|
- Q07 debug mode uses `Ctrl+Shift+D` and verifies the password through `POST /api/authorizations/verify-debug-mode-password`; do not restore hardcoded local debug passwords.
|
||||||
|
- Q07 debug mode exposes `解压测试` / `Ctrl+Shift+E`; it fetches `package-key` and extracts `Q07_package.bin`. The success log should only say `资源准备完成` / `Resources ready`, not app or priv-app counts.
|
||||||
|
- Q07 normal logs should keep the resource structure black-box: do not show app/priv-app counts or the current APK name during `刷入语言包`. `安装App` may show the selected APK filename currently being installed.
|
||||||
|
- Q07 UI has only language switching. Do not reintroduce dark/light theme switching or company/about subtitle text.
|
||||||
|
- Q07 window/taskbar icon should use `Q07/app.ico` at runtime: call `iconbitmap`, set a Windows AppUserModelID before creating `Tk()`, and set big/small Win32 window icons when possible. The pack scripts must include `--icon="%ICON%"` and `--add-data "%ICON%;."`.
|
||||||
|
- Q07 pack scripts (`Q07/pack_q07.bat` and `Q07/pack.bat`) are Cython-only. If Cython fails or no `_core*.pyd` is generated, stop with an error; do not fallback to normal PyInstaller.
|
||||||
|
- Q07 pack scripts use ASCII output name `Qiyuan_Q07-multi-lan-installer`, compile `Qiyuan_Q07-multi-lan-installer.py` into `_core.pyd`, use a tiny launcher, and bundle `adb.exe`, `AdbWinApi.dll`, `AdbWinUsbApi.dll`, `7za.exe`, and `Q07/app.ico`.
|
||||||
|
|
||||||
### S05
|
### S05
|
||||||
|
|
||||||
- Keep `S05/S05.py` as original unless explicitly asked.
|
- Keep `S05/S05.py` as original unless explicitly asked.
|
||||||
@@ -112,12 +136,60 @@ Most tools are single-file tkinter apps with the same rough architecture: GUI, V
|
|||||||
- Language selection: RU/FR/ES/EN. Only the selected language Settings APK is pushed; other language Settings APKs are skipped silently.
|
- Language selection: RU/FR/ES/EN. Only the selected language Settings APK is pushed; other language Settings APKs are skipped silently.
|
||||||
- Hidden debug mode: `Ctrl+Shift+D`, password `zxch5200`, logs full ADB/7za/API details.
|
- Hidden debug mode: `Ctrl+Shift+D`, password `zxch5200`, logs full ADB/7za/API details.
|
||||||
|
|
||||||
|
### UNI-T
|
||||||
|
|
||||||
|
- Main script is `UNI-T/UNI-T-multi-lan-installer.py`; it is cloned from the Q07 installer flow and preserves Q07-style VIN authorization, package extraction, system app push/install logic, language settings, debug password verification, and Cython-only packaging.
|
||||||
|
- UNI-T VIN should be read with fallbacks: `adb shell getprop persist.vendor.car.VIN`, then `settings get global VIN`, then legacy `settings get system VIN` / `settings get system ca_vin_info`. On the connected S202_MCA device, the live VIN is currently in `settings global VIN`.
|
||||||
|
- UNI-T `获取权限` must run `adb shell setenforce 0` before `adb root` and `adb remount`; without this pre-step, `adb root` may not open on this vehicle.
|
||||||
|
- UNI-T release resource package is `UNI-T_package.bin`, with legacy `package.bin` fallback only for local compatibility. User-facing errors/logs should name `UNI-T_package.bin`.
|
||||||
|
- UNI-T cache directory is `%LOCALAPPDATA%\.cache\system\.android\apps_cache_UNI_T`. Clean it before extraction, on normal window close, and through `atexit`.
|
||||||
|
- Before UNI-T `刷入语言包` pushes files, run `pm disable-user --user 10` for: `com.wt.roadbook`, `com.thunder.carplay`, `com.tencent.wecarmas`, `com.tencent.qqlive.audiobox`, `com.incall.apps.softmanager`, `com.changan.appmarket`, and `com.bytedance.byteautoservice`.
|
||||||
|
- UNI-T package root may contain `CarSystemUI.apk`; during `刷入语言包`, push it separately and copy it with `cp -f` to `/system/system_ext/priv-app/CarSystemUI/CarSystemUI.apk`.
|
||||||
|
- UNI-T `package-key` requests must include both `vin` and `vehicleName`; `vehicleName` comes from `auth-check` (`data.vehicleName`) and should be cached when available.
|
||||||
|
- `UNI-T/pack_unit.bat` is Cython-only. If Cython fails or no `_core*.pyd` is generated, stop the build; do not fallback to normal PyInstaller.
|
||||||
|
|
||||||
|
### CS55-Q05
|
||||||
|
|
||||||
|
- CS55Plus/Q05 common installer lives in `CS55-Q05/CS55-Q05_Installer.py`, with icon `CS55-Q05/cs55-q05.ico` and pack script `CS55-Q05/pack_cs55_q05.bat`.
|
||||||
|
- `pack_cs55_q05.bat` is Cython-only. If Cython fails or no `_core*.pyd` is generated, stop the build; do not fall back to normal PyInstaller.
|
||||||
|
- `刷入语言包` must verify VIN authorization before resource flashing. After authorization passes and before APK flashing, run both `pm disable-user <package>` and `pm uninstall -k --user 0 <package>` through `run_adb_shell()` for `com.wtcl.electronicdirections`, `com.tinnove.netease.music`, and `com.changan.appmarket`.
|
||||||
|
- All CS55/Q05 language package and manual APK installs should use `pm install -d -f -r <apk>` through `run_adb_shell()` so older/different app versions can install reliably.
|
||||||
|
- After CS55/Q05 language flashing or APK installation, do not reset `setprop vecentek.model` back to `0`; keep it at `1` so users can continue installing APKs manually.
|
||||||
|
- Normal `刷入语言包` logs should not reveal APK names; show generic current/total progress only. `安装App` may show each APK result and current APK name.
|
||||||
|
|
||||||
|
### CS75Pro
|
||||||
|
|
||||||
|
- CS75Pro installer lives in `CS75Pro/CS75Pro_Installer.py`, copied from the CS55-Q05 flow, with icon `CS75Pro/cs75pro.ico` and pack script `CS75Pro/pack_cs75pro.bat`.
|
||||||
|
- Keep the CS75Pro flashing flow aligned with CS55-Q05 unless explicitly requested.
|
||||||
|
- CS75Pro cache directory is `%LOCALAPPDATA%\.cache\system\.android\apps_cache_CS75Pro`.
|
||||||
|
- CS75Pro `package-key` requests must include both `vin` and `vehicleName`, but `vehicleName` is intentionally hardcoded to `CS75Pro`. This is an explicit exception to the general auth-returned `vehicleName` rule; do not replace it with `auth-check` `data.vehicleName` in future edits.
|
||||||
|
- Before CS75Pro `刷入语言包`, run `pm disable-user <package>` first and then `pm uninstall -k --user 0 <package>` through `run_adb_shell()` for `com.wtcl.electronicdirections`, `com.tinnove.netease.music`, `com.incall.apps.softmanager`, and `com.tencent.qqlive.audiobox`.
|
||||||
|
- CS75Pro language package installs should use `pm install -d -f -r <apk>` through `run_adb_shell()`.
|
||||||
|
|
||||||
### Mazda-EZ60
|
### Mazda-EZ60
|
||||||
|
|
||||||
- Based on `Yidong/app-install.py` / 逸动 flow.
|
- Based on `CS55-Q05/CS55-Q05_Installer.py` / 逸动 flow.
|
||||||
- Uses auto-password shell (`adb36987`) for VIN reads, `pm install`, overlay enable, disable commands, settings, and reboot.
|
- Uses auto-password shell (`adb36987`) for VIN reads, `pm install`, overlay enable, disable commands, settings, and reboot.
|
||||||
- Installs APKs from extracted `apps` via push to `/data/local/tmp` then `pm install -r -d`.
|
- Installs APKs from extracted `apps` via push to `/data/local/tmp` then `pm install -r -d`.
|
||||||
- `Mazda-EZ60/Mazda-EZ60.py` should show 7za extraction progress with stream parsing.
|
- `Mazda-EZ60/Mazda-EZ60_1.2.py` should show 7za extraction progress with stream parsing.
|
||||||
|
- Mazda 1.2 resource package is `package_voice-assistant.bin`, not the old `package.bin`; `package-key` still supplies the extraction password.
|
||||||
|
- Mazda 1.2 package cache is `%LOCALAPPDATA%\.cache\system\.android\apps_cache_Mazda_EZ60`; clean it on startup, before extraction, on normal window close, and through `atexit`.
|
||||||
|
- Mazda `package-key` requests must include both `vin` and `vehicleName`; cache `data.vehicleName` from `auth-check`, and query `auth-check` before `package-key` if it is missing.
|
||||||
|
- Mazda 1.2 has a first-row `语音助理补丁` button that installs Magisk modules from the same extracted package. The package should contain `enable_install.zip` and `MazdaEZ60VoiceEnglish-1.2-Aemeth.zip` beside `apps/` (the tool also tolerates them inside `apps/`), and each zip must contain a valid `module.prop`.
|
||||||
|
- Mazda 1.2 `获取权限` uses a separate `boot-challenge` -> `boot-key` flow and must only use `data.sessionKey` / BOOT_KEY to decrypt `EZ60_resource.dat`; do not reuse `package-key` as the init_boot resource key.
|
||||||
|
- Mazda 1.2 `EZ60_resource.dat` is an AES-256-GCM init_boot resource with `EZ60R2` header / `ez60-resource-v2` format and default AAD `Mazda-EZ60 init_boot resource v1`. Generate it with `tools/encrypt_q05_lidar_resource.py --vehicle ez60 -o EZ60_resource.dat ...` using the EZ60 patched `init_boot.img`.
|
||||||
|
- Mazda 1.2 `EZ60_resource.dat` is optional at build time: `pack_mazda_ez60_1.2.bat` should embed it into the exe only when the file exists, and should not fail the build when it is absent. The permission feature still needs it at runtime to complete init_boot flashing.
|
||||||
|
- Mazda 1.2 `获取权限` follows the Q05_Lidar bootstrap shape: extract `runtime.dat` to `base.apk` using the package-key password when needed, run `setprop vecentek.model 1` through `run_adb_shell()` so the `adb36987` auto-password is sent, push/install `base.apk`, then fetch the boot key, reboot to fastboot through `run_adb_shell('reboot fastboot')`, wait for a real `<serial> fastboot` row, flash `init_boot`, immediately reboot, clear the temporary img, and keep normal logs black-box (`正在获取权限中` / `获取成功` / `获取失败`).
|
||||||
|
- Mazda 1.2 `runtime.dat` is copied next to the release exe by `pack_mazda_ez60_1.2.bat`, and its temporary cache is `%LOCALAPPDATA%\.cache\system\.android\apps_cache_Mazda_EZ60_runtime`.
|
||||||
|
- Mazda 1.2 detects the bootloader driver environment like Q05_Lidar. If missing, prompt once and install automatically; keep a manual `安装驱动` button in the status bar. Bundle `tools/usb_driver/` into the onefile exe via PyInstaller `--add-data`, resolving it from `_MEIPASS` at runtime.
|
||||||
|
- After Mazda 1.2 voice patch root authorization succeeds, clean the Magisk manager/stub launcher entry with the tested `pm uninstall -k com.topjohnwu.magisk` path first, then optional per-user uninstall/disable and manager/stub apk cache cleanup. Because the icon can reappear after reboot, also install a tiny Magisk module under `/data/adb/modules/ez60_magisk_manager_cleanup` whose `service.sh` repeats the same manager/stub cleanup after boot. Do not remove `/data/adb/modules` or the voice modules.
|
||||||
|
- `Mazda-EZ60/Mazda_EZ60-Language-Install_v1.0.py` is the exception: its `package-key` `vehicleName` is fixed to `EZ60_1.0`. Do not replace it with the authorization-returned vehicle name.
|
||||||
|
- Mazda hosts setup should keep exactly one target mapping for `spm.auto-pai.com`: replace mismatched IPs with `103.236.55.140 spm.auto-pai.com` instead of appending a conflicting duplicate. If hosts setup fails, tell the user `环境配置失败`.
|
||||||
|
- Mazda UI should not show the company/about subtitle.
|
||||||
|
- Mazda language switching should cover all operator-facing buttons, labels, dialogs, logs, progress text, hotspot status, password query UI, and quick-language popup text. The right-side usage tips should render from translated `hint_lines` with wrapping so English text does not overlap or overflow.
|
||||||
|
- Mazda normal logs/prompts should keep only key operator-facing outcomes and fuzzy resource/environment errors. Do not show package names, language package APK names, raw commands, paths, or command output during `刷入语言包`; manual `安装App` may show selected APK filenames. VIN and `vehicleName` are not sensitive and may remain visible.
|
||||||
|
- Mazda debug mode uses `Ctrl+Shift+D` and verifies the password through `POST /api/authorizations/verify-debug-mode-password`; detailed ADB/7za/API output belongs in debug mode only.
|
||||||
|
- Mazda pack scripts `pack_mazda_ez60_1.0.bat` and `pack_mazda_ez60_1.2.bat` are Cython-only. If dependencies, Cython compilation, PYD generation, resource copy, PyInstaller, or final exe output fail, stop immediately with an `[ERROR]` reason; do not fall back to normal PyInstaller.
|
||||||
- Regardless of APK install failures, run post-install configuration after the install loop.
|
- Regardless of APK install failures, run post-install configuration after the install loop.
|
||||||
- Post-install overlays to enable:
|
- Post-install overlays to enable:
|
||||||
- `com.tinnove.launcher.overlay`
|
- `com.tinnove.launcher.overlay`
|
||||||
@@ -142,11 +214,18 @@ Most tools are single-file tkinter apps with the same rough architecture: GUI, V
|
|||||||
- The first-row `获取权限` button installs `runtime.dat` -> `base.apk`, reboots to fastboot, waits for a real `fastboot devices` row like `<serial> fastboot` with a non-aggressive interval, then fetches a boot key through `POST /api/authorizations/boot-challenge` then `POST /api/authorizations/boot-key`, decrypts embedded `resource.dat`, flashes `init_boot`, immediately reboots, and deletes the temporary img. Keep the decrypted img lifetime as short as possible.
|
- The first-row `获取权限` button installs `runtime.dat` -> `base.apk`, reboots to fastboot, waits for a real `fastboot devices` row like `<serial> fastboot` with a non-aggressive interval, then fetches a boot key through `POST /api/authorizations/boot-challenge` then `POST /api/authorizations/boot-key`, decrypts embedded `resource.dat`, flashes `init_boot`, immediately reboots, and deletes the temporary img. Keep the decrypted img lifetime as short as possible.
|
||||||
- `resource.dat` is AES-GCM encrypted and must match the server `BOOT_KEY`; the tool only accepts `data.sessionKey` from `boot-key`.
|
- `resource.dat` is AES-GCM encrypted and must match the server `BOOT_KEY`; the tool only accepts `data.sessionKey` from `boot-key`.
|
||||||
- Device fingerprint data sent to the server includes ADB serial, `ro.serialno`, `ro.boot.serialno`, manufacturer, model, device, build fingerprint, and VIN.
|
- Device fingerprint data sent to the server includes ADB serial, `ro.serialno`, `ro.boot.serialno`, manufacturer, model, device, build fingerprint, and VIN.
|
||||||
- `刷入语言包` installs Magisk modules, not APKs. It opens `com.topjohnwu.magisk`, warns the user to grant Shell/root permission, verifies `/debug_ramdisk/su -c "id"` returns `uid=0`, extracts `Q05_Lidar-package.bin`, then pushes module files to `/data/local/tmp/q05_lidar_modules/<MODID>/` and root-copies them into `/data/adb/modules/<MODID>`.
|
- `刷入语言包` installs Magisk modules, not APKs. It opens `com.topjohnwu.magisk`, warns the user to grant Shell/root permission, verifies `/debug_ramdisk/su -c "id"` returns `uid=0`, silently uninstalls `com.topjohnwu.magisk` for user 0, extracts `Q05_Lidar-package.bin`, reads each module zip's root `module.prop` on the PC to get `id=`, pushes each zip to `/data/local/tmp/q05_lidar_modules/<MODID>.zip`, then root-unzips it into `/data/adb/modules/<MODID>`.
|
||||||
- `Q05_Lidar-package.bin` should unpack with module files at archive root: `module.prop`, scripts, `system/`, and `disable-wireless-adb-vecentek-magisk.zip`; do not wrap them in an outer `Q05_LIDAR_DATA/` directory.
|
- If Magisk shows its first-run environment repair/additional setup prompt, do not automate UI clicking in the tool; the operator should confirm it manually and reboot once before retrying.
|
||||||
|
- Before installing Q05_Lidar language resources, disable OTA with `pm disable-user com.incall.apps.softmanager`, then for `com.carcontrolhome.app`, `com.tinnove.netease.music`, and `com.wtcl.electronicdirections` run both `pm disable-user <package>` and `pm uninstall --user 0 <package>`. These commands must go through `run_adb_shell()`.
|
||||||
|
- `Q05_Lidar-package.bin` should unpack to module zip files, preferably under `modules/`, for example `modules/Qiyuan_Q05-multi_lan.zip` and `modules/disable_wireless_adb_vecentek.zip`. Each module zip must contain `module.prop` at zip root, plus scripts and `system/` as needed. Do not wrap `module.prop` inside an extra outer folder.
|
||||||
- Q05_Lidar package cache is `%LOCALAPPDATA%\.cache\system\.android\apps_cache_Q05_Lidar`; the tool cleans it on startup/extraction and on normal/atexit shutdown.
|
- Q05_Lidar package cache is `%LOCALAPPDATA%\.cache\system\.android\apps_cache_Q05_Lidar`; the tool cleans it on startup/extraction and on normal/atexit shutdown.
|
||||||
- Q05_Lidar `runtime.dat` cache is `%LOCALAPPDATA%\.cache\system\.android\apps_cache_q05_lidar_runtime`; treat it as temporary and clean stale contents before extraction.
|
- Q05_Lidar `runtime.dat` cache is `%LOCALAPPDATA%\.cache\system\.android\apps_cache_q05_lidar_runtime`; treat it as temporary and clean stale contents before extraction.
|
||||||
- `Q05_Lidar-package.bin` is an external release file next to the exe because it is large. `resource.dat` is embedded in the exe; `runtime.dat` should be copied next to the exe by the pack script.
|
- `Q05_Lidar-package.bin` is an external release file next to the exe because it is large. `resource.dat` is embedded in the exe; `runtime.dat` should be copied next to the exe by the pack script.
|
||||||
|
- Q05_Lidar checks driver environment on startup. If the driver environment already exists, stay silent and do not log a success/info message for the operator.
|
||||||
|
- If the startup check finds the driver environment missing, show a single simple prompt such as `驱动缺失,即将自动安装驱动。`; after the user clicks confirm, start installation immediately. Do not show a second `是否继续` confirmation in that startup flow.
|
||||||
|
- User-facing driver logs/prompts should say only `驱动环境` / `Driver environment`; do not mention `USB/Fastboot` or `Android/Fastboot`.
|
||||||
|
- Manual `安装驱动` remains available in the status bar, but `检查` should appear before it visually because it is more directly related to device status/refresh actions.
|
||||||
|
- `usb_driver/` is bundled inside the Q05_Lidar onefile exe via PyInstaller `--add-data`, and runtime driver installation should resolve files from the extracted temporary resource directory (for frozen builds, `_MEIPASS`) instead of requiring a sidecar `usb_driver` folder next to the exe.
|
||||||
- `package-key` must include the `vehicleName` returned by `auth-check` for the VIN. The tool caches `data.vehicleName` from password query / authorization check and uses it for package-key; if missing, query `auth-check` first rather than falling back to a hardcoded Q05_Lidar value.
|
- `package-key` must include the `vehicleName` returned by `auth-check` for the VIN. The tool caches `data.vehicleName` from password query / authorization check and uses it for package-key; if missing, query `auth-check` first rather than falling back to a hardcoded Q05_Lidar value.
|
||||||
- All `adb shell` commands in Q05_Lidar, including Magisk launch and `/debug_ramdisk/su -c ...`, must go through `run_adb_shell()` so the tool silently sends `adb36987`.
|
- All `adb shell` commands in Q05_Lidar, including Magisk launch and `/debug_ramdisk/su -c ...`, must go through `run_adb_shell()` so the tool silently sends `adb36987`.
|
||||||
- The `安装App` button remains the APK install path: file picker -> `adb push` -> `setprop vecentek.model 1` -> `pm install -r -d -f` -> cleanup. Do not replace it with the Magisk module flow.
|
- The `安装App` button remains the APK install path: file picker -> `adb push` -> `setprop vecentek.model 1` -> `pm install -r -d -f` -> cleanup. Do not replace it with the Magisk module flow.
|
||||||
@@ -173,9 +252,9 @@ Most tools are single-file tkinter apps with the same rough architecture: GUI, V
|
|||||||
- `PYD: _core...pyd`
|
- `PYD: _core...pyd`
|
||||||
- output under `dist_cy\dist\...exe`
|
- output under `dist_cy\dist\...exe`
|
||||||
- If logs show `[WARN] Cython failed, fallback` and `[INFO] Normal PyInstaller`, the exe still builds but is normal PyInstaller and easier to reverse.
|
- If logs show `[WARN] Cython failed, fallback` and `[INFO] Normal PyInstaller`, the exe still builds but is normal PyInstaller and easier to reverse.
|
||||||
- For security-sensitive tools like Q05_Lidar, do not keep a normal PyInstaller fallback. If Cython fails or no `_core*.pyd` is generated, stop the build and show an error.
|
- For security-sensitive tools like Q05_Lidar and Q07, do not keep a normal PyInstaller fallback. If Cython fails or no `_core*.pyd` is generated, stop the build and show an error.
|
||||||
- A Cython onefile PyInstaller build should use a tiny `launcher.py` that imports `main` from compiled `_core.pyd`, and the exe archive should contain `_core*.pyd`. Confirm with PyInstaller archive viewer when in doubt.
|
- A Cython onefile PyInstaller build should use a tiny `launcher.py` that imports `main` from compiled `_core.pyd`, and the exe archive should contain `_core*.pyd`. Confirm with PyInstaller archive viewer when in doubt.
|
||||||
- `UNIZ/pack_uniz.bat` and `Mazda-EZ60/pack_mazda_ez60.bat` use ASCII output names to avoid CMD encoding problems.
|
- `UNIZ/pack_uniz.bat` and Mazda-EZ60 pack scripts use ASCII output names to avoid CMD encoding problems.
|
||||||
- Generated `.exe`, `.spec`, `build/`, `dist/`, and `dist_cy/` are build artifacts and should not be committed unless explicitly requested.
|
- Generated `.exe`, `.spec`, `build/`, `dist/`, and `dist_cy/` are build artifacts and should not be committed unless explicitly requested.
|
||||||
|
|
||||||
## Key Behaviors To Preserve
|
## Key Behaviors To Preserve
|
||||||
@@ -191,7 +270,7 @@ Most tools are single-file tkinter apps with the same rough architecture: GUI, V
|
|||||||
## Current Local State (2026-05-28)
|
## Current Local State (2026-05-28)
|
||||||
|
|
||||||
- `UNIZ/UNIZ.py` and `UNIZ/pack_uniz.bat` exist locally. Cython build has succeeded after installing Microsoft C++ Build Tools, producing `dist_cy\dist\UNIZ-Language-Pusher.exe`.
|
- `UNIZ/UNIZ.py` and `UNIZ/pack_uniz.bat` exist locally. Cython build has succeeded after installing Microsoft C++ Build Tools, producing `dist_cy\dist\UNIZ-Language-Pusher.exe`.
|
||||||
- `Mazda-EZ60/Mazda-EZ60.py` and `Mazda-EZ60/pack_mazda_ez60.bat` exist locally. Mazda has 7za progress extraction, unconditional post-install configuration, three overlay enables, and nine package disables.
|
- `Mazda-EZ60/Mazda-EZ60_1.2.py` with `pack_mazda_ez60_1.2.bat`, and `Mazda-EZ60/Mazda_EZ60-Language-Install_v1.0.py` with `pack_mazda_ez60_1.0.bat`, exist locally. Mazda has 7za progress extraction, Cython-only packaging, and version-specific output names.
|
||||||
- `Yidong/app-yidong.py` has been updated for 7za progress compatibility and `Incorrect command line` fallback.
|
- `Yidong/app-yidong.py` has been updated for 7za progress compatibility and `Incorrect command line` fallback.
|
||||||
- `Yidong/pack_yidong.bat` has been updated with quoted paths, `cd /d "%~dp0"`, and ASCII output name.
|
- `Yidong/pack_yidong.bat` has been updated with quoted paths, `cd /d "%~dp0"`, and ASCII output name.
|
||||||
- `.gitignore` has been expanded to ignore generated exe/spec artifacts.
|
- `.gitignore` has been expanded to ignore generated exe/spec artifacts.
|
||||||
|
|||||||
@@ -1,205 +1,221 @@
|
|||||||
# 长安语言刷入工具 (Changan Language Flashing Tool)
|
# 长安语言刷入工具 (Changan Language Flashing Tool)
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Windows GUI tool suite (Python 3.6+ / tkinter) for flashing or pushing multi-language APKs to Android-based vehicle infotainment systems. Built by 宜宾科宜科技有限公司.
|
Windows GUI tool suite (Python 3.6+ / tkinter) for flashing or pushing multi-language APKs to Android-based vehicle infotainment systems. Built by 宜宾科宜科技有限公司.
|
||||||
|
|
||||||
Most tools are single-file tkinter apps with the same rough architecture: GUI, VIN authorization, package extraction, ADB commands, logging, and worker threads live in one class. Preserve that style unless the user explicitly asks for a larger refactor.
|
Most tools are single-file tkinter apps with the same rough architecture: GUI, VIN authorization, package extraction, ADB commands, logging, and worker threads live in one class. Preserve that style unless the user explicitly asks for a larger refactor.
|
||||||
|
|
||||||
## Tool Variants
|
## Tool Variants
|
||||||
|
|
||||||
| Tool file | Vehicle / purpose | Window title | Pack script |
|
| Tool file | Vehicle / purpose | Window title | Pack script |
|
||||||
|-----------|-------------------|--------------|-------------|
|
|-----------|-------------------|--------------|-------------|
|
||||||
| `Q07/app.py` | 启源Q07 | 长安语言安装工具 | `Q07/pack_q07.bat` |
|
| `Q07/app.py` | 启源Q07 | 长安语言安装工具 | `Q07/pack_q07.bat` |
|
||||||
| `S05/S05.py` | 深蓝S05 original | 深蓝S05多语言安装 | `S05/pack_s05.bat` |
|
| `S05/S05.py` | 深蓝S05 original | 深蓝S05多语言安装 | `S05/pack_s05.bat` |
|
||||||
| `S05/S05_fixed.py` | 深蓝S05 fixed/experimental copy | 长安语言安装工具 | `S05/pack_s05_fixed.bat` |
|
| `S05/S05_fixed.py` | 深蓝S05 fixed/experimental copy | 长安语言安装工具 | `S05/pack_s05_fixed.bat` |
|
||||||
| `X5plus/X5plusTool.py` | X5plus | 适用于X5plus多语言安装 | `X5plus/pack_x5plus.bat` |
|
| `X5plus/X5plusTool.py` | X5plus | 适用于X5plus多语言安装 | `X5plus/pack_x5plus.bat` |
|
||||||
| `Yidong/app-install.py` | 长安逸动通用 | 长安语言刷入工具 | `Yidong/pack_common.bat` |
|
| `CS55-Q05/CS55-Q05_Installer.py` | CS55Plus/Q05 通用 | CS55Plus/Q05 语言刷入工具 | `CS55-Q05/pack_cs55_q05.bat` |
|
||||||
| `Yidong/app-yidong.py` | 长安逸动 | 长安逸动语言刷入工具 | `Yidong/pack_yidong.bat` |
|
| `Yidong/app-yidong.py` | 长安逸动 | 长安逸动语言刷入工具 | `Yidong/pack_yidong.bat` |
|
||||||
| `UNIZ/UNIZ.py` | UNI-Z file pusher | UNI-Z语言文件推送工具 | `UNIZ/pack_uniz.bat` |
|
| `UNIZ/UNIZ.py` | UNI-Z file pusher | UNI-Z语言文件推送工具 | `UNIZ/pack_uniz.bat` |
|
||||||
| `Mazda-EZ60/Mazda-EZ60.py` | Mazda-EZ60 OS 1.2 | Mazda-EZ60_OS-1.2适用 | `Mazda-EZ60/pack_mazda_ez60.bat` |
|
| `Mazda-EZ60/Mazda-EZ60_1.2.py` | Mazda-EZ60 OS 1.2 | Mazda-EZ60_OS-1.2适用 | `Mazda-EZ60/pack_mazda_ez60_1.2.bat` |
|
||||||
| `Q05-Lidar/Q05-Lidar_Installer.py` | Q05_Lidar permission/bootstrap + language installer | Q05_Lidar | `Q05-Lidar/pack_q05_lidar.bat` |
|
| `Mazda-EZ60/Mazda_EZ60-Language-Install_v1.0.py` | Mazda-EZ60 OS 1.0 | 马自达EZ60刷机工具_OS-1.0 | `Mazda-EZ60/pack_mazda_ez60_1.0.bat` |
|
||||||
|
| `Q05-Lidar/Q05-Lidar_Installer.py` | Q05_Lidar permission/bootstrap + language installer | Q05_Lidar | `Q05-Lidar/pack_q05_lidar.bat` |
|
||||||
## Project Structure
|
|
||||||
|
## Project Structure
|
||||||
```
|
|
||||||
├── Q07/ # Q07 script, pack scripts, ignored build outputs
|
```
|
||||||
├── S05/ # S05 original and fixed copy
|
├── Q07/ # Q07 script, pack scripts, ignored build outputs
|
||||||
├── X5plus/ # X5plus tool
|
├── S05/ # S05 original and fixed copy
|
||||||
├── Yidong/ # common/yidong tools
|
├── X5plus/ # X5plus tool
|
||||||
├── UNIZ/ # UNI-Z file pusher
|
├── CS55-Q05/ # CS55Plus/Q05 common installer and icon
|
||||||
├── Mazda-EZ60/ # Mazda-EZ60 tool
|
├── Yidong/ # Yidong-specific tool
|
||||||
├── A07/ # Qiyuan A07 tool
|
├── UNIZ/ # UNI-Z file pusher
|
||||||
├── Q05-Lidar/ # Q05_Lidar tool plus resource.dat/tools
|
├── Mazda-EZ60/ # Mazda-EZ60 tool
|
||||||
├── app.ico
|
├── A07/ # Qiyuan A07 tool
|
||||||
├── package.bin # encrypted package, not committed; shared from root
|
├── Q05-Lidar/ # Q05_Lidar tool plus resource.dat/tools
|
||||||
└── tools/ # shared adb/fastboot/7za dependencies for pack scripts
|
├── app.ico
|
||||||
├── adb.exe
|
├── package.bin # encrypted package, not committed; shared from root
|
||||||
├── AdbWinApi.dll
|
└── tools/ # shared adb/fastboot/7za dependencies for pack scripts
|
||||||
├── AdbWinUsbApi.dll
|
├── adb.exe
|
||||||
├── fastboot.exe
|
├── AdbWinApi.dll
|
||||||
└── 7za.exe # 7-Zip Extra 26.01, bundled by pack scripts
|
├── AdbWinUsbApi.dll
|
||||||
```
|
├── fastboot.exe
|
||||||
|
└── 7za.exe # 7-Zip Extra 26.01, bundled by pack scripts
|
||||||
## Architecture Notes
|
```
|
||||||
|
|
||||||
- Most tools use class `ADKAPKGUI`; `UNIZ/UNIZ.py` uses `UNIZLanguageGUI`.
|
## Architecture Notes
|
||||||
- Worker actions run in `threading.Thread(..., daemon=True)`.
|
|
||||||
- Tkinter calls from workers must go through `run_on_ui_thread(...)`.
|
- Most tools use class `ADKAPKGUI`; `UNIZ/UNIZ.py` uses `UNIZLanguageGUI`.
|
||||||
- Prefer `self.root.after(0, lambda: func(*args, **kwargs))` in `run_on_ui_thread`; direct `after(0, func, *args, **kwargs)` breaks when kwargs such as `text=` or `fg=` are passed.
|
- Worker actions run in `threading.Thread(..., daemon=True)`.
|
||||||
- Background `messagebox.*` calls should be scheduled with `run_on_ui_thread`.
|
- Tkinter calls from workers must go through `run_on_ui_thread(...)`.
|
||||||
- Keep files UTF-8 with `# -*- coding: utf-8 -*-`.
|
- Prefer `self.root.after(0, lambda: func(*args, **kwargs))` in `run_on_ui_thread`; direct `after(0, func, *args, **kwargs)` breaks when kwargs such as `text=` or `fg=` are passed.
|
||||||
|
- Background `messagebox.*` calls should be scheduled with `run_on_ui_thread`.
|
||||||
## ADB And Auth
|
- Keep files UTF-8 with `# -*- coding: utf-8 -*-`.
|
||||||
|
|
||||||
- `run_adb_command(command)` handles normal ADB commands such as `adb devices`, `adb push`, and non-shell install calls.
|
## ADB And Auth
|
||||||
- `run_adb_shell(shell_command)` exists in the 逸动-family tools and Mazda copy; it shells into the device and automatically sends password `adb36987`.
|
|
||||||
- All `adb shell` operations in `Yidong/app-install.py`, `Yidong/app-yidong.py`, and `Mazda-EZ60/Mazda-EZ60.py` should go through `run_adb_shell()`.
|
- `run_adb_command(command)` handles normal ADB commands such as `adb devices`, `adb push`, and non-shell install calls.
|
||||||
- `UNIZ/UNIZ.py` must not use `adb shell`; it only checks devices and pushes APKs to `/storage/emulated/0/Download/`.
|
- `run_adb_shell(shell_command)` exists in the 逸动-family tools and Mazda copy; it shells into the device and automatically sends password `adb36987`.
|
||||||
- Standard auth flow uses:
|
- All `adb shell` operations in `CS55-Q05/CS55-Q05_Installer.py`, `Yidong/app-yidong.py`, and `Mazda-EZ60/Mazda-EZ60_1.2.py` should go through `run_adb_shell()`.
|
||||||
- `auth-check?vin=...` for authorization.
|
- `UNIZ/UNIZ.py` must not use `adb shell`; it only checks devices and pushes APKs to `/storage/emulated/0/Download/`.
|
||||||
- `package-key?vin=...` for `package.bin` extraction password.
|
- Standard auth flow uses:
|
||||||
- VIN keys:
|
- `auth-check?vin=...` for authorization.
|
||||||
- Q07/S05/X5plus: `ca_vin_info` or `VIN`.
|
- `package-key?vin=...` for `package.bin` extraction password.
|
||||||
- 逸动/Mazda: `settings get system ca.car.vin` via auto-password shell.
|
- VIN keys:
|
||||||
- UNI-Z: user manually enters VIN.
|
- Q07/S05/X5plus: `ca_vin_info` or `VIN`.
|
||||||
|
- 逸动/Mazda: `settings get system ca.car.vin` via auto-password shell.
|
||||||
## Package Extraction
|
- UNI-Z: user manually enters VIN.
|
||||||
|
|
||||||
- `package.bin` is extracted under `%LOCALAPPDATA%\.cache\system\.android\...`.
|
## Package Extraction
|
||||||
- Current cache directories:
|
|
||||||
- `Q07/app.py` -> `apps_cache_Q07`
|
- `package.bin` is extracted under `%LOCALAPPDATA%\.cache\system\.android\...`.
|
||||||
- `S05/S05.py` / `S05/S05_fixed.py` -> `apps_cache_S05`
|
- Current cache directories:
|
||||||
- `X5plus/X5plusTool.py` -> `apps_cache_X5plus`
|
- `Q07/app.py` -> `apps_cache_Q07`
|
||||||
- `Yidong/app-install.py` -> `apps_cache_common`
|
- `S05/S05.py` / `S05/S05_fixed.py` -> `apps_cache_S05`
|
||||||
- `Yidong/app-yidong.py` -> `apps_cache_yidong`
|
- `X5plus/X5plusTool.py` -> `apps_cache_X5plus`
|
||||||
- `UNIZ/UNIZ.py` -> `apps_cache_UNIZ`
|
- `CS55-Q05/CS55-Q05_Installer.py` -> `apps_cache_common`
|
||||||
- `Mazda-EZ60/Mazda-EZ60.py` -> `apps_cache_Mazda_EZ60`
|
- `Yidong/app-yidong.py` -> `apps_cache_yidong`
|
||||||
- Shared binaries are managed under root `tools/`: `adb.exe`, `AdbWinApi.dll`, `AdbWinUsbApi.dll`, `fastboot.exe`, and `7za.exe`. Vehicle pack scripts in subfolders should copy from `%ROOT%\tools`, not from each vehicle folder.
|
- `UNIZ/UNIZ.py` -> `apps_cache_UNIZ`
|
||||||
- For progress display, detect support for `-bsp1` by checking for `-bs{o|e|p}` in 7za help output.
|
- `Mazda-EZ60/Mazda-EZ60_1.2.py` -> `apps_cache_Mazda_EZ60`
|
||||||
- If `Incorrect command line` appears, retry with the basic compatible command: `x package.bin -pPASSWORD -oDIR -y`.
|
- Shared binaries are managed under root `tools/`: `adb.exe`, `AdbWinApi.dll`, `AdbWinUsbApi.dll`, `fastboot.exe`, and `7za.exe`. Vehicle pack scripts in subfolders should copy from `%ROOT%\tools`, not from each vehicle folder.
|
||||||
- Decode 7za output with GBK first, then UTF-8 fallback.
|
- For progress display, detect support for `-bsp1` by checking for `-bs{o|e|p}` in 7za help output.
|
||||||
- 7za progress must parse streamed output cumulatively. Do not read one byte and regex that single byte; percentages such as `42%` span multiple bytes and will otherwise jump from 0 to 100.
|
- If `Incorrect command line` appears, retry with the basic compatible command: `x package.bin -pPASSWORD -oDIR -y`.
|
||||||
- User-facing resource extraction text should say `资源准备中` / `Preparing resources`, not `资源解压` / `Extracting package`, unless the UI is an explicit debug test.
|
- Decode 7za output with GBK first, then UTF-8 fallback.
|
||||||
- Cache cleanup should be best effort in three places when feasible: before a new extraction, during normal window close, and via `atexit` for ordinary process exit. A forced process kill cannot be guaranteed, so also clear stale caches at next extraction/startup.
|
- 7za progress must parse streamed output cumulatively. Do not read one byte and regex that single byte; percentages such as `42%` span multiple bytes and will otherwise jump from 0 to 100.
|
||||||
|
- User-facing resource extraction text should say `资源准备中` / `Preparing resources`, not `资源解压` / `Extracting package`, unless the UI is an explicit debug test.
|
||||||
## Shared UX And Safety Rules
|
- Cache cleanup should be best effort in three places when feasible: before a new extraction, during normal window close, and via `atexit` for ordinary process exit. A forced process kill cannot be guaranteed, so also clear stale caches at next extraction/startup.
|
||||||
|
|
||||||
- Hosts update logic should replace conflicting entries for the managed domain. If the hosts file already contains the target domain with a different IP, delete that line and write the expected `IP domain` entry instead of appending duplicates.
|
## Shared UX And Safety Rules
|
||||||
- VIN authorization logs should be explicit for operator-facing flows: print the current VIN, print `data.vehicleName` when `auth-check` returns it, print authorization success, and print a clear unauthorized/failure log when denied.
|
|
||||||
- `package-key` requests should use the vehicle name returned by `auth-check` (`data.vehicleName`) whenever available. Do not hardcode a model name if the authorization API already returned the exact vehicle name for the VIN.
|
- Hosts update logic should replace conflicting entries for the managed domain. If the hosts file already contains the target domain with a different IP, delete that line and write the expected `IP domain` entry instead of appending duplicates.
|
||||||
- Normal users should not see low-level sensitive process details such as `fastboot`, `init_boot`, boot keys, or image names during permission/bootstrap flows. Use black-box text such as `正在获取权限中`, `获取成功`, and `获取失败`; leave command details for debug mode only.
|
- VIN authorization logs should be explicit for operator-facing flows: print the current VIN, print `data.vehicleName` when `auth-check` returns it, print authorization success, and print a clear unauthorized/failure log when denied.
|
||||||
- Process logs should stay minimal in normal mode. Detailed ADB/7za/API command logs belong behind debug mode.
|
- `package-key` requests should use the vehicle name returned by `auth-check` (`data.vehicleName`) whenever available. Do not hardcode a model name if the authorization API already returned the exact vehicle name for the VIN.
|
||||||
- All Tkinter UI updates and `messagebox.*` calls from workers must go through `run_on_ui_thread(...)`.
|
- Normal users should not see low-level sensitive process details such as `fastboot`, `init_boot`, boot keys, or image names during permission/bootstrap flows. Use black-box text such as `正在获取权限中`, `获取成功`, and `获取失败`; leave command details for debug mode only.
|
||||||
|
- Process logs should stay minimal in normal mode. Detailed ADB/7za/API command logs belong behind debug mode.
|
||||||
## Model-Specific Behavior
|
- All Tkinter UI updates and `messagebox.*` calls from workers must go through `run_on_ui_thread(...)`.
|
||||||
|
|
||||||
### S05
|
## Model-Specific Behavior
|
||||||
|
|
||||||
- Keep `S05/S05.py` as original unless explicitly asked.
|
### S05
|
||||||
- Use `S05/S05_fixed.py` for experimental/fixed S05 changes.
|
|
||||||
- Do not add `chmod`, `chown`, or `restorecon` to the S05 system-app push path unless explicitly requested; the target system inherits permissions.
|
- Keep `S05/S05.py` as original unless explicitly asked.
|
||||||
- `S05/S05_fixed.py` includes debug extract test `Ctrl+Shift+E` and 7za progress support.
|
- Use `S05/S05_fixed.py` for experimental/fixed S05 changes.
|
||||||
|
- Do not add `chmod`, `chown`, or `restorecon` to the S05 system-app push path unless explicitly requested; the target system inherits permissions.
|
||||||
### UNI-Z
|
- `S05/S05_fixed.py` includes debug extract test `Ctrl+Shift+E` and 7za progress support.
|
||||||
|
|
||||||
- Endpoint for visible passwords: `/api/authorizations/get-uni-z-pwd`.
|
### UNI-Z
|
||||||
- Display `factoryPwd` as factory mode password and `password` as debug password.
|
|
||||||
- If `authorized == false` or `password` is empty, show unauthorized state and do not proceed.
|
- Endpoint for visible passwords: `/api/authorizations/get-uni-z-pwd`.
|
||||||
- `password` from `get-uni-z-pwd` is not the package extraction password.
|
- Display `factoryPwd` as factory mode password and `password` as debug password.
|
||||||
- Before push, call `/api/authorizations/package-key?vin=...` to get the real `package.bin` password.
|
- If `authorized == false` or `password` is empty, show unauthorized state and do not proceed.
|
||||||
- Push only to `/storage/emulated/0/Download/`; no `adb shell`.
|
- `password` from `get-uni-z-pwd` is not the package extraction password.
|
||||||
- Language selection: RU/FR/ES/EN. Only the selected language Settings APK is pushed; other language Settings APKs are skipped silently.
|
- Before push, call `/api/authorizations/package-key?vin=...` to get the real `package.bin` password.
|
||||||
- Hidden debug mode: `Ctrl+Shift+D`, password `zxch5200`, logs full ADB/7za/API details.
|
- Push only to `/storage/emulated/0/Download/`; no `adb shell`.
|
||||||
|
- Language selection: RU/FR/ES/EN. Only the selected language Settings APK is pushed; other language Settings APKs are skipped silently.
|
||||||
### Mazda-EZ60
|
- Hidden debug mode: `Ctrl+Shift+D`, password `zxch5200`, logs full ADB/7za/API details.
|
||||||
|
|
||||||
- Based on `Yidong/app-install.py` / 逸动 flow.
|
### CS55-Q05
|
||||||
- Uses auto-password shell (`adb36987`) for VIN reads, `pm install`, overlay enable, disable commands, settings, and reboot.
|
|
||||||
- Installs APKs from extracted `apps` via push to `/data/local/tmp` then `pm install -r -d`.
|
- CS55Plus/Q05 common installer lives in `CS55-Q05/CS55-Q05_Installer.py`, with icon `CS55-Q05/cs55-q05.ico` and pack script `CS55-Q05/pack_cs55_q05.bat`.
|
||||||
- `Mazda-EZ60/Mazda-EZ60.py` should show 7za extraction progress with stream parsing.
|
- `pack_cs55_q05.bat` is Cython-only. If Cython fails or no `_core*.pyd` is generated, stop the build; do not fall back to normal PyInstaller.
|
||||||
- Regardless of APK install failures, run post-install configuration after the install loop.
|
- Normal `刷入语言包` logs should not reveal APK names; show generic current/total progress only. `安装App` may show each APK result and current APK name.
|
||||||
- Post-install overlays to enable:
|
|
||||||
- `com.tinnove.launcher.overlay`
|
### Mazda-EZ60
|
||||||
- `com.tinnove.scenemode.overlay`
|
|
||||||
- `com.incall.dvr.overlay`
|
- Based on `CS55-Q05/CS55-Q05_Installer.py` / 逸动 flow.
|
||||||
- Post-install packages to disable:
|
- Uses auto-password shell (`adb36987`) for VIN reads, `pm install`, overlay enable, disable commands, settings, and reboot.
|
||||||
- `com.carinno.p1`
|
- Installs APKs from extracted `apps` via push to `/data/local/tmp` then `pm install -r -d`.
|
||||||
- `com.wtcl.electronicdirections`
|
- `Mazda-EZ60/Mazda-EZ60_1.2.py` should show 7za extraction progress with stream parsing.
|
||||||
- `com.ximalaya.ting.android.car`
|
- Mazda 1.2 package cache is `%LOCALAPPDATA%\.cache\system\.android\apps_cache_Mazda_EZ60`; clean it on startup, before extraction, on normal window close, and through `atexit`.
|
||||||
- `com.tinnove.netease.music`
|
- Mazda `package-key` requests must include both `vin` and `vehicleName`; cache `data.vehicleName` from `auth-check`, and query `auth-check` before `package-key` if it is missing.
|
||||||
- `com.migu.miguplay.car`
|
- Mazda hosts setup should keep exactly one target mapping for `spm.auto-pai.com`: replace mismatched IPs with `103.236.55.140 spm.auto-pai.com` instead of appending a conflicting duplicate. If hosts setup fails, tell the user `环境配置失败`.
|
||||||
- `cn.cmvideo.car.play`
|
- Mazda UI should not show the company/about subtitle.
|
||||||
- `com.tinnove.carshow`
|
- Mazda language switching should cover all operator-facing buttons, labels, dialogs, logs, progress text, hotspot status, password query UI, and quick-language popup text. The right-side usage tips should render from translated `hint_lines` with wrapping so English text does not overlap or overflow.
|
||||||
- `com.tinnove.changba`
|
- Mazda normal logs/prompts should keep only key operator-facing outcomes and fuzzy resource/environment errors. Do not show package names, language package APK names, raw commands, paths, or command output during `刷入语言包`; manual `安装App` may show selected APK filenames. VIN and `vehicleName` are not sensitive and may remain visible.
|
||||||
- `com.qiyi.video.iv`
|
- Mazda debug mode uses `Ctrl+Shift+D` and verifies the password through `POST /api/authorizations/verify-debug-mode-password`; detailed ADB/7za/API output belongs in debug mode only.
|
||||||
- User cancelled the `Ctrl+Shift+E` direct extract test request for Mazda; do not add it unless asked again.
|
- Mazda pack scripts `pack_mazda_ez60_1.0.bat` and `pack_mazda_ez60_1.2.bat` are Cython-only. If dependencies, Cython compilation, PYD generation, resource copy, PyInstaller, or final exe output fail, stop immediately with an `[ERROR]` reason; do not fall back to normal PyInstaller.
|
||||||
|
- Regardless of APK install failures, run post-install configuration after the install loop.
|
||||||
### Q05_Lidar
|
- Post-install overlays to enable:
|
||||||
|
- `com.tinnove.launcher.overlay`
|
||||||
- This is the Q05_Lidar-specific tool and must not be confused with any ordinary Q05 variant or package.
|
- `com.tinnove.scenemode.overlay`
|
||||||
- Based on the shared installer visual style, but its resource structure and flashing flow are Q05_Lidar-specific.
|
- `com.incall.dvr.overlay`
|
||||||
- The first-row `获取权限` button installs `runtime.dat` -> `base.apk`, reboots to fastboot, waits for a real `fastboot devices` row like `<serial> fastboot` with a non-aggressive interval, then fetches a boot key through `POST /api/authorizations/boot-challenge` then `POST /api/authorizations/boot-key`, decrypts embedded `resource.dat`, flashes `init_boot`, immediately reboots, and deletes the temporary img. Keep the decrypted img lifetime as short as possible.
|
- Post-install packages to disable:
|
||||||
- `resource.dat` is AES-GCM encrypted and must match the server `BOOT_KEY`; the tool only accepts `data.sessionKey` from `boot-key`.
|
- `com.carinno.p1`
|
||||||
- Device fingerprint data sent to the server includes ADB serial, `ro.serialno`, `ro.boot.serialno`, manufacturer, model, device, build fingerprint, and VIN.
|
- `com.wtcl.electronicdirections`
|
||||||
- `刷入语言包` installs Magisk modules, not APKs. It opens `com.topjohnwu.magisk`, warns the user to grant Shell/root permission, verifies `/debug_ramdisk/su -c "id"` returns `uid=0`, extracts `Q05_Lidar-package.bin`, then pushes module files to `/data/local/tmp/q05_lidar_modules/<MODID>/` and root-copies them into `/data/adb/modules/<MODID>`.
|
- `com.ximalaya.ting.android.car`
|
||||||
- `Q05_Lidar-package.bin` should unpack with module files at archive root: `module.prop`, scripts, `system/`, and `disable-wireless-adb-vecentek-magisk.zip`; do not wrap them in an outer `Q05_LIDAR_DATA/` directory.
|
- `com.tinnove.netease.music`
|
||||||
- Q05_Lidar package cache is `%LOCALAPPDATA%\.cache\system\.android\apps_cache_Q05_Lidar`; the tool cleans it on startup/extraction and on normal/atexit shutdown.
|
- `com.migu.miguplay.car`
|
||||||
- Q05_Lidar `runtime.dat` cache is `%LOCALAPPDATA%\.cache\system\.android\apps_cache_q05_lidar_runtime`; treat it as temporary and clean stale contents before extraction.
|
- `cn.cmvideo.car.play`
|
||||||
- `Q05_Lidar-package.bin` is an external release file next to the exe because it is large. `resource.dat` is embedded in the exe; `runtime.dat` should be copied next to the exe by the pack script.
|
- `com.tinnove.carshow`
|
||||||
- `package-key` must include the `vehicleName` returned by `auth-check` for the VIN. The tool caches `data.vehicleName` from password query / authorization check and uses it for package-key; if missing, query `auth-check` first rather than falling back to a hardcoded Q05_Lidar value.
|
- `com.tinnove.changba`
|
||||||
- All `adb shell` commands in Q05_Lidar, including Magisk launch and `/debug_ramdisk/su -c ...`, must go through `run_adb_shell()` so the tool silently sends `adb36987`.
|
- `com.qiyi.video.iv`
|
||||||
- The `安装App` button remains the APK install path: file picker -> `adb push` -> `setprop vecentek.model 1` -> `pm install -r -d -f` -> cleanup. Do not replace it with the Magisk module flow.
|
- User cancelled the `Ctrl+Shift+E` direct extract test request for Mazda; do not add it unless asked again.
|
||||||
- Temporary debug mode exists only for development and should be removed before release when requested. Press `Ctrl+Shift+D`; the password is verified through `POST /api/authorizations/verify-debug-mode-password`.
|
|
||||||
- In Q05_Lidar debug mode, hidden buttons appear for:
|
### Q05_Lidar
|
||||||
- `指纹测试`: collect and log device fingerprint fields plus local SHA256 summary.
|
|
||||||
- `解密测试`: if VIN/device is available, fetch boot key; otherwise prompt for a pasted `BOOT_KEY`/`sessionKey`, decrypt `resource.dat` locally to a temporary img, log size/SHA256, then delete it.
|
- This is the Q05_Lidar-specific tool and must not be confused with any ordinary Q05 variant or package.
|
||||||
- `解压测试`: fetch `package-key`, extract `Q05_Lidar-package.bin`, and verify the main Magisk module plus `disable_wireless_adb_vecentek` module can be identified.
|
- Based on the shared installer visual style, but its resource structure and flashing flow are Q05_Lidar-specific.
|
||||||
- Do not log the actual boot key/session key in debug mode.
|
- The first-row `获取权限` button installs `runtime.dat` -> `base.apk`, reboots to fastboot, waits for a real `fastboot devices` row like `<serial> fastboot` with a non-aggressive interval, then fetches a boot key through `POST /api/authorizations/boot-challenge` then `POST /api/authorizations/boot-key`, decrypts embedded `resource.dat`, flashes `init_boot`, immediately reboots, and deletes the temporary img. Keep the decrypted img lifetime as short as possible.
|
||||||
|
- `resource.dat` is AES-GCM encrypted and must match the server `BOOT_KEY`; the tool only accepts `data.sessionKey` from `boot-key`.
|
||||||
### Yidong/app-yidong.py
|
- Device fingerprint data sent to the server includes ADB serial, `ro.serialno`, `ro.boot.serialno`, manufacturer, model, device, build fingerprint, and VIN.
|
||||||
|
- `刷入语言包` installs Magisk modules, not APKs. It opens `com.topjohnwu.magisk`, warns the user to grant Shell/root permission, verifies `/debug_ramdisk/su -c "id"` returns `uid=0`, extracts `Q05_Lidar-package.bin`, then pushes module files to `/data/local/tmp/q05_lidar_modules/<MODID>/` and root-copies them into `/data/adb/modules/<MODID>`.
|
||||||
- Uses `apps_cache_yidong`.
|
- `Q05_Lidar-package.bin` should unpack with module files at archive root: `module.prop`, scripts, `system/`, and `disable-wireless-adb-vecentek-magisk.zip`; do not wrap them in an outer `Q05_LIDAR_DATA/` directory.
|
||||||
- Has 7za compatibility handling for progress switches and `Incorrect command line` fallback.
|
- Q05_Lidar package cache is `%LOCALAPPDATA%\.cache\system\.android\apps_cache_Q05_Lidar`; the tool cleans it on startup/extraction and on normal/atexit shutdown.
|
||||||
- `Yidong/pack_yidong.bat` output name is ASCII: `Changan-Yidong-Language-Installer.exe`, to avoid CMD codepage issues with Chinese `NAME`.
|
- Q05_Lidar `runtime.dat` cache is `%LOCALAPPDATA%\.cache\system\.android\apps_cache_q05_lidar_runtime`; treat it as temporary and clean stale contents before extraction.
|
||||||
|
- `Q05_Lidar-package.bin` is an external release file next to the exe because it is large. `resource.dat` is embedded in the exe; `runtime.dat` should be copied next to the exe by the pack script.
|
||||||
## Build Notes
|
- `package-key` must include the `vehicleName` returned by `auth-check` for the VIN. The tool caches `data.vehicleName` from password query / authorization check and uses it for package-key; if missing, query `auth-check` first rather than falling back to a hardcoded Q05_Lidar value.
|
||||||
|
- All `adb shell` commands in Q05_Lidar, including Magisk launch and `/debug_ramdisk/su -c ...`, must go through `run_adb_shell()` so the tool silently sends `adb36987`.
|
||||||
- Pack scripts install/use `pyinstaller`, `cython`, and usually `pyzipper`.
|
- The `安装App` button remains the APK install path: file picker -> `adb push` -> `setprop vecentek.model 1` -> `pm install -r -d -f` -> cleanup. Do not replace it with the Magisk module flow.
|
||||||
- Cython success requires Microsoft C++ Build Tools.
|
- Temporary debug mode exists only for development and should be removed before release when requested. Press `Ctrl+Shift+D`; the password is verified through `POST /api/authorizations/verify-debug-mode-password`.
|
||||||
- Cython success signs in logs:
|
- In Q05_Lidar debug mode, hidden buttons appear for:
|
||||||
- `building '_core' extension`
|
- `指纹测试`: collect and log device fingerprint fields plus local SHA256 summary.
|
||||||
- `_core.cpXXX-win_amd64.pyd`
|
- `解密测试`: if VIN/device is available, fetch boot key; otherwise prompt for a pasted `BOOT_KEY`/`sessionKey`, decrypt `resource.dat` locally to a temporary img, log size/SHA256, then delete it.
|
||||||
- `PYD: _core...pyd`
|
- `解压测试`: fetch `package-key`, extract `Q05_Lidar-package.bin`, and verify the main Magisk module plus `disable_wireless_adb_vecentek` module can be identified.
|
||||||
- output under `dist_cy\dist\...exe`
|
- Do not log the actual boot key/session key in debug mode.
|
||||||
- If logs show `[WARN] Cython failed, fallback` and `[INFO] Normal PyInstaller`, the exe still builds but is normal PyInstaller and easier to reverse.
|
|
||||||
- For security-sensitive tools like Q05_Lidar, do not keep a normal PyInstaller fallback. If Cython fails or no `_core*.pyd` is generated, stop the build and show an error.
|
### Yidong/app-yidong.py
|
||||||
- A Cython onefile PyInstaller build should use a tiny `launcher.py` that imports `main` from compiled `_core.pyd`, and the exe archive should contain `_core*.pyd`. Confirm with PyInstaller archive viewer when in doubt.
|
|
||||||
- `UNIZ/pack_uniz.bat` and `Mazda-EZ60/pack_mazda_ez60.bat` use ASCII output names to avoid CMD encoding problems.
|
- Uses `apps_cache_yidong`.
|
||||||
- Generated `.exe`, `.spec`, `build/`, `dist/`, and `dist_cy/` are build artifacts and should not be committed unless explicitly requested.
|
- Has 7za compatibility handling for progress switches and `Incorrect command line` fallback.
|
||||||
|
- `Yidong/pack_yidong.bat` output name is ASCII: `Changan-Yidong-Language-Installer.exe`, to avoid CMD codepage issues with Chinese `NAME`.
|
||||||
## Key Behaviors To Preserve
|
|
||||||
|
## Build Notes
|
||||||
1. Keep original tools untouched when a fixed or model-specific copy exists.
|
|
||||||
2. Preserve VIN-based authorization for normal flashing tools.
|
- Pack scripts install/use `pyinstaller`, `cython`, and usually `pyzipper`.
|
||||||
3. Fetch `package-key` from the server instead of hardcoding package passwords.
|
- Cython success requires Microsoft C++ Build Tools.
|
||||||
4. Keep all shell commands in 逸动/Mazda tools behind `run_adb_shell()`.
|
- Cython success signs in logs:
|
||||||
5. Keep UNI-Z shell-free.
|
- `building '_core' extension`
|
||||||
6. Use `run_on_ui_thread()` for all tkinter UI updates from worker threads.
|
- `_core.cpXXX-win_amd64.pyd`
|
||||||
7. Keep shared Android/7za binaries under root `tools/` and have pack scripts copy from there.
|
- `PYD: _core...pyd`
|
||||||
|
- output under `dist_cy\dist\...exe`
|
||||||
## Current Local State (2026-05-28)
|
- If logs show `[WARN] Cython failed, fallback` and `[INFO] Normal PyInstaller`, the exe still builds but is normal PyInstaller and easier to reverse.
|
||||||
|
- For security-sensitive tools like Q05_Lidar, do not keep a normal PyInstaller fallback. If Cython fails or no `_core*.pyd` is generated, stop the build and show an error.
|
||||||
- `UNIZ/UNIZ.py` and `UNIZ/pack_uniz.bat` exist locally. Cython build has succeeded after installing Microsoft C++ Build Tools, producing `dist_cy\dist\UNIZ-Language-Pusher.exe`.
|
- A Cython onefile PyInstaller build should use a tiny `launcher.py` that imports `main` from compiled `_core.pyd`, and the exe archive should contain `_core*.pyd`. Confirm with PyInstaller archive viewer when in doubt.
|
||||||
- `Mazda-EZ60/Mazda-EZ60.py` and `Mazda-EZ60/pack_mazda_ez60.bat` exist locally. Mazda has 7za progress extraction, unconditional post-install configuration, three overlay enables, and nine package disables.
|
- `UNIZ/pack_uniz.bat` and Mazda-EZ60 pack scripts use ASCII output names to avoid CMD encoding problems.
|
||||||
- `Yidong/app-yidong.py` has been updated for 7za progress compatibility and `Incorrect command line` fallback.
|
- Generated `.exe`, `.spec`, `build/`, `dist/`, and `dist_cy/` are build artifacts and should not be committed unless explicitly requested.
|
||||||
- `Yidong/pack_yidong.bat` has been updated with quoted paths, `cd /d "%~dp0"`, and ASCII output name.
|
|
||||||
- `.gitignore` has been expanded to ignore generated exe/spec artifacts.
|
## Key Behaviors To Preserve
|
||||||
- There may be untracked local build outputs and generated specs; inspect `git status --ignored` before committing.
|
|
||||||
|
1. Keep original tools untouched when a fixed or model-specific copy exists.
|
||||||
## Known Issues
|
2. Preserve VIN-based authorization for normal flashing tools.
|
||||||
|
3. Fetch `package-key` from the server instead of hardcoding package passwords.
|
||||||
- Some older tools still have minimal exception handling and bare `except: pass`.
|
4. Keep all shell commands in 逸动/Mazda tools behind `run_adb_shell()`.
|
||||||
- `test_extract.py` hardcodes a password and should not be treated as production flow.
|
5. Keep UNI-Z shell-free.
|
||||||
- `on_disable_upgrade` behavior is Windows/vehicle specific.
|
6. Use `run_on_ui_thread()` for all tkinter UI updates from worker threads.
|
||||||
- Pure PyInstaller fallback is easy to reverse; prefer successful Cython builds for release.
|
7. Keep shared Android/7za binaries under root `tools/` and have pack scripts copy from there.
|
||||||
|
|
||||||
|
## Current Local State (2026-05-28)
|
||||||
|
|
||||||
|
- `UNIZ/UNIZ.py` and `UNIZ/pack_uniz.bat` exist locally. Cython build has succeeded after installing Microsoft C++ Build Tools, producing `dist_cy\dist\UNIZ-Language-Pusher.exe`.
|
||||||
|
- `Mazda-EZ60/Mazda-EZ60_1.2.py` with `pack_mazda_ez60_1.2.bat`, and `Mazda-EZ60/Mazda_EZ60-Language-Install_v1.0.py` with `pack_mazda_ez60_1.0.bat`, exist locally. Mazda has 7za progress extraction, Cython-only packaging, and version-specific output names.
|
||||||
|
- `Yidong/app-yidong.py` has been updated for 7za progress compatibility and `Incorrect command line` fallback.
|
||||||
|
- `Yidong/pack_yidong.bat` has been updated with quoted paths, `cd /d "%~dp0"`, and ASCII output name.
|
||||||
|
- `.gitignore` has been expanded to ignore generated exe/spec artifacts.
|
||||||
|
- There may be untracked local build outputs and generated specs; inspect `git status --ignored` before committing.
|
||||||
|
|
||||||
|
## Known Issues
|
||||||
|
|
||||||
|
- Some older tools still have minimal exception handling and bare `except: pass`.
|
||||||
|
- `test_extract.py` hardcodes a password and should not be treated as production flow.
|
||||||
|
- `on_disable_upgrade` behavior is Windows/vehicle specific.
|
||||||
|
- Pure PyInstaller fallback is easy to reverse; prefer successful Cython builds for release.
|
||||||
|
|||||||
@@ -59,9 +59,24 @@ def find_tool(file_name, fallback=None):
|
|||||||
path = find_resource(file_name)
|
path = find_resource(file_name)
|
||||||
if path.exists():
|
if path.exists():
|
||||||
return str(path)
|
return str(path)
|
||||||
return fallback or str(path)
|
return fallback or str(path)
|
||||||
|
|
||||||
|
|
||||||
|
def set_windows_app_user_model_id():
|
||||||
|
if sys.platform != 'win32':
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
import ctypes
|
||||||
|
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(
|
||||||
|
"yibin.keyi.cs55.q05.language.installer"
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ADKAPKGUI:
|
class ADKAPKGUI:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
set_windows_app_user_model_id()
|
||||||
self.root = tk.Tk()
|
self.root = tk.Tk()
|
||||||
self.root.title("长安语言刷入工具")
|
self.root.title("长安语言刷入工具")
|
||||||
self.root.geometry("900x620")
|
self.root.geometry("900x620")
|
||||||
@@ -101,7 +116,9 @@ class ADKAPKGUI:
|
|||||||
self.lang = 'zh'
|
self.lang = 'zh'
|
||||||
self.T = {
|
self.T = {
|
||||||
'zh': {
|
'zh': {
|
||||||
|
'window_title': 'CS55Plus/Q05 语言刷入工具',
|
||||||
'title': 'CS55Plus/Q05',
|
'title': 'CS55Plus/Q05',
|
||||||
|
'btn_unlock_install': '🔓 解锁安装权限',
|
||||||
'btn_push': '📦 刷入语言包',
|
'btn_push': '📦 刷入语言包',
|
||||||
'btn_install': '📱 安装App',
|
'btn_install': '📱 安装App',
|
||||||
'btn_language': '🌐 语言设置',
|
'btn_language': '🌐 语言设置',
|
||||||
@@ -111,6 +128,8 @@ class ADKAPKGUI:
|
|||||||
'btn_disable_upgrade': '❌ 禁用升级',
|
'btn_disable_upgrade': '❌ 禁用升级',
|
||||||
'btn_clear_log': '🗑 清空日志',
|
'btn_clear_log': '🗑 清空日志',
|
||||||
'btn_query_pwd': '查询密码',
|
'btn_query_pwd': '查询密码',
|
||||||
|
'pwd_query_label': '工程密码查询:',
|
||||||
|
'vin_placeholder': '请输入VIN',
|
||||||
'device_label': '设备:',
|
'device_label': '设备:',
|
||||||
'vin_label': 'VIN码:',
|
'vin_label': 'VIN码:',
|
||||||
'auth_label': '授权:',
|
'auth_label': '授权:',
|
||||||
@@ -127,15 +146,60 @@ class ADKAPKGUI:
|
|||||||
'hint_factory': '🔧 关闭车辆WI-FI和4G网络,拨号获取的密码进入工程模式',
|
'hint_factory': '🔧 关闭车辆WI-FI和4G网络,拨号获取的密码进入工程模式',
|
||||||
'hotspot_title': '📶 电脑热点',
|
'hotspot_title': '📶 电脑热点',
|
||||||
'hotspot_start': '🔧 打开热点设置',
|
'hotspot_start': '🔧 打开热点设置',
|
||||||
|
'hotspot_name_detecting': '名称: 检测中...',
|
||||||
|
'hotspot_name_value': '名称: {ssid}',
|
||||||
|
'hotspot_name_empty': '名称: 未配置',
|
||||||
|
'hotspot_pwd_default': '密码: changan2024',
|
||||||
|
'hotspot_pwd_value': '密码: {password}',
|
||||||
|
'hotspot_status_value': '状态: {status}',
|
||||||
|
'hotspot_status_off': '状态: 未启动',
|
||||||
'hint_title': '💡 使用提示',
|
'hint_title': '💡 使用提示',
|
||||||
|
'hint_lines': [
|
||||||
|
'1. 确保电脑已开启热点',
|
||||||
|
'2. 拨号进入工厂模式,点击调试工具',
|
||||||
|
'3. 需要云端认证时,点击车机状态栏',
|
||||||
|
' Wi-Fi图标,连接上方显示的热点',
|
||||||
|
'4. 连接后点击车机“云端认证”按钮',
|
||||||
|
'5. 打开ADB后即可正常刷入语言包',
|
||||||
|
],
|
||||||
'theme_dark': '🌙 暗色',
|
'theme_dark': '🌙 暗色',
|
||||||
'theme_light': '☀️ 亮色',
|
'theme_light': '☀️ 亮色',
|
||||||
'lang_zh': '中',
|
'lang_zh': '中',
|
||||||
'lang_en': 'EN',
|
'lang_en': 'EN',
|
||||||
'about_company': '宜宾科宜科技有限公司 - 出口改装一站式服务',
|
'log_lang_changed': '语言已切换为中文',
|
||||||
|
'log_cleared': '日志已清空',
|
||||||
|
'log_unlock_success': '安装权限已解锁',
|
||||||
|
'log_unlock_failed': '安装权限解锁失败: {output}',
|
||||||
|
'msg_success_title': '成功',
|
||||||
|
'msg_error_title': '错误',
|
||||||
|
'msg_warn_title': '警告',
|
||||||
|
'msg_device_not_connected_title': '设备未连接',
|
||||||
|
'msg_device_not_connected': '请先连接设备并点击「检查」按钮刷新状态!',
|
||||||
|
'msg_unlock_success': '安装权限已解锁,可以继续安装或刷入语言包。',
|
||||||
|
'msg_unlock_failed': '安装权限解锁失败:{output}',
|
||||||
|
'msg_input_vin': '请输入VIN码',
|
||||||
|
'msg_need_vin': '请先刷新设备状态并获取VIN码',
|
||||||
|
'msg_auth_failed_title': '授权失败',
|
||||||
|
'msg_device_unauthorized': '设备未授权',
|
||||||
|
'msg_data_prepare_failed': '数据准备失败!',
|
||||||
|
'msg_resource_prepare_failed': '资源准备失败!',
|
||||||
|
'msg_resource_dir_missing': '资源目录未找到',
|
||||||
|
'quick_lang_title': '快捷语言设置',
|
||||||
|
'quick_lang_header': '选择目标语言',
|
||||||
|
'quick_lang_hint': '点击按钮即可将系统语言切换为对应语言,重启后生效',
|
||||||
|
'quick_lang_system': '⚙️ 打开系统语言设置(手动选择)',
|
||||||
|
'quick_lang_success_title': '设置成功',
|
||||||
|
'quick_lang_success': '系统语言已设置为 {language}\n\n⚠️ 请重启设备使其生效。',
|
||||||
|
'quick_lang_failed_title': '设置失败',
|
||||||
|
'quick_lang_failed': '语言设置失败!\n\n{output}',
|
||||||
|
'quick_lang_names': ['🇨🇳 中文', '英 English', '俄 Русский', '法 Français', '西 Español', '葡 Português', '意 Italiano', '阿 العربية'],
|
||||||
|
'log_quick_lang_success': '语言已设置为 {language}',
|
||||||
|
'log_quick_lang_failed': '语言设置失败: {output}',
|
||||||
},
|
},
|
||||||
'en': {
|
'en': {
|
||||||
|
'window_title': 'CS55Plus/Q05 Language Installer',
|
||||||
'title': 'CS55Plus/Q05',
|
'title': 'CS55Plus/Q05',
|
||||||
|
'btn_unlock_install': '🔓 Unlock Install',
|
||||||
'btn_push': '📦 Flash Lang Pkg',
|
'btn_push': '📦 Flash Lang Pkg',
|
||||||
'btn_install': '📱 Install App',
|
'btn_install': '📱 Install App',
|
||||||
'btn_language': '🌐 Language',
|
'btn_language': '🌐 Language',
|
||||||
@@ -145,6 +209,8 @@ class ADKAPKGUI:
|
|||||||
'btn_disable_upgrade': '❌ Disable OTA',
|
'btn_disable_upgrade': '❌ Disable OTA',
|
||||||
'btn_clear_log': '🗑 Clear Log',
|
'btn_clear_log': '🗑 Clear Log',
|
||||||
'btn_query_pwd': 'Query Pwd',
|
'btn_query_pwd': 'Query Pwd',
|
||||||
|
'pwd_query_label': 'Factory password:',
|
||||||
|
'vin_placeholder': 'Enter VIN',
|
||||||
'device_label': 'Device:',
|
'device_label': 'Device:',
|
||||||
'vin_label': 'VIN:',
|
'vin_label': 'VIN:',
|
||||||
'auth_label': 'Auth:',
|
'auth_label': 'Auth:',
|
||||||
@@ -161,12 +227,55 @@ class ADKAPKGUI:
|
|||||||
'hint_factory': '🔧 Turn off WiFi & 4G, enter factory mode with dial code',
|
'hint_factory': '🔧 Turn off WiFi & 4G, enter factory mode with dial code',
|
||||||
'hotspot_title': '📶 Hotspot',
|
'hotspot_title': '📶 Hotspot',
|
||||||
'hotspot_start': '🔧 Open Hotspot Settings',
|
'hotspot_start': '🔧 Open Hotspot Settings',
|
||||||
|
'hotspot_name_detecting': 'Name: detecting...',
|
||||||
|
'hotspot_name_value': 'Name: {ssid}',
|
||||||
|
'hotspot_name_empty': 'Name: not configured',
|
||||||
|
'hotspot_pwd_default': 'Password: changan2024',
|
||||||
|
'hotspot_pwd_value': 'Password: {password}',
|
||||||
|
'hotspot_status_value': 'Status: {status}',
|
||||||
|
'hotspot_status_off': 'Status: off',
|
||||||
'hint_title': '💡 Tips',
|
'hint_title': '💡 Tips',
|
||||||
|
'hint_lines': [
|
||||||
|
'1. Make sure the PC hotspot is enabled',
|
||||||
|
'2. Enter factory mode from the dialer',
|
||||||
|
'3. When cloud auth is needed, tap the',
|
||||||
|
' Wi-Fi icon and connect to the hotspot',
|
||||||
|
'4. Tap Cloud Auth on the vehicle screen',
|
||||||
|
'5. Enable ADB, then flash the language pack',
|
||||||
|
],
|
||||||
'theme_dark': '🌙 Dark',
|
'theme_dark': '🌙 Dark',
|
||||||
'theme_light': '☀️ Light',
|
'theme_light': '☀️ Light',
|
||||||
'lang_zh': '中',
|
'lang_zh': '中',
|
||||||
'lang_en': 'EN',
|
'lang_en': 'EN',
|
||||||
'about_company': 'Yibin Keyi Technology - Export Modification Service',
|
'log_lang_changed': 'Language switched to English',
|
||||||
|
'log_cleared': 'Log cleared',
|
||||||
|
'log_unlock_success': 'Install permission unlocked',
|
||||||
|
'log_unlock_failed': 'Install permission unlock failed: {output}',
|
||||||
|
'msg_success_title': 'Success',
|
||||||
|
'msg_error_title': 'Error',
|
||||||
|
'msg_warn_title': 'Warning',
|
||||||
|
'msg_device_not_connected_title': 'Device Not Connected',
|
||||||
|
'msg_device_not_connected': 'Connect the device and click Check first.',
|
||||||
|
'msg_unlock_success': 'Install permission is unlocked. You can continue installing or flashing.',
|
||||||
|
'msg_unlock_failed': 'Install permission unlock failed: {output}',
|
||||||
|
'msg_input_vin': 'Enter VIN',
|
||||||
|
'msg_need_vin': 'Refresh device status and get VIN first.',
|
||||||
|
'msg_auth_failed_title': 'Authorization Failed',
|
||||||
|
'msg_device_unauthorized': 'Device is not authorized',
|
||||||
|
'msg_data_prepare_failed': 'Data preparation failed.',
|
||||||
|
'msg_resource_prepare_failed': 'Resource preparation failed.',
|
||||||
|
'msg_resource_dir_missing': 'Resource directory not found',
|
||||||
|
'quick_lang_title': 'Quick Language',
|
||||||
|
'quick_lang_header': 'Choose target language',
|
||||||
|
'quick_lang_hint': 'Tap a button to switch system language. Reboot to apply.',
|
||||||
|
'quick_lang_system': '⚙️ Open system language settings',
|
||||||
|
'quick_lang_success_title': 'Language Set',
|
||||||
|
'quick_lang_success': 'System language was set to {language}.\n\n⚠️ Reboot the device to apply it.',
|
||||||
|
'quick_lang_failed_title': 'Language Failed',
|
||||||
|
'quick_lang_failed': 'Language setting failed.\n\n{output}',
|
||||||
|
'quick_lang_names': ['Chinese', 'English', 'Russian', 'French', 'Spanish', 'Portuguese', 'Italian', 'Arabic'],
|
||||||
|
'log_quick_lang_success': 'Language set to {language}',
|
||||||
|
'log_quick_lang_failed': 'Language setting failed: {output}',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,10 +291,12 @@ class ADKAPKGUI:
|
|||||||
self.device_connected = False
|
self.device_connected = False
|
||||||
self._refreshing = False # 防止并发刷新
|
self._refreshing = False # 防止并发刷新
|
||||||
self.debug_mode = False # 调试模式
|
self.debug_mode = False # 调试模式
|
||||||
|
self.root.title(self.t('window_title'))
|
||||||
|
|
||||||
# 设置样式
|
# 设置样式
|
||||||
self.setup_styles()
|
self.setup_styles()
|
||||||
self.setup_ui()
|
self.setup_ui()
|
||||||
|
self.root.after(200, self.set_window_icon)
|
||||||
self.center_window()
|
self.center_window()
|
||||||
|
|
||||||
# 检查环境
|
# 检查环境
|
||||||
@@ -194,6 +305,38 @@ class ADKAPKGUI:
|
|||||||
# 启动设备状态监控
|
# 启动设备状态监控
|
||||||
self.start_device_monitor()
|
self.start_device_monitor()
|
||||||
|
|
||||||
|
def set_window_icon(self):
|
||||||
|
"""Set the Tk window/taskbar icon at runtime; PyInstaller --icon only sets the exe file icon."""
|
||||||
|
try:
|
||||||
|
icon_path = find_resource("cs55-q05.ico")
|
||||||
|
if icon_path.exists():
|
||||||
|
self.root.iconbitmap(str(icon_path))
|
||||||
|
self._set_windows_hwnd_icon(icon_path)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _set_windows_hwnd_icon(self, icon_path):
|
||||||
|
if sys.platform != 'win32':
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
import ctypes
|
||||||
|
user32 = ctypes.windll.user32
|
||||||
|
hwnd = self.root.winfo_id()
|
||||||
|
image_icon = 1
|
||||||
|
lr_loadfromfile = 0x00000010
|
||||||
|
wm_seticon = 0x0080
|
||||||
|
icon_small = 0
|
||||||
|
icon_big = 1
|
||||||
|
path = str(icon_path)
|
||||||
|
small = user32.LoadImageW(None, path, image_icon, 16, 16, lr_loadfromfile)
|
||||||
|
big = user32.LoadImageW(None, path, image_icon, 32, 32, lr_loadfromfile)
|
||||||
|
if small:
|
||||||
|
user32.SendMessageW(hwnd, wm_seticon, icon_small, small)
|
||||||
|
if big:
|
||||||
|
user32.SendMessageW(hwnd, wm_seticon, icon_big, big)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
def setup_styles(self):
|
def setup_styles(self):
|
||||||
"""设置自定义样式"""
|
"""设置自定义样式"""
|
||||||
style = ttk.Style()
|
style = ttk.Style()
|
||||||
@@ -235,28 +378,22 @@ class ADKAPKGUI:
|
|||||||
title_frame.pack_propagate(False)
|
title_frame.pack_propagate(False)
|
||||||
|
|
||||||
# 标题
|
# 标题
|
||||||
title_label = tk.Label(title_frame,
|
self.title_label = tk.Label(title_frame,
|
||||||
text="🚀 CS55Plus/Q05",
|
text="🚀 " + self.t('title'),
|
||||||
font=('Microsoft YaHei', 18, 'bold'),
|
font=('Microsoft YaHei', 18, 'bold'),
|
||||||
fg=self.colors['accent'],
|
fg=self.colors['accent'],
|
||||||
bg=self.colors['bg_dark'])
|
bg=self.colors['bg_dark'])
|
||||||
title_label.pack()
|
self.title_label.pack()
|
||||||
|
|
||||||
subtitle_label = tk.Label(title_frame,
|
|
||||||
text="@宜宾科宜科技有限公司 - 出口改装一站式服务",
|
|
||||||
font=('Microsoft YaHei', 9),
|
|
||||||
fg=self.colors['text_secondary'],
|
|
||||||
bg=self.colors['bg_dark'])
|
|
||||||
subtitle_label.pack()
|
|
||||||
|
|
||||||
# 工程密码查询区域
|
# 工程密码查询区域
|
||||||
pwd_query_frame = tk.Frame(left_frame, bg=self.colors['bg_light'], relief=tk.RAISED, bd=1)
|
pwd_query_frame = tk.Frame(left_frame, bg=self.colors['bg_light'], relief=tk.RAISED, bd=1)
|
||||||
pwd_query_frame.pack(fill=tk.X, pady=(0, 5), padx=5)
|
pwd_query_frame.pack(fill=tk.X, pady=(0, 5), padx=5)
|
||||||
|
|
||||||
tk.Label(pwd_query_frame, text="工程密码查询:",
|
self.pwd_query_label = tk.Label(pwd_query_frame, text=self.t('pwd_query_label'),
|
||||||
font=('Microsoft YaHei', 9),
|
font=('Microsoft YaHei', 9),
|
||||||
fg=self.colors['text'],
|
fg=self.colors['text'],
|
||||||
bg=self.colors['bg_light']).pack(side=tk.LEFT, padx=(10, 5), pady=5)
|
bg=self.colors['bg_light'])
|
||||||
|
self.pwd_query_label.pack(side=tk.LEFT, padx=(10, 5), pady=5)
|
||||||
|
|
||||||
self.vin_input = tk.Entry(pwd_query_frame,
|
self.vin_input = tk.Entry(pwd_query_frame,
|
||||||
font=('Consolas', 9),
|
font=('Consolas', 9),
|
||||||
@@ -265,12 +402,12 @@ class ADKAPKGUI:
|
|||||||
insertbackground='white',
|
insertbackground='white',
|
||||||
relief=tk.FLAT,
|
relief=tk.FLAT,
|
||||||
width=20)
|
width=20)
|
||||||
self.vin_input.insert(0, "请输入VIN")
|
self.vin_input.insert(0, self.t('vin_placeholder'))
|
||||||
self.vin_input.bind("<FocusIn>", self._on_vin_input_focus_in)
|
self.vin_input.bind("<FocusIn>", self._on_vin_input_focus_in)
|
||||||
self.vin_input.bind("<FocusOut>", self._on_vin_input_focus_out)
|
self.vin_input.bind("<FocusOut>", self._on_vin_input_focus_out)
|
||||||
self.vin_input.pack(side=tk.LEFT, padx=5, pady=5)
|
self.vin_input.pack(side=tk.LEFT, padx=5, pady=5)
|
||||||
|
|
||||||
self.btn_query_pwd = tk.Button(pwd_query_frame, text="查询密码",
|
self.btn_query_pwd = tk.Button(pwd_query_frame, text=self.t('btn_query_pwd'),
|
||||||
command=self.query_password_by_vin,
|
command=self.query_password_by_vin,
|
||||||
font=('Microsoft YaHei', 8),
|
font=('Microsoft YaHei', 8),
|
||||||
fg='white',
|
fg='white',
|
||||||
@@ -289,10 +426,11 @@ class ADKAPKGUI:
|
|||||||
# 工厂模式提示
|
# 工厂模式提示
|
||||||
factory_hint_frame = tk.Frame(left_frame, bg=self.colors['bg_dark'])
|
factory_hint_frame = tk.Frame(left_frame, bg=self.colors['bg_dark'])
|
||||||
factory_hint_frame.pack(fill=tk.X, pady=(0, 3))
|
factory_hint_frame.pack(fill=tk.X, pady=(0, 3))
|
||||||
tk.Label(factory_hint_frame, text="🔧 关闭车辆WI-FI和4G网络,拨号获取的密码进入工程模式",
|
self.hint_label = tk.Label(factory_hint_frame, text=self.t('hint_factory'),
|
||||||
font=('Microsoft YaHei', 8),
|
font=('Microsoft YaHei', 8),
|
||||||
fg=self.colors['warning'],
|
fg=self.colors['warning'],
|
||||||
bg=self.colors['bg_dark']).pack(side=tk.LEFT, padx=2)
|
bg=self.colors['bg_dark'])
|
||||||
|
self.hint_label.pack(side=tk.LEFT, padx=2)
|
||||||
|
|
||||||
# 按钮区域(两排,每排5个)
|
# 按钮区域(两排,每排5个)
|
||||||
button_frame = tk.Frame(left_frame, bg=self.colors['bg_light'], relief=tk.RAISED, bd=1)
|
button_frame = tk.Frame(left_frame, bg=self.colors['bg_light'], relief=tk.RAISED, bd=1)
|
||||||
@@ -305,26 +443,32 @@ class ADKAPKGUI:
|
|||||||
'relief': tk.FLAT,
|
'relief': tk.FLAT,
|
||||||
'cursor': 'hand2',
|
'cursor': 'hand2',
|
||||||
'height': 1,
|
'height': 1,
|
||||||
'width': 14
|
'width': 13
|
||||||
}
|
}
|
||||||
|
|
||||||
# 第一排按钮
|
# 第一排按钮
|
||||||
row1_frame = tk.Frame(button_frame, bg=self.colors['bg_light'])
|
row1_frame = tk.Frame(button_frame, bg=self.colors['bg_light'])
|
||||||
row1_frame.pack(pady=(8, 4))
|
row1_frame.pack(pady=(8, 4))
|
||||||
|
|
||||||
self.btn_push = tk.Button(row1_frame, text="📦 刷入语言包",
|
self.btn_push = tk.Button(row1_frame, text=self.t('btn_push'),
|
||||||
command=self.push_all_apks,
|
command=self.push_all_apks,
|
||||||
bg=self.colors['accent'],
|
bg=self.colors['accent'],
|
||||||
**btn_params)
|
**btn_params)
|
||||||
self.btn_push.pack(side=tk.LEFT, padx=4)
|
self.btn_push.pack(side=tk.LEFT, padx=4)
|
||||||
|
|
||||||
self.btn_install_all = tk.Button(row1_frame, text="📱 安装App",
|
self.btn_unlock_install = tk.Button(row1_frame, text=self.t('btn_unlock_install'),
|
||||||
|
command=self.unlock_install_permission,
|
||||||
|
bg=self.colors['warning'],
|
||||||
|
**btn_params)
|
||||||
|
self.btn_unlock_install.pack(side=tk.LEFT, padx=4)
|
||||||
|
|
||||||
|
self.btn_install_all = tk.Button(row1_frame, text=self.t('btn_install'),
|
||||||
command=self.install_apps,
|
command=self.install_apps,
|
||||||
bg=self.colors['accent'],
|
bg=self.colors['accent'],
|
||||||
**btn_params)
|
**btn_params)
|
||||||
self.btn_install_all.pack(side=tk.LEFT, padx=4)
|
self.btn_install_all.pack(side=tk.LEFT, padx=4)
|
||||||
|
|
||||||
self.btn_language = tk.Button(row1_frame, text="🌐 语言设置",
|
self.btn_language = tk.Button(row1_frame, text=self.t('btn_language'),
|
||||||
command=self.open_language_quick_set,
|
command=self.open_language_quick_set,
|
||||||
bg=self.colors['accent'],
|
bg=self.colors['accent'],
|
||||||
**btn_params)
|
**btn_params)
|
||||||
@@ -334,25 +478,25 @@ class ADKAPKGUI:
|
|||||||
row2_frame = tk.Frame(button_frame, bg=self.colors['bg_light'])
|
row2_frame = tk.Frame(button_frame, bg=self.colors['bg_light'])
|
||||||
row2_frame.pack(pady=(4, 8))
|
row2_frame.pack(pady=(4, 8))
|
||||||
|
|
||||||
self.btn_timezone = tk.Button(row2_frame, text="⏰ 时区设置",
|
self.btn_timezone = tk.Button(row2_frame, text=self.t('btn_timezone'),
|
||||||
command=self.open_timezone_settings,
|
command=self.open_timezone_settings,
|
||||||
bg=self.colors['accent'],
|
bg=self.colors['accent'],
|
||||||
**btn_params)
|
**btn_params)
|
||||||
self.btn_timezone.pack(side=tk.LEFT, padx=4)
|
self.btn_timezone.pack(side=tk.LEFT, padx=4)
|
||||||
|
|
||||||
self.btn_settings = tk.Button(row2_frame, text="⚙️ 安卓设置",
|
self.btn_settings = tk.Button(row2_frame, text=self.t('btn_settings'),
|
||||||
command=self.open_android_settings,
|
command=self.open_android_settings,
|
||||||
bg=self.colors['accent'],
|
bg=self.colors['accent'],
|
||||||
**btn_params)
|
**btn_params)
|
||||||
self.btn_settings.pack(side=tk.LEFT, padx=4)
|
self.btn_settings.pack(side=tk.LEFT, padx=4)
|
||||||
|
|
||||||
self.btn_reboot = tk.Button(row2_frame, text="🔄 重启设备",
|
self.btn_reboot = tk.Button(row2_frame, text=self.t('btn_reboot'),
|
||||||
command=self.reboot_device,
|
command=self.reboot_device,
|
||||||
bg=self.colors['warning'],
|
bg=self.colors['warning'],
|
||||||
**btn_params)
|
**btn_params)
|
||||||
self.btn_reboot.pack(side=tk.LEFT, padx=4)
|
self.btn_reboot.pack(side=tk.LEFT, padx=4)
|
||||||
|
|
||||||
self.btn_exit = tk.Button(row2_frame, text="❌ 禁用升级",
|
self.btn_exit = tk.Button(row2_frame, text=self.t('btn_disable_upgrade'),
|
||||||
command=self.on_disable_upgrade,
|
command=self.on_disable_upgrade,
|
||||||
bg=self.colors['error'],
|
bg=self.colors['error'],
|
||||||
**btn_params)
|
**btn_params)
|
||||||
@@ -371,12 +515,13 @@ class ADKAPKGUI:
|
|||||||
self.status_indicator.pack(side=tk.LEFT)
|
self.status_indicator.pack(side=tk.LEFT)
|
||||||
self.status_dot = self.status_indicator.create_oval(2, 2, 8, 8, fill='#636e72')
|
self.status_dot = self.status_indicator.create_oval(2, 2, 8, 8, fill='#636e72')
|
||||||
|
|
||||||
tk.Label(status_indicator_frame, text="设备:",
|
self.device_label = tk.Label(status_indicator_frame, text=self.t('device_label'),
|
||||||
font=('Microsoft YaHei', 9),
|
font=('Microsoft YaHei', 9),
|
||||||
fg=self.colors['text'],
|
fg=self.colors['text'],
|
||||||
bg=self.colors['bg_light']).pack(side=tk.LEFT, padx=(5, 3))
|
bg=self.colors['bg_light'])
|
||||||
|
self.device_label.pack(side=tk.LEFT, padx=(5, 3))
|
||||||
|
|
||||||
self.device_status_label = tk.Label(status_indicator_frame, text="未检测",
|
self.device_status_label = tk.Label(status_indicator_frame, text=self.t('status_detecting'),
|
||||||
font=('Microsoft YaHei', 9, 'bold'),
|
font=('Microsoft YaHei', 9, 'bold'),
|
||||||
fg='#636e72',
|
fg='#636e72',
|
||||||
bg=self.colors['bg_light'])
|
bg=self.colors['bg_light'])
|
||||||
@@ -385,11 +530,12 @@ class ADKAPKGUI:
|
|||||||
# VIN信息
|
# VIN信息
|
||||||
vin_frame = tk.Frame(status_bar_frame, bg=self.colors['bg_light'])
|
vin_frame = tk.Frame(status_bar_frame, bg=self.colors['bg_light'])
|
||||||
vin_frame.pack(side=tk.LEFT, padx=20, pady=5)
|
vin_frame.pack(side=tk.LEFT, padx=20, pady=5)
|
||||||
tk.Label(vin_frame, text="VIN码:",
|
self.vin_label_title = tk.Label(vin_frame, text=self.t('vin_label'),
|
||||||
font=('Microsoft YaHei', 9),
|
font=('Microsoft YaHei', 9),
|
||||||
fg=self.colors['text'],
|
fg=self.colors['text'],
|
||||||
bg=self.colors['bg_light']).pack(side=tk.LEFT)
|
bg=self.colors['bg_light'])
|
||||||
self.vin_label = tk.Label(vin_frame, text="未获取",
|
self.vin_label_title.pack(side=tk.LEFT)
|
||||||
|
self.vin_label = tk.Label(vin_frame, text=self.t('vin_none'),
|
||||||
font=('Microsoft YaHei', 9, 'bold'),
|
font=('Microsoft YaHei', 9, 'bold'),
|
||||||
fg='#636e72',
|
fg='#636e72',
|
||||||
bg=self.colors['bg_light'])
|
bg=self.colors['bg_light'])
|
||||||
@@ -398,25 +544,26 @@ class ADKAPKGUI:
|
|||||||
# 授权状态
|
# 授权状态
|
||||||
auth_frame = tk.Frame(status_bar_frame, bg=self.colors['bg_light'])
|
auth_frame = tk.Frame(status_bar_frame, bg=self.colors['bg_light'])
|
||||||
auth_frame.pack(side=tk.LEFT, padx=20, pady=5)
|
auth_frame.pack(side=tk.LEFT, padx=20, pady=5)
|
||||||
tk.Label(auth_frame, text="授权:",
|
self.auth_label_title = tk.Label(auth_frame, text=self.t('auth_label'),
|
||||||
font=('Microsoft YaHei', 9),
|
font=('Microsoft YaHei', 9),
|
||||||
fg=self.colors['text'],
|
fg=self.colors['text'],
|
||||||
bg=self.colors['bg_light']).pack(side=tk.LEFT)
|
bg=self.colors['bg_light'])
|
||||||
self.auth_label = tk.Label(auth_frame, text="未验证",
|
self.auth_label_title.pack(side=tk.LEFT)
|
||||||
|
self.auth_label = tk.Label(auth_frame, text=self.t('auth_none'),
|
||||||
font=('Microsoft YaHei', 9, 'bold'),
|
font=('Microsoft YaHei', 9, 'bold'),
|
||||||
fg='#636e72',
|
fg='#636e72',
|
||||||
bg=self.colors['bg_light'])
|
bg=self.colors['bg_light'])
|
||||||
self.auth_label.pack(side=tk.LEFT, padx=(5, 0))
|
self.auth_label.pack(side=tk.LEFT, padx=(5, 0))
|
||||||
|
|
||||||
# 刷新按钮
|
# 刷新按钮
|
||||||
refresh_btn = tk.Button(status_bar_frame, text="🔄 检查",
|
self.btn_refresh = tk.Button(status_bar_frame, text=self.t('btn_refresh'),
|
||||||
command=lambda: self.refresh_device_status(force=True),
|
command=lambda: self.refresh_device_status(force=True),
|
||||||
font=('Microsoft YaHei', 8),
|
font=('Microsoft YaHei', 8),
|
||||||
fg=self.colors['accent'],
|
fg=self.colors['accent'],
|
||||||
bg=self.colors['bg_light'],
|
bg=self.colors['bg_light'],
|
||||||
relief=tk.FLAT,
|
relief=tk.FLAT,
|
||||||
cursor='hand2')
|
cursor='hand2')
|
||||||
refresh_btn.pack(side=tk.RIGHT, padx=10, pady=5)
|
self.btn_refresh.pack(side=tk.RIGHT, padx=10, pady=5)
|
||||||
|
|
||||||
# 解压进度条框架
|
# 解压进度条框架
|
||||||
progress_frame = tk.Frame(left_frame, bg=self.colors['bg_dark'])
|
progress_frame = tk.Frame(left_frame, bg=self.colors['bg_dark'])
|
||||||
@@ -448,12 +595,13 @@ class ADKAPKGUI:
|
|||||||
log_title_frame.pack(fill=tk.X)
|
log_title_frame.pack(fill=tk.X)
|
||||||
log_title_frame.pack_propagate(False)
|
log_title_frame.pack_propagate(False)
|
||||||
|
|
||||||
tk.Label(log_title_frame, text="📋 运行日志",
|
self.log_title_label = tk.Label(log_title_frame, text=self.t('log_title'),
|
||||||
font=('Microsoft YaHei', 10, 'bold'),
|
font=('Microsoft YaHei', 10, 'bold'),
|
||||||
fg=self.colors['accent'],
|
fg=self.colors['accent'],
|
||||||
bg=self.colors['bg_dark']).pack(side=tk.LEFT, padx=10)
|
bg=self.colors['bg_dark'])
|
||||||
|
self.log_title_label.pack(side=tk.LEFT, padx=10)
|
||||||
|
|
||||||
self.btn_clear = tk.Button(log_title_frame, text="🗑 清空日志",
|
self.btn_clear = tk.Button(log_title_frame, text=self.t('btn_clear_log'),
|
||||||
command=self.clear_log,
|
command=self.clear_log,
|
||||||
font=('Microsoft YaHei', 8),
|
font=('Microsoft YaHei', 8),
|
||||||
fg=self.colors['text_secondary'],
|
fg=self.colors['text_secondary'],
|
||||||
@@ -489,14 +637,14 @@ class ADKAPKGUI:
|
|||||||
bottom_status.pack(fill=tk.X, pady=(5, 0))
|
bottom_status.pack(fill=tk.X, pady=(5, 0))
|
||||||
bottom_status.pack_propagate(False)
|
bottom_status.pack_propagate(False)
|
||||||
|
|
||||||
self.status_text = tk.Label(bottom_status, text="就绪",
|
self.status_text = tk.Label(bottom_status, text=self.t('status_ready'),
|
||||||
font=('Microsoft YaHei', 8),
|
font=('Microsoft YaHei', 8),
|
||||||
fg=self.colors['text_secondary'],
|
fg=self.colors['text_secondary'],
|
||||||
bg=self.colors['bg_light'])
|
bg=self.colors['bg_light'])
|
||||||
self.status_text.pack(side=tk.LEFT, padx=10)
|
self.status_text.pack(side=tk.LEFT, padx=10)
|
||||||
|
|
||||||
# 主题和语言切换按钮
|
# 主题和语言切换按钮
|
||||||
self.btn_theme_switch = tk.Button(bottom_status, text="🌙 暗色",
|
self.btn_theme_switch = tk.Button(bottom_status, text=self.t('theme_light'),
|
||||||
command=self.toggle_theme,
|
command=self.toggle_theme,
|
||||||
font=('Microsoft YaHei', 8),
|
font=('Microsoft YaHei', 8),
|
||||||
fg=self.colors['accent'],
|
fg=self.colors['accent'],
|
||||||
@@ -520,30 +668,31 @@ class ADKAPKGUI:
|
|||||||
hotspot_card = tk.Frame(right_frame, bg=self.colors['bg_dark'], relief=tk.RAISED, bd=1)
|
hotspot_card = tk.Frame(right_frame, bg=self.colors['bg_dark'], relief=tk.RAISED, bd=1)
|
||||||
hotspot_card.pack(fill=tk.X, padx=5, pady=(10, 5))
|
hotspot_card.pack(fill=tk.X, padx=5, pady=(10, 5))
|
||||||
|
|
||||||
tk.Label(hotspot_card, text="📶 电脑热点",
|
self.hotspot_title_label = tk.Label(hotspot_card, text=self.t('hotspot_title'),
|
||||||
font=('Microsoft YaHei', 11, 'bold'),
|
font=('Microsoft YaHei', 11, 'bold'),
|
||||||
fg=self.colors['accent'],
|
fg=self.colors['accent'],
|
||||||
bg=self.colors['bg_dark']).pack(pady=(8, 5))
|
bg=self.colors['bg_dark'])
|
||||||
|
self.hotspot_title_label.pack(pady=(8, 5))
|
||||||
|
|
||||||
self.hotspot_ssid_label = tk.Label(hotspot_card, text="名称: 检测中...",
|
self.hotspot_ssid_label = tk.Label(hotspot_card, text=self.t('hotspot_name_detecting'),
|
||||||
font=('Microsoft YaHei', 9),
|
font=('Microsoft YaHei', 9),
|
||||||
fg=self.colors['text'],
|
fg=self.colors['text'],
|
||||||
bg=self.colors['bg_dark'])
|
bg=self.colors['bg_dark'])
|
||||||
self.hotspot_ssid_label.pack(anchor='w', padx=10, pady=2)
|
self.hotspot_ssid_label.pack(anchor='w', padx=10, pady=2)
|
||||||
|
|
||||||
self.hotspot_pwd_label = tk.Label(hotspot_card, text="密码: changan2024",
|
self.hotspot_pwd_label = tk.Label(hotspot_card, text=self.t('hotspot_pwd_default'),
|
||||||
font=('Microsoft YaHei', 9),
|
font=('Microsoft YaHei', 9),
|
||||||
fg=self.colors['text'],
|
fg=self.colors['text'],
|
||||||
bg=self.colors['bg_dark'])
|
bg=self.colors['bg_dark'])
|
||||||
self.hotspot_pwd_label.pack(anchor='w', padx=10, pady=2)
|
self.hotspot_pwd_label.pack(anchor='w', padx=10, pady=2)
|
||||||
|
|
||||||
self.hotspot_status_label = tk.Label(hotspot_card, text="状态: 未启动",
|
self.hotspot_status_label = tk.Label(hotspot_card, text=self.t('hotspot_status_off'),
|
||||||
font=('Microsoft YaHei', 9),
|
font=('Microsoft YaHei', 9),
|
||||||
fg=self.colors['warning'],
|
fg=self.colors['warning'],
|
||||||
bg=self.colors['bg_dark'])
|
bg=self.colors['bg_dark'])
|
||||||
self.hotspot_status_label.pack(anchor='w', padx=10, pady=2)
|
self.hotspot_status_label.pack(anchor='w', padx=10, pady=2)
|
||||||
|
|
||||||
self.btn_hotspot = tk.Button(hotspot_card, text="🔧 打开热点设置",
|
self.btn_hotspot = tk.Button(hotspot_card, text=self.t('hotspot_start'),
|
||||||
command=self.start_hotspot_action,
|
command=self.start_hotspot_action,
|
||||||
font=('Microsoft YaHei', 8),
|
font=('Microsoft YaHei', 8),
|
||||||
fg='white',
|
fg='white',
|
||||||
@@ -556,36 +705,24 @@ class ADKAPKGUI:
|
|||||||
tk.Frame(right_frame, bg=self.colors['border'], height=1).pack(fill=tk.X, padx=8, pady=5)
|
tk.Frame(right_frame, bg=self.colors['border'], height=1).pack(fill=tk.X, padx=8, pady=5)
|
||||||
|
|
||||||
# 使用提示卡片
|
# 使用提示卡片
|
||||||
hint_card = tk.Frame(right_frame, bg=self.colors['bg_dark'], relief=tk.RAISED, bd=1)
|
self.hint_card = tk.Frame(right_frame, bg=self.colors['bg_dark'], relief=tk.RAISED, bd=1)
|
||||||
hint_card.pack(fill=tk.X, padx=5, pady=5)
|
self.hint_card.pack(fill=tk.X, padx=5, pady=5)
|
||||||
|
|
||||||
tk.Label(hint_card, text="💡 使用提示",
|
self.hint_title_label = tk.Label(self.hint_card, text=self.t('hint_title'),
|
||||||
font=('Microsoft YaHei', 11, 'bold'),
|
font=('Microsoft YaHei', 11, 'bold'),
|
||||||
fg=self.colors['warning'],
|
fg=self.colors['warning'],
|
||||||
bg=self.colors['bg_dark']).pack(pady=(8, 5))
|
bg=self.colors['bg_dark'])
|
||||||
|
self.hint_title_label.pack(pady=(8, 5))
|
||||||
|
|
||||||
hint_lines = [
|
self.hint_line_labels = []
|
||||||
"1. 确保电脑已开启热点",
|
self._render_hint_lines()
|
||||||
"2. 拨号进入工厂模式,点击调试工具",
|
|
||||||
"3. 需要云端认证时,点击车机状态栏",
|
|
||||||
"Wi-Fi图标,连接上方显示的热点",
|
|
||||||
"4. 连接后点击车机“云端认证”按钮",
|
|
||||||
"5. 打开ADB后即可正常刷入语言包",
|
|
||||||
]
|
|
||||||
for line in hint_lines:
|
|
||||||
tk.Label(hint_card, text=line,
|
|
||||||
font=('Microsoft YaHei', 8),
|
|
||||||
fg=self.colors['text_secondary'],
|
|
||||||
bg=self.colors['bg_dark'],
|
|
||||||
justify=tk.LEFT,
|
|
||||||
anchor='w').pack(anchor='w', padx=10)
|
|
||||||
|
|
||||||
# 绑定悬停效果
|
# 绑定悬停效果
|
||||||
self.bind_hover_effects()
|
self.bind_hover_effects()
|
||||||
|
|
||||||
def bind_hover_effects(self):
|
def bind_hover_effects(self):
|
||||||
"""绑定按钮悬停效果"""
|
"""绑定按钮悬停效果"""
|
||||||
buttons = [self.btn_push, self.btn_install_all,
|
buttons = [self.btn_push, self.btn_unlock_install, self.btn_install_all,
|
||||||
self.btn_language, self.btn_timezone, self.btn_settings,
|
self.btn_language, self.btn_timezone, self.btn_settings,
|
||||||
self.btn_reboot, self.btn_clear, self.btn_exit, self.btn_query_pwd,
|
self.btn_reboot, self.btn_clear, self.btn_exit, self.btn_query_pwd,
|
||||||
self.btn_hotspot]
|
self.btn_hotspot]
|
||||||
@@ -634,11 +771,37 @@ class ADKAPKGUI:
|
|||||||
def t(self, key):
|
def t(self, key):
|
||||||
return self.T.get(self.lang, self.T['zh']).get(key, key)
|
return self.T.get(self.lang, self.T['zh']).get(key, key)
|
||||||
|
|
||||||
|
def tf(self, key, **kwargs):
|
||||||
|
return str(self.t(key)).format(**kwargs)
|
||||||
|
|
||||||
|
def is_placeholder_vin(self, text):
|
||||||
|
return text in (self.T['zh']['vin_placeholder'], self.T['en']['vin_placeholder'])
|
||||||
|
|
||||||
def toggle_lang(self):
|
def toggle_lang(self):
|
||||||
self.lang = 'en' if self.lang == 'zh' else 'zh'
|
self.lang = 'en' if self.lang == 'zh' else 'zh'
|
||||||
self.btn_lang_switch.config(text=self.t('lang_en') if self.lang == 'zh' else self.t('lang_zh'))
|
self.btn_lang_switch.config(text=self.t('lang_en') if self.lang == 'zh' else self.t('lang_zh'))
|
||||||
self._refresh_ui_texts()
|
self._refresh_ui_texts()
|
||||||
self.log(f"语言已切换为 {'English' if self.lang == 'en' else '中文'}", "INFO")
|
self.log(self.t('log_lang_changed'), "INFO")
|
||||||
|
|
||||||
|
def _render_hint_lines(self):
|
||||||
|
if not getattr(self, 'hint_card', None):
|
||||||
|
return
|
||||||
|
for label in getattr(self, 'hint_line_labels', []):
|
||||||
|
label.destroy()
|
||||||
|
self.hint_line_labels = []
|
||||||
|
for line in self.t('hint_lines'):
|
||||||
|
label = tk.Label(
|
||||||
|
self.hint_card,
|
||||||
|
text=line,
|
||||||
|
font=('Microsoft YaHei', 8),
|
||||||
|
fg=self.colors['text_secondary'],
|
||||||
|
bg=self.colors['bg_dark'],
|
||||||
|
justify=tk.LEFT,
|
||||||
|
anchor='w',
|
||||||
|
wraplength=205
|
||||||
|
)
|
||||||
|
label.pack(anchor='w', fill=tk.X, padx=10, pady=1)
|
||||||
|
self.hint_line_labels.append(label)
|
||||||
|
|
||||||
def toggle_theme(self):
|
def toggle_theme(self):
|
||||||
if self.theme == 'dark':
|
if self.theme == 'dark':
|
||||||
@@ -672,9 +835,10 @@ class ADKAPKGUI:
|
|||||||
|
|
||||||
def _refresh_ui_texts(self):
|
def _refresh_ui_texts(self):
|
||||||
t = self.t
|
t = self.t
|
||||||
|
self.root.title(t('window_title'))
|
||||||
widgets = [
|
widgets = [
|
||||||
(getattr(self, 'title_label', None), 'title', None),
|
(getattr(self, 'title_label', None), 'title', None),
|
||||||
(getattr(self, 'subtitle_label', None), 'about_company', None),
|
(getattr(self, 'btn_unlock_install', None), 'btn_unlock_install', None),
|
||||||
(getattr(self, 'btn_push', None), 'btn_push', None),
|
(getattr(self, 'btn_push', None), 'btn_push', None),
|
||||||
(getattr(self, 'btn_install_all', None), 'btn_install', None),
|
(getattr(self, 'btn_install_all', None), 'btn_install', None),
|
||||||
(getattr(self, 'btn_language', None), 'btn_language', None),
|
(getattr(self, 'btn_language', None), 'btn_language', None),
|
||||||
@@ -691,22 +855,41 @@ class ADKAPKGUI:
|
|||||||
(getattr(self, 'auth_label_title', None), 'auth_label', None),
|
(getattr(self, 'auth_label_title', None), 'auth_label', None),
|
||||||
(getattr(self, 'btn_refresh', None), 'btn_refresh', None),
|
(getattr(self, 'btn_refresh', None), 'btn_refresh', None),
|
||||||
(getattr(self, 'hint_label', None), 'hint_factory', None),
|
(getattr(self, 'hint_label', None), 'hint_factory', None),
|
||||||
|
(getattr(self, 'pwd_query_label', None), 'pwd_query_label', None),
|
||||||
|
(getattr(self, 'hotspot_title_label', None), 'hotspot_title', None),
|
||||||
|
(getattr(self, 'hotspot_ssid_label', None), 'hotspot_name_detecting', None),
|
||||||
|
(getattr(self, 'hotspot_pwd_label', None), 'hotspot_pwd_default', None),
|
||||||
|
(getattr(self, 'hotspot_status_label', None), 'hotspot_status_off', None),
|
||||||
|
(getattr(self, 'btn_hotspot', None), 'hotspot_start', None),
|
||||||
|
(getattr(self, 'hint_title_label', None), 'hint_title', None),
|
||||||
]
|
]
|
||||||
for w, key, _ in widgets:
|
for w, key, _ in widgets:
|
||||||
if w: w.config(text=t(key))
|
if not w:
|
||||||
|
continue
|
||||||
|
text = t(key)
|
||||||
|
if key == 'title':
|
||||||
|
text = "🚀 " + text
|
||||||
|
w.config(text=text)
|
||||||
self.btn_theme_switch.config(text=t('theme_light') if self.theme == 'dark' else t('theme_dark'))
|
self.btn_theme_switch.config(text=t('theme_light') if self.theme == 'dark' else t('theme_dark'))
|
||||||
self.btn_lang_switch.config(text=t('lang_en') if self.lang == 'zh' else t('lang_zh'))
|
self.btn_lang_switch.config(text=t('lang_en') if self.lang == 'zh' else t('lang_zh'))
|
||||||
|
if self.is_placeholder_vin(self.vin_input.get()):
|
||||||
|
self.vin_input.delete(0, tk.END)
|
||||||
|
self.vin_input.insert(0, t('vin_placeholder'))
|
||||||
|
self._render_hint_lines()
|
||||||
if self.vin:
|
if self.vin:
|
||||||
self._update_device_status_impl(self.device_connected, self.vin,
|
self._update_device_status_impl(self.device_connected, self.vin,
|
||||||
getattr(self, '_last_authorized', False))
|
getattr(self, '_last_authorized', False))
|
||||||
|
|
||||||
def _log_impl(self, message, level="INFO"):
|
def _log_impl(self, message, level="INFO"):
|
||||||
"""日志写入的实际实现(必须在主线程调用)"""
|
"""日志写入的实际实现(必须在主线程调用)"""
|
||||||
|
if not self.debug_mode and level in ("INFO", "CMD"):
|
||||||
|
return
|
||||||
timestamp = datetime.now().strftime("%H:%M:%S")
|
timestamp = datetime.now().strftime("%H:%M:%S")
|
||||||
log_entry = f"[{timestamp}] [{level}] {message}\n"
|
log_entry = f"[{timestamp}] [{level}] {message}\n"
|
||||||
self.log_text.insert(tk.END, log_entry, level)
|
self.log_text.insert(tk.END, log_entry, level)
|
||||||
self.log_text.see(tk.END)
|
self.log_text.see(tk.END)
|
||||||
|
|
||||||
|
|
||||||
def log(self, message, level="INFO"):
|
def log(self, message, level="INFO"):
|
||||||
"""添加日志(线程安全)"""
|
"""添加日志(线程安全)"""
|
||||||
self.run_on_ui_thread(self._log_impl, message, level)
|
self.run_on_ui_thread(self._log_impl, message, level)
|
||||||
@@ -789,10 +972,35 @@ class ADKAPKGUI:
|
|||||||
if self.debug_mode:
|
if self.debug_mode:
|
||||||
return True
|
return True
|
||||||
if not self.device_connected:
|
if not self.device_connected:
|
||||||
messagebox.showwarning("设备未连接", "请先连接设备并点击「检查」按钮刷新状态!")
|
messagebox.showwarning(self.t('msg_device_not_connected_title'), self.t('msg_device_not_connected'))
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def unlock_install_permission(self):
|
||||||
|
"""解锁安装权限。"""
|
||||||
|
if not self.check_device_connection():
|
||||||
|
return
|
||||||
|
|
||||||
|
def worker():
|
||||||
|
ok, output = self.run_adb_shell('setprop vecentek.model 1')
|
||||||
|
if ok:
|
||||||
|
self.log(self.t('log_unlock_success'), "SUCCESS")
|
||||||
|
self.run_on_ui_thread(
|
||||||
|
messagebox.showinfo,
|
||||||
|
self.t('msg_success_title'),
|
||||||
|
self.t('msg_unlock_success')
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
msg = output or self.t('msg_error_title')
|
||||||
|
self.log(self.tf('log_unlock_failed', output=msg), "ERROR")
|
||||||
|
self.run_on_ui_thread(
|
||||||
|
messagebox.showerror,
|
||||||
|
self.t('msg_error_title'),
|
||||||
|
self.tf('msg_unlock_failed', output=msg)
|
||||||
|
)
|
||||||
|
|
||||||
|
threading.Thread(target=worker, daemon=True).start()
|
||||||
|
|
||||||
def start_device_monitor(self):
|
def start_device_monitor(self):
|
||||||
"""启动设备状态监控(每5秒检查一次)"""
|
"""启动设备状态监控(每5秒检查一次)"""
|
||||||
def monitor():
|
def monitor():
|
||||||
@@ -1189,7 +1397,7 @@ class ADKAPKGUI:
|
|||||||
if not ok:
|
if not ok:
|
||||||
return False, f"push失败: {err}"
|
return False, f"push失败: {err}"
|
||||||
|
|
||||||
ok, err = self.run_adb_shell(f'pm install -r -d -f {temp_apk_path}')
|
ok, err = self.run_adb_shell(f'pm install -d -f -r {temp_apk_path}')
|
||||||
self.run_adb_shell(f'rm -f {temp_apk_path}')
|
self.run_adb_shell(f'rm -f {temp_apk_path}')
|
||||||
if not ok:
|
if not ok:
|
||||||
return False, f"install失败: {err}"
|
return False, f"install失败: {err}"
|
||||||
@@ -1201,6 +1409,42 @@ class ADKAPKGUI:
|
|||||||
ok, _ = self.push_single_apk(apk_path, apk_name)
|
ok, _ = self.push_single_apk(apk_path, apk_name)
|
||||||
return ok
|
return ok
|
||||||
|
|
||||||
|
def cleanup_preinstalled_apps_for_language(self):
|
||||||
|
"""Disable and uninstall built-in apps before flashing language packages."""
|
||||||
|
packages = [
|
||||||
|
"com.wtcl.electronicdirections",
|
||||||
|
"com.tinnove.netease.music",
|
||||||
|
"com.changan.appmarket",
|
||||||
|
]
|
||||||
|
failed = []
|
||||||
|
|
||||||
|
for package in packages:
|
||||||
|
disable_ok, disable_output = self.run_adb_shell(f'pm disable-user {package}')
|
||||||
|
uninstall_ok, uninstall_output = self.run_adb_shell(f'pm uninstall -k --user 0 {package}')
|
||||||
|
|
||||||
|
if self.debug_mode:
|
||||||
|
if disable_ok:
|
||||||
|
self.log(f"禁用完成: {package}", "CMD")
|
||||||
|
else:
|
||||||
|
self.log(f"禁用失败: {package} {disable_output}", "CMD")
|
||||||
|
if uninstall_ok:
|
||||||
|
self.log(f"卸载完成: {package}", "CMD")
|
||||||
|
else:
|
||||||
|
self.log(f"卸载失败: {package} {uninstall_output}", "CMD")
|
||||||
|
|
||||||
|
if not disable_ok or not uninstall_ok:
|
||||||
|
failed.append(package)
|
||||||
|
|
||||||
|
if failed:
|
||||||
|
if self.debug_mode:
|
||||||
|
self.log("预置应用清理部分失败: " + ", ".join(failed), "WARNING")
|
||||||
|
else:
|
||||||
|
self.log("预置应用清理部分失败,继续刷入语言包", "WARNING")
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.log("预置应用清理完成", "SUCCESS")
|
||||||
|
return True
|
||||||
|
|
||||||
def push_all_apks(self):
|
def push_all_apks(self):
|
||||||
"""推送APK并安装 —— 逸动版仅处理 app 目录,使用 pm install"""
|
"""推送APK并安装 —— 逸动版仅处理 app 目录,使用 pm install"""
|
||||||
# 检查设备连接(仅 UI 层检查在主线程,其余工作进后台线程)
|
# 检查设备连接(仅 UI 层检查在主线程,其余工作进后台线程)
|
||||||
@@ -1208,19 +1452,23 @@ class ADKAPKGUI:
|
|||||||
return
|
return
|
||||||
|
|
||||||
if not self.vin:
|
if not self.vin:
|
||||||
messagebox.showwarning("警告", "请先刷新设备状态并获取VIN码")
|
messagebox.showwarning(self.t('msg_warn_title'), self.t('msg_need_vin'))
|
||||||
return
|
return
|
||||||
|
|
||||||
def do_push_all():
|
def do_push_all():
|
||||||
# 验证授权
|
# 验证授权
|
||||||
if not self.check_authorization(self.vin):
|
if not self.check_authorization(self.vin):
|
||||||
self.run_on_ui_thread(lambda: messagebox.showerror("授权失败", "设备未授权"))
|
self.run_on_ui_thread(
|
||||||
|
lambda: messagebox.showerror(self.t('msg_auth_failed_title'), self.t('msg_device_unauthorized'))
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# 获取解压密码
|
# 获取解压密码
|
||||||
if not self.extract_password:
|
if not self.extract_password:
|
||||||
if not self.fetch_package_password():
|
if not self.fetch_package_password():
|
||||||
self.run_on_ui_thread(lambda: messagebox.showerror("错误", "数据准备失败!"))
|
self.run_on_ui_thread(
|
||||||
|
lambda: messagebox.showerror(self.t('msg_error_title'), self.t('msg_data_prepare_failed'))
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# 解压
|
# 解压
|
||||||
@@ -1229,12 +1477,16 @@ class ADKAPKGUI:
|
|||||||
self.show_progress(True, is_push=False)
|
self.show_progress(True, is_push=False)
|
||||||
if not self.extract_package_silent():
|
if not self.extract_package_silent():
|
||||||
self.show_progress(False, is_push=False)
|
self.show_progress(False, is_push=False)
|
||||||
self.run_on_ui_thread(lambda: messagebox.showerror("错误", "资源准备失败!"))
|
self.run_on_ui_thread(
|
||||||
|
lambda: messagebox.showerror(self.t('msg_error_title'), self.t('msg_resource_prepare_failed'))
|
||||||
|
)
|
||||||
return
|
return
|
||||||
self.show_progress(False, is_push=False)
|
self.show_progress(False, is_push=False)
|
||||||
|
|
||||||
if not self.apps_dir or not self.apps_dir.exists():
|
if not self.apps_dir or not self.apps_dir.exists():
|
||||||
self.run_on_ui_thread(lambda: messagebox.showerror("错误", "资源目录未找到"))
|
self.run_on_ui_thread(
|
||||||
|
lambda: messagebox.showerror(self.t('msg_error_title'), self.t('msg_resource_dir_missing'))
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# 开始刷入
|
# 开始刷入
|
||||||
@@ -1242,6 +1494,7 @@ class ADKAPKGUI:
|
|||||||
self.log("开始刷入语言包...", "INFO")
|
self.log("开始刷入语言包...", "INFO")
|
||||||
self.run_adb_shell('mkdir -p /data/local/tmp')
|
self.run_adb_shell('mkdir -p /data/local/tmp')
|
||||||
self.run_adb_shell('setprop vecentek.model 1')
|
self.run_adb_shell('setprop vecentek.model 1')
|
||||||
|
self.cleanup_preinstalled_apps_for_language()
|
||||||
|
|
||||||
success_count = 0
|
success_count = 0
|
||||||
try:
|
try:
|
||||||
@@ -1255,11 +1508,15 @@ class ADKAPKGUI:
|
|||||||
apk_name = apk_path.stem
|
apk_name = apk_path.stem
|
||||||
ok, _ = self.push_single_apk(apk_path, apk_name)
|
ok, _ = self.push_single_apk(apk_path, apk_name)
|
||||||
if ok:
|
if ok:
|
||||||
self.log(f"安装成功: {apk_name}.apk", "SUCCESS")
|
if self.debug_mode:
|
||||||
|
self.log(f"安装成功: {apk_name}.apk", "SUCCESS")
|
||||||
success_count += 1
|
success_count += 1
|
||||||
else:
|
else:
|
||||||
self.log(f"安装失败: {apk_name}.apk", "ERROR")
|
if self.debug_mode:
|
||||||
self.update_progress(i, total, "正在刷入...", is_push=True)
|
self.log(f"安装失败: {apk_name}.apk", "ERROR")
|
||||||
|
else:
|
||||||
|
self.log(f"语言包刷入失败: {i}/{total}", "ERROR")
|
||||||
|
self.update_progress(i, total, "正在刷入", is_push=True)
|
||||||
|
|
||||||
self.update_progress(total, total, "刷入完成", is_push=True)
|
self.update_progress(total, total, "刷入完成", is_push=True)
|
||||||
|
|
||||||
@@ -1268,7 +1525,6 @@ class ADKAPKGUI:
|
|||||||
else:
|
else:
|
||||||
self.log("语言包刷入失败", "ERROR")
|
self.log("语言包刷入失败", "ERROR")
|
||||||
finally:
|
finally:
|
||||||
self.run_adb_shell('setprop vecentek.model 0')
|
|
||||||
self.show_progress(False, is_push=True)
|
self.show_progress(False, is_push=True)
|
||||||
|
|
||||||
threading.Thread(target=do_push_all, daemon=True).start()
|
threading.Thread(target=do_push_all, daemon=True).start()
|
||||||
@@ -1339,7 +1595,6 @@ class ADKAPKGUI:
|
|||||||
self.log(f"安装过程异常: {str(e)}", "ERROR")
|
self.log(f"安装过程异常: {str(e)}", "ERROR")
|
||||||
self.run_on_ui_thread(messagebox.showerror, "安装失败", f"安装过程异常:{str(e)}")
|
self.run_on_ui_thread(messagebox.showerror, "安装失败", f"安装过程异常:{str(e)}")
|
||||||
finally:
|
finally:
|
||||||
self.run_adb_shell('setprop vecentek.model 0')
|
|
||||||
self.show_progress(False, is_push=True)
|
self.show_progress(False, is_push=True)
|
||||||
|
|
||||||
threading.Thread(target=install, daemon=True).start()
|
threading.Thread(target=install, daemon=True).start()
|
||||||
@@ -1379,7 +1634,6 @@ class ADKAPKGUI:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log(f"安装过程异常: {str(e)}", "ERROR")
|
self.log(f"安装过程异常: {str(e)}", "ERROR")
|
||||||
finally:
|
finally:
|
||||||
self.run_adb_shell('setprop vecentek.model 0')
|
|
||||||
self.show_progress(False, is_push=True)
|
self.show_progress(False, is_push=True)
|
||||||
|
|
||||||
threading.Thread(target=install, daemon=True).start()
|
threading.Thread(target=install, daemon=True).start()
|
||||||
@@ -1398,7 +1652,7 @@ class ADKAPKGUI:
|
|||||||
|
|
||||||
# 创建弹窗
|
# 创建弹窗
|
||||||
popup = tk.Toplevel(self.root)
|
popup = tk.Toplevel(self.root)
|
||||||
popup.title("快捷语言设置")
|
popup.title(self.t('quick_lang_title'))
|
||||||
popup.geometry("520x320")
|
popup.geometry("520x320")
|
||||||
popup.configure(bg=self.colors['bg_dark'])
|
popup.configure(bg=self.colors['bg_dark'])
|
||||||
popup.resizable(False, False)
|
popup.resizable(False, False)
|
||||||
@@ -1412,29 +1666,21 @@ class ADKAPKGUI:
|
|||||||
popup.grab_set()
|
popup.grab_set()
|
||||||
|
|
||||||
# 标题
|
# 标题
|
||||||
header = tk.Label(popup, text="选择目标语言",
|
header = tk.Label(popup, text=self.t('quick_lang_header'),
|
||||||
font=('Microsoft YaHei', 13, 'bold'),
|
font=('Microsoft YaHei', 13, 'bold'),
|
||||||
fg=self.colors['accent'],
|
fg=self.colors['accent'],
|
||||||
bg=self.colors['bg_dark'])
|
bg=self.colors['bg_dark'])
|
||||||
header.pack(pady=(15, 10))
|
header.pack(pady=(15, 10))
|
||||||
|
|
||||||
hint = tk.Label(popup, text="点击按钮即可将系统语言切换为对应语言,重启后生效",
|
hint = tk.Label(popup, text=self.t('quick_lang_hint'),
|
||||||
font=('Microsoft YaHei', 9),
|
font=('Microsoft YaHei', 9),
|
||||||
fg=self.colors['text_secondary'],
|
fg=self.colors['text_secondary'],
|
||||||
bg=self.colors['bg_dark'])
|
bg=self.colors['bg_dark'])
|
||||||
hint.pack(pady=(0, 12))
|
hint.pack(pady=(0, 12))
|
||||||
|
|
||||||
# 语言列表:(显示名, locale_code)
|
# 语言列表:(显示名, locale_code)
|
||||||
languages = [
|
locale_codes = ["zh-CN", "en-US", "ru-RU", "fr-FR", "es-ES", "pt-BR", "it-IT", "ar-SA"]
|
||||||
("🇨🇳 中文", "zh-CN"),
|
languages = list(zip(self.t('quick_lang_names'), locale_codes))
|
||||||
("英 English", "en-US"),
|
|
||||||
("俄 Русский", "ru-RU"),
|
|
||||||
("法 Français", "fr-FR"),
|
|
||||||
("西 Español", "es-ES"),
|
|
||||||
("葡 Português", "pt-BR"),
|
|
||||||
("意 Italiano", "it-IT"),
|
|
||||||
("阿 العربية", "ar-SA"),
|
|
||||||
]
|
|
||||||
|
|
||||||
# 创建按钮容器
|
# 创建按钮容器
|
||||||
btn_frame = tk.Frame(popup, bg=self.colors['bg_dark'])
|
btn_frame = tk.Frame(popup, bg=self.colors['bg_dark'])
|
||||||
@@ -1468,7 +1714,7 @@ class ADKAPKGUI:
|
|||||||
sep = tk.Frame(popup, bg=self.colors['border'], height=1)
|
sep = tk.Frame(popup, bg=self.colors['border'], height=1)
|
||||||
sep.pack(fill=tk.X, padx=20, pady=(8, 6))
|
sep.pack(fill=tk.X, padx=20, pady=(8, 6))
|
||||||
|
|
||||||
sys_btn = tk.Button(popup, text="⚙️ 打开系统语言设置(手动选择)",
|
sys_btn = tk.Button(popup, text=self.t('quick_lang_system'),
|
||||||
command=lambda: self._open_sys_and_close(popup),
|
command=lambda: self._open_sys_and_close(popup),
|
||||||
font=('Microsoft YaHei', 9),
|
font=('Microsoft YaHei', 9),
|
||||||
fg=self.colors['text_secondary'],
|
fg=self.colors['text_secondary'],
|
||||||
@@ -1482,21 +1728,24 @@ class ADKAPKGUI:
|
|||||||
popup.destroy()
|
popup.destroy()
|
||||||
|
|
||||||
def do_set():
|
def do_set():
|
||||||
self.log(f"正在设置系统语言为: {language_name} ({locale_code})", "INFO")
|
|
||||||
success, output = self.run_adb_shell(
|
success, output = self.run_adb_shell(
|
||||||
f'settings put system system_locales {locale_code}'
|
f'settings put system system_locales {locale_code}'
|
||||||
)
|
)
|
||||||
|
|
||||||
if success:
|
if success:
|
||||||
self.log(f"✓ 语言已设置为 {language_name}", "SUCCESS")
|
self.log(self.tf('log_quick_lang_success', language=language_name), "SUCCESS")
|
||||||
self.run_on_ui_thread(
|
self.run_on_ui_thread(
|
||||||
messagebox.showinfo,
|
messagebox.showinfo,
|
||||||
"设置成功",
|
self.t('quick_lang_success_title'),
|
||||||
f"系统语言已设置为 {language_name}\n\n⚠️ 请重启设备使其生效。"
|
self.tf('quick_lang_success', language=language_name)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.log(f"✗ 语言设置失败: {output}", "ERROR")
|
self.log(self.tf('log_quick_lang_failed', output=output), "ERROR")
|
||||||
self.run_on_ui_thread(messagebox.showerror, "设置失败", f"语言设置失败!\n\n{output}")
|
self.run_on_ui_thread(
|
||||||
|
messagebox.showerror,
|
||||||
|
self.t('quick_lang_failed_title'),
|
||||||
|
self.tf('quick_lang_failed', output=output)
|
||||||
|
)
|
||||||
|
|
||||||
threading.Thread(target=do_set, daemon=True).start()
|
threading.Thread(target=do_set, daemon=True).start()
|
||||||
|
|
||||||
@@ -1570,21 +1819,21 @@ class ADKAPKGUI:
|
|||||||
|
|
||||||
def _on_vin_input_focus_in(self, event):
|
def _on_vin_input_focus_in(self, event):
|
||||||
"""输入框获得焦点时清除占位符"""
|
"""输入框获得焦点时清除占位符"""
|
||||||
if self.vin_input.get() == "请输入VIN":
|
if self.is_placeholder_vin(self.vin_input.get()):
|
||||||
self.vin_input.delete(0, tk.END)
|
self.vin_input.delete(0, tk.END)
|
||||||
self.vin_input.config(fg='#e0e0e0')
|
self.vin_input.config(fg='#e0e0e0')
|
||||||
|
|
||||||
def _on_vin_input_focus_out(self, event):
|
def _on_vin_input_focus_out(self, event):
|
||||||
"""输入框失去焦点时恢复占位符"""
|
"""输入框失去焦点时恢复占位符"""
|
||||||
if not self.vin_input.get():
|
if not self.vin_input.get():
|
||||||
self.vin_input.insert(0, "请输入VIN")
|
self.vin_input.insert(0, self.t('vin_placeholder'))
|
||||||
self.vin_input.config(fg='#636e72')
|
self.vin_input.config(fg='#636e72')
|
||||||
|
|
||||||
def query_password_by_vin(self):
|
def query_password_by_vin(self):
|
||||||
"""通过VIN查询密码"""
|
"""通过VIN查询密码"""
|
||||||
vin = self.vin_input.get().strip()
|
vin = self.vin_input.get().strip()
|
||||||
if not vin:
|
if not vin or self.is_placeholder_vin(vin):
|
||||||
messagebox.showwarning("提示", "请输入VIN码")
|
messagebox.showwarning(self.t('msg_warn_title'), self.t('msg_input_vin'))
|
||||||
return
|
return
|
||||||
|
|
||||||
def do_query():
|
def do_query():
|
||||||
@@ -1696,7 +1945,6 @@ class ADKAPKGUI:
|
|||||||
self.log(f"安装过程异常: {str(e)}", "ERROR")
|
self.log(f"安装过程异常: {str(e)}", "ERROR")
|
||||||
self.run_on_ui_thread(messagebox.showerror, "安装失败", f"安装过程异常:{str(e)}")
|
self.run_on_ui_thread(messagebox.showerror, "安装失败", f"安装过程异常:{str(e)}")
|
||||||
finally:
|
finally:
|
||||||
self.run_adb_shell('setprop vecentek.model 0')
|
|
||||||
self.show_progress(False, is_push=True)
|
self.show_progress(False, is_push=True)
|
||||||
|
|
||||||
threading.Thread(target=install, daemon=True).start()
|
threading.Thread(target=install, daemon=True).start()
|
||||||
@@ -1708,20 +1956,48 @@ class ADKAPKGUI:
|
|||||||
def modify_hosts(self):
|
def modify_hosts(self):
|
||||||
"""修改hosts文件,添加云端认证DNS映射"""
|
"""修改hosts文件,添加云端认证DNS映射"""
|
||||||
hosts_path = r"C:\Windows\System32\drivers\etc\hosts"
|
hosts_path = r"C:\Windows\System32\drivers\etc\hosts"
|
||||||
entry = "103.236.55.140 spm.auto-pai.com"
|
host_ip = "103.236.55.140"
|
||||||
|
host_name = "spm.auto-pai.com"
|
||||||
|
entry = f"{host_ip} {host_name}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(hosts_path, 'r', encoding='utf-8') as f:
|
try:
|
||||||
content = f.read()
|
with open(hosts_path, 'r', encoding='utf-8') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
with open(hosts_path, 'r', encoding='gbk', errors='replace') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
if entry in content:
|
new_lines = []
|
||||||
# self.log("hosts条目已存在,无需修改", "INFO")
|
changed = False
|
||||||
|
has_exact = False
|
||||||
|
for line in lines:
|
||||||
|
stripped = line.strip()
|
||||||
|
if not stripped or stripped.startswith('#'):
|
||||||
|
new_lines.append(line)
|
||||||
|
continue
|
||||||
|
|
||||||
|
body, _, _ = line.partition('#')
|
||||||
|
parts = body.split()
|
||||||
|
if len(parts) >= 2 and host_name.lower() in [p.lower() for p in parts[1:]]:
|
||||||
|
if parts[0] == host_ip and len(parts) == 2:
|
||||||
|
has_exact = True
|
||||||
|
new_lines.append(line)
|
||||||
|
else:
|
||||||
|
changed = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
new_lines.append(line)
|
||||||
|
|
||||||
|
if has_exact and not changed:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
with open(hosts_path, 'a', encoding='utf-8') as f:
|
if not new_lines or (new_lines[-1] and not new_lines[-1].endswith(('\n', '\r'))):
|
||||||
f.write(f"\n{entry}\n")
|
new_lines.append('\n')
|
||||||
|
new_lines.append(f"{entry}\n")
|
||||||
|
|
||||||
# self.log(f"已添加hosts条目: {entry}", "SUCCESS")
|
with open(hosts_path, 'w', encoding='utf-8', newline='') as f:
|
||||||
|
f.writelines(new_lines)
|
||||||
return True
|
return True
|
||||||
except PermissionError:
|
except PermissionError:
|
||||||
self.log("需要管理员权限,请以管理员身份运行", "WARNING")
|
self.log("需要管理员权限,请以管理员身份运行", "WARNING")
|
||||||
@@ -1873,15 +2149,15 @@ class ADKAPKGUI:
|
|||||||
def _refresh_hotspot_display_impl(self, ssid, password, status):
|
def _refresh_hotspot_display_impl(self, ssid, password, status):
|
||||||
"""刷新热点显示的UI实现"""
|
"""刷新热点显示的UI实现"""
|
||||||
if ssid:
|
if ssid:
|
||||||
self.hotspot_ssid_label.config(text=f"名称: {ssid}")
|
self.hotspot_ssid_label.config(text=self.tf('hotspot_name_value', ssid=ssid))
|
||||||
else:
|
else:
|
||||||
self.hotspot_ssid_label.config(text="名称: 未配置")
|
self.hotspot_ssid_label.config(text=self.t('hotspot_name_empty'))
|
||||||
if password:
|
if password:
|
||||||
self.hotspot_pwd_label.config(text=f"密码: {password}")
|
self.hotspot_pwd_label.config(text=self.tf('hotspot_pwd_value', password=password))
|
||||||
else:
|
else:
|
||||||
self.hotspot_pwd_label.config(text="密码: changan2024")
|
self.hotspot_pwd_label.config(text=self.t('hotspot_pwd_default'))
|
||||||
self.hotspot_status_label.config(
|
self.hotspot_status_label.config(
|
||||||
text=f"状态: {status}",
|
text=self.tf('hotspot_status_value', status=status),
|
||||||
fg=self.colors['success'] if '已启动' in status else self.colors['warning']
|
fg=self.colors['success'] if '已启动' in status else self.colors['warning']
|
||||||
)
|
)
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 135 KiB |
@@ -0,0 +1 @@
|
|||||||
|
Q1DLEsRw96XfzeCXtWG5PlTP10PpL8HbHQvr4YUqDzhUCzSboW924hT5xhjKu78S5ZZGGhxqjyPDh3GHf9L4cqCwpeg2tfKlsl/b7bmUWVVY5KfpwhRQuJlhQn7VJkycYe+TNb89n56mLYIV7DhNLhgGENXdMjHWPdUXQhNanpata8n920J2GGe71dONPRh/Ljeab2sv8SJprCaUYaSwxtkXXz5OS/9L5CRGJ13YDuu+aEIQp5R4i25mqqL4KD+zSkjkyHfQzzemIolfueVya27xZYULSBnQCYL/39GjdTM5btZ7OL9FCjzXROfQyyZ21HZIZgKo/p1wuRH7j4Ne0g==
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001 >nul
|
||||||
|
cd /d "%~dp0"
|
||||||
|
set "ROOT=%~dp0.."
|
||||||
|
set "TOOLS=%ROOT%\tools"
|
||||||
|
set "NAME=CS55-Q05-Installer"
|
||||||
|
set "SRC=CS55-Q05_Installer.py"
|
||||||
|
set "ICON=cs55-q05.ico"
|
||||||
|
title %NAME% - Cython Build
|
||||||
|
|
||||||
|
echo ============================================================
|
||||||
|
echo %NAME% - Cython Build
|
||||||
|
echo ============================================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
where python >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Python not found
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
for /f "delims=" %%i in ('where python') do set "PY=%%i"
|
||||||
|
echo Python: %PY%
|
||||||
|
|
||||||
|
if not exist "%SRC%" (
|
||||||
|
echo [ERROR] Source not found: %SRC%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%ICON%" (
|
||||||
|
echo [ERROR] Icon not found: %ICON%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\adb.exe" (
|
||||||
|
echo [ERROR] adb.exe not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\7za.exe" (
|
||||||
|
echo [ERROR] 7za.exe not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [1/6] Installing deps...
|
||||||
|
%PY% -m pip install pyinstaller cython pyzipper -q
|
||||||
|
if errorlevel 1 (
|
||||||
|
%PY% -m pip install pyinstaller cython pyzipper -q -i https://pypi.tuna.tsinghua.edu.cn/simple
|
||||||
|
)
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Dependency install failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [2/6] Clean...
|
||||||
|
if exist "dist_cy" rmdir /s /q dist_cy 2>nul
|
||||||
|
if exist "build" rmdir /s /q build 2>nul
|
||||||
|
if exist "dist" rmdir /s /q dist 2>nul
|
||||||
|
if exist "%NAME%.spec" del /q "%NAME%.spec" 2>nul
|
||||||
|
|
||||||
|
echo [3/6] Cython compile...
|
||||||
|
mkdir dist_cy 2>nul
|
||||||
|
copy "%SRC%" dist_cy\_core.py >nul
|
||||||
|
|
||||||
|
%PY% -c "open('dist_cy/setup_cython.py','w',encoding='utf-8').write('from setuptools import setup\nfrom Cython.Build import cythonize\nsetup(ext_modules=cythonize(\"_core.py\", compiler_directives={\"language_level\":\"3\"}))\n')"
|
||||||
|
|
||||||
|
cd dist_cy
|
||||||
|
%PY% setup_cython.py build_ext --inplace
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Cython failed. Build stopped.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
set "PYD="
|
||||||
|
for %%f in (_core*.pyd) do set "PYD=%%f"
|
||||||
|
if "%PYD%"=="" (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] No Cython PYD generated. Build stopped.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo PYD: %PYD%
|
||||||
|
copy "%PYD%" _core.pyd >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Failed to copy Cython PYD
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
%PY% -c "open('launcher.py','w',encoding='utf-8').write('# -*- coding: utf-8 -*-\nfrom _core import main\nmain()\n')"
|
||||||
|
|
||||||
|
echo [4/6] Copy resources...
|
||||||
|
copy "%TOOLS%\adb.exe" . >nul
|
||||||
|
copy "%TOOLS%\AdbWinApi.dll" . >nul
|
||||||
|
copy "%TOOLS%\AdbWinUsbApi.dll" . >nul
|
||||||
|
copy "%TOOLS%\7za.exe" . >nul
|
||||||
|
copy "..\%ICON%" . >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Copy resources failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [5/6] PyInstaller...
|
||||||
|
%PY% -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ICON%" --add-data "adb.exe;." --add-data "AdbWinApi.dll;." --add-data "AdbWinUsbApi.dll;." --add-data "7za.exe;." --add-data "%ICON%;." --add-binary "_core.pyd;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin launcher.py
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] PyInstaller failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [6/6] Cleanup...
|
||||||
|
del /q _core.py _core.c _core.pyd %PYD% launcher.py setup_cython.py "%ICON%" 2>nul
|
||||||
|
rmdir /s /q build 2>nul
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Done.
|
||||||
|
if exist "dist_cy\dist\%NAME%.exe" (
|
||||||
|
echo Output: dist_cy\dist\%NAME%.exe
|
||||||
|
) else (
|
||||||
|
echo [ERROR] Output exe was not generated
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
pause
|
||||||
|
After Width: | Height: | Size: 135 KiB |
@@ -0,0 +1,133 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001 >nul
|
||||||
|
cd /d "%~dp0"
|
||||||
|
set "ROOT=%~dp0.."
|
||||||
|
set "TOOLS=%ROOT%\tools"
|
||||||
|
set "NAME=CS75Pro-Installer"
|
||||||
|
set "SRC=CS75Pro_Installer.py"
|
||||||
|
set "ICON=cs75pro.ico"
|
||||||
|
title %NAME% - Cython Build
|
||||||
|
|
||||||
|
echo ============================================================
|
||||||
|
echo %NAME% - Cython Build
|
||||||
|
echo ============================================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
where python >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Python not found
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
for /f "delims=" %%i in ('where python') do set "PY=%%i"
|
||||||
|
echo Python: %PY%
|
||||||
|
|
||||||
|
if not exist "%SRC%" (
|
||||||
|
echo [ERROR] Source not found: %SRC%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%ICON%" (
|
||||||
|
echo [ERROR] Icon not found: %ICON%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\adb.exe" (
|
||||||
|
echo [ERROR] adb.exe not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\7za.exe" (
|
||||||
|
echo [ERROR] 7za.exe not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [1/6] Installing deps...
|
||||||
|
%PY% -m pip install pyinstaller cython pyzipper -q
|
||||||
|
if errorlevel 1 (
|
||||||
|
%PY% -m pip install pyinstaller cython pyzipper -q -i https://pypi.tuna.tsinghua.edu.cn/simple
|
||||||
|
)
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Dependency install failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [2/6] Clean...
|
||||||
|
if exist "dist_cy" rmdir /s /q dist_cy 2>nul
|
||||||
|
if exist "build" rmdir /s /q build 2>nul
|
||||||
|
if exist "dist" rmdir /s /q dist 2>nul
|
||||||
|
if exist "%NAME%.spec" del /q "%NAME%.spec" 2>nul
|
||||||
|
|
||||||
|
echo [3/6] Cython compile...
|
||||||
|
mkdir dist_cy 2>nul
|
||||||
|
copy "%SRC%" dist_cy\_core.py >nul
|
||||||
|
|
||||||
|
%PY% -c "open('dist_cy/setup_cython.py','w',encoding='utf-8').write('from setuptools import setup\nfrom Cython.Build import cythonize\nsetup(ext_modules=cythonize(\"_core.py\", compiler_directives={\"language_level\":\"3\"}))\n')"
|
||||||
|
|
||||||
|
cd dist_cy
|
||||||
|
%PY% setup_cython.py build_ext --inplace
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Cython failed. Build stopped.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
set "PYD="
|
||||||
|
for %%f in (_core*.pyd) do set "PYD=%%f"
|
||||||
|
if "%PYD%"=="" (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] No Cython PYD generated. Build stopped.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo PYD: %PYD%
|
||||||
|
copy "%PYD%" _core.pyd >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Failed to copy Cython PYD
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
%PY% -c "open('launcher.py','w',encoding='utf-8').write('# -*- coding: utf-8 -*-\nfrom _core import main\nmain()\n')"
|
||||||
|
|
||||||
|
echo [4/6] Copy resources...
|
||||||
|
copy "%TOOLS%\adb.exe" . >nul
|
||||||
|
copy "%TOOLS%\AdbWinApi.dll" . >nul
|
||||||
|
copy "%TOOLS%\AdbWinUsbApi.dll" . >nul
|
||||||
|
copy "%TOOLS%\7za.exe" . >nul
|
||||||
|
copy "..\%ICON%" . >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Copy resources failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [5/6] PyInstaller...
|
||||||
|
%PY% -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ICON%" --add-data "adb.exe;." --add-data "AdbWinApi.dll;." --add-data "AdbWinUsbApi.dll;." --add-data "7za.exe;." --add-data "%ICON%;." --add-binary "_core.pyd;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin launcher.py
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] PyInstaller failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [6/6] Cleanup...
|
||||||
|
del /q _core.py _core.c _core.pyd %PYD% launcher.py setup_cython.py "%ICON%" 2>nul
|
||||||
|
rmdir /s /q build 2>nul
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Done.
|
||||||
|
if exist "dist_cy\dist\%NAME%.exe" (
|
||||||
|
echo Output: dist_cy\dist\%NAME%.exe
|
||||||
|
) else (
|
||||||
|
echo [ERROR] Output exe was not generated
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
pause
|
||||||
|
After Width: | Height: | Size: 48 KiB |
@@ -0,0 +1,153 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001 >nul
|
||||||
|
cd /d "%~dp0"
|
||||||
|
set "ROOT=%~dp0.."
|
||||||
|
set "TOOLS=%ROOT%\tools"
|
||||||
|
set "NAME=Mazda-EZ6-Installer"
|
||||||
|
set "SRC=Mazda-EZ6_Installer.py"
|
||||||
|
set "ICON=%~dp0app.ico"
|
||||||
|
title %NAME% - Cython Build
|
||||||
|
|
||||||
|
echo ============================================================
|
||||||
|
echo %NAME% - Cython Build
|
||||||
|
echo ============================================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
where python >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Python not found
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
for /f "delims=" %%i in ('where python') do set "PY=%%i"
|
||||||
|
echo Python: %PY%
|
||||||
|
|
||||||
|
if not exist "%SRC%" (
|
||||||
|
echo [ERROR] Source not found: %SRC%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%ICON%" (
|
||||||
|
echo [ERROR] Icon not found: %ICON%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\adb.exe" (
|
||||||
|
echo [ERROR] adb.exe not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\AdbWinApi.dll" (
|
||||||
|
echo [ERROR] AdbWinApi.dll not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\AdbWinUsbApi.dll" (
|
||||||
|
echo [ERROR] AdbWinUsbApi.dll not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [1/6] Installing deps...
|
||||||
|
"%PY%" -m pip install pyinstaller cython -q
|
||||||
|
if errorlevel 1 (
|
||||||
|
"%PY%" -m pip install pyinstaller cython -q -i https://pypi.tuna.tsinghua.edu.cn/simple
|
||||||
|
)
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Dependency install failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [2/6] Clean...
|
||||||
|
if exist "dist_cy" rmdir /s /q dist_cy 2>nul
|
||||||
|
if exist "build" rmdir /s /q build 2>nul
|
||||||
|
if exist "dist" rmdir /s /q dist 2>nul
|
||||||
|
if exist "%NAME%.spec" del /q "%NAME%.spec" 2>nul
|
||||||
|
|
||||||
|
echo [3/6] Cython compile...
|
||||||
|
mkdir dist_cy 2>nul
|
||||||
|
copy "%SRC%" "dist_cy\_core.py" >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Copy source failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
"%PY%" -c "open('dist_cy/setup_cython.py','w',encoding='utf-8').write('from setuptools import setup\nfrom Cython.Build import cythonize\nsetup(ext_modules=cythonize(\"_core.py\", compiler_directives={\"language_level\":\"3\"}))\n')"
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Failed to create Cython setup script
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
cd dist_cy
|
||||||
|
"%PY%" setup_cython.py build_ext --inplace
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Cython failed. Build stopped.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
set "PYD="
|
||||||
|
for %%f in (_core*.pyd) do set "PYD=%%f"
|
||||||
|
if "%PYD%"=="" (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] No Cython PYD generated. Build stopped.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo PYD: %PYD%
|
||||||
|
copy "%PYD%" "_core.pyd" >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Failed to copy Cython PYD
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
"%PY%" -c "open('launcher.py','w',encoding='utf-8').write('# -*- coding: utf-8 -*-\nfrom _core import main\nmain()\n')"
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Failed to create launcher.py
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [4/6] Copy resources...
|
||||||
|
copy "%TOOLS%\adb.exe" . >nul
|
||||||
|
copy "%TOOLS%\AdbWinApi.dll" . >nul
|
||||||
|
copy "%TOOLS%\AdbWinUsbApi.dll" . >nul
|
||||||
|
copy "%ICON%" . >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Copy resources failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [5/6] PyInstaller...
|
||||||
|
"%PY%" -m PyInstaller --onefile --windowed --name="%NAME%" --icon="app.ico" --add-data "app.ico;." --add-data "adb.exe;." --add-data "AdbWinApi.dll;." --add-data "AdbWinUsbApi.dll;." --add-binary "_core.pyd;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.filedialog --hidden-import=tkinter.messagebox --hidden-import=tkinter.scrolledtext --hidden-import=tkinter.ttk --hidden-import=json --hidden-import=urllib --hidden-import=urllib.error --hidden-import=urllib.parse --hidden-import=urllib.request --collect-all tkinter --uac-admin launcher.py
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] PyInstaller failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [6/6] Cleanup...
|
||||||
|
del /q _core.py _core.c _core.pyd %PYD% launcher.py setup_cython.py adb.exe AdbWinApi.dll AdbWinUsbApi.dll app.ico 2>nul
|
||||||
|
rmdir /s /q build 2>nul
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Done.
|
||||||
|
if exist "dist_cy\dist\%NAME%.exe" (
|
||||||
|
echo Output: dist_cy\dist\%NAME%.exe
|
||||||
|
) else (
|
||||||
|
echo [ERROR] Output exe was not generated
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
pause
|
||||||
|
After Width: | Height: | Size: 48 KiB |
@@ -0,0 +1,209 @@
|
|||||||
|
param(
|
||||||
|
[string]$AppsDir = "",
|
||||||
|
[string]$AdbPath = "",
|
||||||
|
[string]$ShellPassword = "adb36987"
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
if (-not $AppsDir) {
|
||||||
|
$AppsDir = Join-Path $ScriptDir "apps"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Find-AdbPath {
|
||||||
|
param(
|
||||||
|
[string]$BaseDir
|
||||||
|
)
|
||||||
|
|
||||||
|
$candidates = @(
|
||||||
|
(Join-Path $BaseDir "adb.exe"),
|
||||||
|
(Join-Path $BaseDir "tools\adb.exe"),
|
||||||
|
(Join-Path $BaseDir "..\adb.exe"),
|
||||||
|
(Join-Path $BaseDir "..\tools\adb.exe"),
|
||||||
|
(Join-Path $BaseDir "..\..\tools\adb.exe"),
|
||||||
|
"D:\Code\language-installer\tools\adb.exe",
|
||||||
|
"D:\apk_tools\platform-tools\adb.exe"
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach ($candidate in $candidates) {
|
||||||
|
try {
|
||||||
|
$resolved = Resolve-Path -LiteralPath $candidate -ErrorAction Stop
|
||||||
|
if ($resolved) {
|
||||||
|
return $resolved.Path
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
$pathAdb = Get-Command adb.exe -ErrorAction SilentlyContinue
|
||||||
|
if ($pathAdb) {
|
||||||
|
return $pathAdb.Source
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $AdbPath) {
|
||||||
|
$AdbPath = Find-AdbPath -BaseDir $ScriptDir
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $AdbPath)) {
|
||||||
|
throw "adb.exe not found. Put adb.exe next to this script or run with -AdbPath `"D:\path\adb.exe`""
|
||||||
|
}
|
||||||
|
if (-not (Test-Path -LiteralPath $AppsDir)) {
|
||||||
|
throw "apps directory not found: $AppsDir"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Quote-ProcessArgument {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Value
|
||||||
|
)
|
||||||
|
|
||||||
|
if ($Value -notmatch '[\s"]') {
|
||||||
|
return $Value
|
||||||
|
}
|
||||||
|
|
||||||
|
return '"' + $Value.Replace('"', '\"') + '"'
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-Adb {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string[]]$Arguments
|
||||||
|
)
|
||||||
|
|
||||||
|
$psi = New-Object System.Diagnostics.ProcessStartInfo
|
||||||
|
$psi.FileName = $AdbPath
|
||||||
|
$psi.Arguments = (($Arguments | ForEach-Object { Quote-ProcessArgument $_ }) -join " ")
|
||||||
|
$psi.RedirectStandardOutput = $true
|
||||||
|
$psi.RedirectStandardError = $true
|
||||||
|
$psi.UseShellExecute = $false
|
||||||
|
$psi.CreateNoWindow = $true
|
||||||
|
|
||||||
|
$proc = [System.Diagnostics.Process]::Start($psi)
|
||||||
|
$stdout = $proc.StandardOutput.ReadToEnd()
|
||||||
|
$stderr = $proc.StandardError.ReadToEnd()
|
||||||
|
$proc.WaitForExit()
|
||||||
|
|
||||||
|
return [pscustomobject]@{
|
||||||
|
Code = $proc.ExitCode
|
||||||
|
Out = ($stdout + $stderr).Trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-AdbShellWithPassword {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Command,
|
||||||
|
[int]$TimeoutMs = 60000
|
||||||
|
)
|
||||||
|
|
||||||
|
$psi = New-Object System.Diagnostics.ProcessStartInfo
|
||||||
|
$psi.FileName = $AdbPath
|
||||||
|
$psi.Arguments = "-d shell"
|
||||||
|
$psi.RedirectStandardInput = $true
|
||||||
|
$psi.RedirectStandardOutput = $true
|
||||||
|
$psi.RedirectStandardError = $true
|
||||||
|
$psi.UseShellExecute = $false
|
||||||
|
$psi.CreateNoWindow = $true
|
||||||
|
|
||||||
|
$proc = New-Object System.Diagnostics.Process
|
||||||
|
$proc.StartInfo = $psi
|
||||||
|
[void]$proc.Start()
|
||||||
|
|
||||||
|
Start-Sleep -Milliseconds 500
|
||||||
|
$proc.StandardInput.WriteLine($ShellPassword)
|
||||||
|
Start-Sleep -Milliseconds 300
|
||||||
|
$proc.StandardInput.WriteLine($Command)
|
||||||
|
$proc.StandardInput.WriteLine("exit")
|
||||||
|
$proc.StandardInput.Close()
|
||||||
|
|
||||||
|
if (-not $proc.WaitForExit($TimeoutMs)) {
|
||||||
|
try { $proc.Kill() } catch {}
|
||||||
|
return [pscustomobject]@{
|
||||||
|
Code = -1
|
||||||
|
Out = "shell command timeout: $Command"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$stdout = $proc.StandardOutput.ReadToEnd()
|
||||||
|
$stderr = $proc.StandardError.ReadToEnd()
|
||||||
|
return [pscustomobject]@{
|
||||||
|
Code = $proc.ExitCode
|
||||||
|
Out = ($stdout + $stderr).Trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "ADB: $AdbPath"
|
||||||
|
Write-Host "Apps: $AppsDir"
|
||||||
|
|
||||||
|
$devices = Invoke-Adb -Arguments @("-d", "devices")
|
||||||
|
if ($devices.Code -ne 0 -or $devices.Out -notmatch "`tdevice") {
|
||||||
|
Write-Host "[ERROR] No adb device connected." -ForegroundColor Red
|
||||||
|
if ($devices.Out) { Write-Host $devices.Out }
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$root = Invoke-Adb -Arguments @("-d", "root")
|
||||||
|
if ($root.Out) {
|
||||||
|
Write-Host $root.Out
|
||||||
|
}
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
|
||||||
|
$apks = Get-ChildItem -LiteralPath $AppsDir -Filter "*.apk" -File | Sort-Object Name
|
||||||
|
if (-not $apks) {
|
||||||
|
Write-Host "[ERROR] No APK files found in apps directory." -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$total = $apks.Count
|
||||||
|
$index = 0
|
||||||
|
$success = 0
|
||||||
|
|
||||||
|
foreach ($apk in $apks) {
|
||||||
|
$index++
|
||||||
|
$apkName = [System.IO.Path]::GetFileNameWithoutExtension($apk.Name)
|
||||||
|
$targetDir = "/system/app/$apkName"
|
||||||
|
$targetApk = "$targetDir/$($apk.Name)"
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[$index/$total] $($apk.Name)" -ForegroundColor Cyan
|
||||||
|
|
||||||
|
$mkdir = Invoke-AdbShellWithPassword -Command "mkdir -p '$targetDir'"
|
||||||
|
if ($mkdir.Code -ne 0) {
|
||||||
|
Write-Host "[ERROR] mkdir failed: $targetDir" -ForegroundColor Red
|
||||||
|
if ($mkdir.Out) { Write-Host $mkdir.Out }
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$push = Invoke-Adb -Arguments @("-d", "push", $apk.FullName, $targetApk)
|
||||||
|
if ($push.Code -ne 0) {
|
||||||
|
Write-Host "[ERROR] push failed: $($apk.Name)" -ForegroundColor Red
|
||||||
|
if ($push.Out) { Write-Host $push.Out }
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
if ($push.Out) {
|
||||||
|
Write-Host $push.Out
|
||||||
|
}
|
||||||
|
|
||||||
|
$chmodDir = Invoke-AdbShellWithPassword -Command "chmod 755 '$targetDir'"
|
||||||
|
if ($chmodDir.Code -ne 0) {
|
||||||
|
Write-Host "[ERROR] chmod 755 failed: $targetDir" -ForegroundColor Red
|
||||||
|
if ($chmodDir.Out) { Write-Host $chmodDir.Out }
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$chmodApk = Invoke-AdbShellWithPassword -Command "chmod 644 '$targetApk'"
|
||||||
|
if ($chmodApk.Code -ne 0) {
|
||||||
|
Write-Host "[ERROR] chmod 644 failed: $targetApk" -ForegroundColor Red
|
||||||
|
if ($chmodApk.Out) { Write-Host $chmodApk.Out }
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$success++
|
||||||
|
Write-Host "[OK] $($apk.Name)" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[DONE] Pushed $success/$total APK files." -ForegroundColor Green
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
@echo off
|
|
||||||
chcp 65001 >nul
|
|
||||||
cd /d "%~dp0"
|
|
||||||
set "ROOT=%~dp0.."
|
|
||||||
set "TOOLS=%ROOT%\tools"
|
|
||||||
set "NAME=Mazda-EZ60-Language-Installer"
|
|
||||||
set "SRC=Mazda-EZ60.py"
|
|
||||||
title %NAME% - Build
|
|
||||||
|
|
||||||
echo ============================================================
|
|
||||||
echo %NAME% - Cython Build
|
|
||||||
echo ============================================================
|
|
||||||
echo.
|
|
||||||
|
|
||||||
where python >nul 2>&1
|
|
||||||
if errorlevel 1 (
|
|
||||||
echo [ERROR] Python not found
|
|
||||||
pause
|
|
||||||
exit /b
|
|
||||||
)
|
|
||||||
for /f "delims=" %%i in ('where python') do set "PY=%%i"
|
|
||||||
echo Python: %PY%
|
|
||||||
|
|
||||||
echo [1/6] Installing deps...
|
|
||||||
"%PY%" -m pip install pyinstaller cython pyzipper -q
|
|
||||||
if errorlevel 1 (
|
|
||||||
"%PY%" -m pip install pyinstaller cython pyzipper -q -i https://pypi.tuna.tsinghua.edu.cn/simple
|
|
||||||
)
|
|
||||||
|
|
||||||
echo [2/6] Clean...
|
|
||||||
if exist "dist_cy" rmdir /s /q dist_cy 2>nul
|
|
||||||
if exist "build" rmdir /s /q build 2>nul
|
|
||||||
if exist "dist" rmdir /s /q dist 2>nul
|
|
||||||
|
|
||||||
echo [3/6] Cython compile...
|
|
||||||
mkdir dist_cy 2>nul
|
|
||||||
copy "%SRC%" dist_cy\_core.py >nul
|
|
||||||
if errorlevel 1 (
|
|
||||||
echo [WARN] Copy source failed, fallback
|
|
||||||
goto :NORMAL
|
|
||||||
)
|
|
||||||
|
|
||||||
"%PY%" -c "open('dist_cy/setup_cython.py','w').write('from setuptools import setup\nfrom Cython.Build import cythonize\nsetup(ext_modules=cythonize(\"_core.py\", compiler_directives={\"language_level\":\"3\"}))\n')"
|
|
||||||
|
|
||||||
cd dist_cy
|
|
||||||
"%PY%" setup_cython.py build_ext --inplace
|
|
||||||
if errorlevel 1 (
|
|
||||||
cd ..
|
|
||||||
echo [WARN] Cython failed, fallback
|
|
||||||
goto :NORMAL
|
|
||||||
)
|
|
||||||
|
|
||||||
for %%f in (_core*.pyd) do set PYD=%%f
|
|
||||||
if "%PYD%"=="" (
|
|
||||||
cd ..
|
|
||||||
echo [WARN] No pyd, fallback
|
|
||||||
goto :NORMAL
|
|
||||||
)
|
|
||||||
echo PYD: %PYD%
|
|
||||||
copy "%PYD%" _core.pyd >nul
|
|
||||||
|
|
||||||
"%PY%" -c "open('launcher.py','w').write('# -*- coding: utf-8 -*-\nfrom _core import main\nmain()\n')"
|
|
||||||
|
|
||||||
echo [4/6] Copy resources...
|
|
||||||
copy "%TOOLS%\adb.exe" . >nul
|
|
||||||
copy "%TOOLS%\AdbWinApi.dll" . >nul
|
|
||||||
copy "%TOOLS%\AdbWinUsbApi.dll" . >nul
|
|
||||||
copy "%TOOLS%\7za.exe" . >nul
|
|
||||||
if exist "%ROOT%\app.ico" copy "%ROOT%\app.ico" . >nul
|
|
||||||
|
|
||||||
echo [5/6] PyInstaller...
|
|
||||||
"%PY%" -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ROOT%\app.ico" --add-data "%TOOLS%\adb.exe;." --add-data "%TOOLS%\AdbWinApi.dll;." --add-data "%TOOLS%\AdbWinUsbApi.dll;." --add-data "%TOOLS%\7za.exe;." --add-binary "_core.pyd;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --collect-all tkinter --uac-admin launcher.py
|
|
||||||
if errorlevel 1 (
|
|
||||||
cd ..
|
|
||||||
echo [ERROR] PyInstaller failed
|
|
||||||
pause
|
|
||||||
exit /b
|
|
||||||
)
|
|
||||||
|
|
||||||
echo [6/6] Cleanup...
|
|
||||||
del /q _core.py _core.c _core.pyd %PYD% launcher.py setup_cython.py 2>nul
|
|
||||||
rmdir /s /q build 2>nul
|
|
||||||
cd ..
|
|
||||||
goto :DONE
|
|
||||||
|
|
||||||
:NORMAL
|
|
||||||
echo [INFO] Normal PyInstaller...
|
|
||||||
"%PY%" -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ROOT%\app.ico" --add-data "%TOOLS%\adb.exe;." --add-data "%TOOLS%\AdbWinApi.dll;." --add-data "%TOOLS%\AdbWinUsbApi.dll;." --add-data "%TOOLS%\7za.exe;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --collect-all tkinter --uac-admin "%SRC%"
|
|
||||||
|
|
||||||
:DONE
|
|
||||||
echo.
|
|
||||||
echo Done.
|
|
||||||
if exist "dist_cy\dist\%NAME%.exe" (
|
|
||||||
echo Output: dist_cy\dist\%NAME%.exe
|
|
||||||
) else if exist "dist\%NAME%.exe" (
|
|
||||||
echo Output: dist\%NAME%.exe
|
|
||||||
) else (
|
|
||||||
echo Check dist folder
|
|
||||||
)
|
|
||||||
pause
|
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001 >nul
|
||||||
|
cd /d "%~dp0"
|
||||||
|
set "ROOT=%~dp0.."
|
||||||
|
set "TOOLS=%ROOT%\tools"
|
||||||
|
set "NAME=Mazda_EZ60-Language-Install_v1.0"
|
||||||
|
set "SRC=Mazda_EZ60-Language-Install_v1.0.py"
|
||||||
|
set "ICON=%~dp0app.ico"
|
||||||
|
title %NAME% - Cython Build
|
||||||
|
|
||||||
|
echo ============================================================
|
||||||
|
echo %NAME% - Cython Build
|
||||||
|
echo ============================================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
where python >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Python not found
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
for /f "delims=" %%i in ('where python') do set "PY=%%i"
|
||||||
|
echo Python: %PY%
|
||||||
|
|
||||||
|
if not exist "%SRC%" (
|
||||||
|
echo [ERROR] Source not found: %SRC%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%ICON%" (
|
||||||
|
echo [ERROR] Icon not found: %ICON%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\adb.exe" (
|
||||||
|
echo [ERROR] adb.exe not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\AdbWinApi.dll" (
|
||||||
|
echo [ERROR] AdbWinApi.dll not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\AdbWinUsbApi.dll" (
|
||||||
|
echo [ERROR] AdbWinUsbApi.dll not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\7za.exe" (
|
||||||
|
echo [ERROR] 7za.exe not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [1/6] Installing deps...
|
||||||
|
"%PY%" -m pip install pyinstaller cython pyzipper -q
|
||||||
|
if errorlevel 1 (
|
||||||
|
"%PY%" -m pip install pyinstaller cython pyzipper -q -i https://pypi.tuna.tsinghua.edu.cn/simple
|
||||||
|
)
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Dependency install failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [2/6] Clean...
|
||||||
|
if exist "dist_cy" rmdir /s /q dist_cy 2>nul
|
||||||
|
if exist "build" rmdir /s /q build 2>nul
|
||||||
|
if exist "dist" rmdir /s /q dist 2>nul
|
||||||
|
if exist "%NAME%.spec" del /q "%NAME%.spec" 2>nul
|
||||||
|
|
||||||
|
echo [3/6] Cython compile...
|
||||||
|
mkdir dist_cy 2>nul
|
||||||
|
copy "%SRC%" dist_cy\_core.py >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Copy source failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
"%PY%" -c "open('dist_cy/setup_cython.py','w',encoding='utf-8').write('from setuptools import setup\nfrom Cython.Build import cythonize\nsetup(ext_modules=cythonize(\"_core.py\", compiler_directives={\"language_level\":\"3\"}))\n')"
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Failed to create Cython setup script
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
cd dist_cy
|
||||||
|
"%PY%" setup_cython.py build_ext --inplace
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Cython failed. Build stopped.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
set "PYD="
|
||||||
|
for %%f in (_core*.pyd) do set "PYD=%%f"
|
||||||
|
if "%PYD%"=="" (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] No Cython PYD generated. Build stopped.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo PYD: %PYD%
|
||||||
|
copy "%PYD%" _core.pyd >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Failed to copy Cython PYD
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
"%PY%" -c "open('launcher.py','w',encoding='utf-8').write('# -*- coding: utf-8 -*-\nfrom _core import main\nmain()\n')"
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Failed to create launcher.py
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [4/6] Copy resources...
|
||||||
|
copy "%TOOLS%\adb.exe" . >nul
|
||||||
|
copy "%TOOLS%\AdbWinApi.dll" . >nul
|
||||||
|
copy "%TOOLS%\AdbWinUsbApi.dll" . >nul
|
||||||
|
copy "%TOOLS%\7za.exe" . >nul
|
||||||
|
copy "%ICON%" . >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Copy resources failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [5/6] PyInstaller...
|
||||||
|
"%PY%" -m PyInstaller --onefile --windowed --name="%NAME%" --icon="app.ico" --add-data "adb.exe;." --add-data "AdbWinApi.dll;." --add-data "AdbWinUsbApi.dll;." --add-data "7za.exe;." --add-data "app.ico;." --add-binary "_core.pyd;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin launcher.py
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] PyInstaller failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [6/6] Cleanup...
|
||||||
|
del /q _core.py _core.c _core.pyd %PYD% launcher.py setup_cython.py app.ico 2>nul
|
||||||
|
rmdir /s /q build 2>nul
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Done.
|
||||||
|
if exist "dist_cy\dist\%NAME%.exe" (
|
||||||
|
echo Output: dist_cy\dist\%NAME%.exe
|
||||||
|
) else (
|
||||||
|
echo [ERROR] Output exe was not generated
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
pause
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001 >nul
|
||||||
|
cd /d "%~dp0"
|
||||||
|
set "ROOT=%~dp0.."
|
||||||
|
set "TOOLS=%ROOT%\tools"
|
||||||
|
set "NAME=Mazda_EZ60-Language-Install_v1.0-direct-push"
|
||||||
|
set "SRC=Mazda_EZ60-Language-Install_v1.0_direct_push.py"
|
||||||
|
set "ICON=%~dp0app.ico"
|
||||||
|
title %NAME% - Cython Build
|
||||||
|
|
||||||
|
echo ============================================================
|
||||||
|
echo %NAME% - Cython Build
|
||||||
|
echo ============================================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
where python >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Python not found
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
for /f "delims=" %%i in ('where python') do set "PY=%%i"
|
||||||
|
echo Python: %PY%
|
||||||
|
|
||||||
|
if not exist "%SRC%" (
|
||||||
|
echo [ERROR] Source not found: %SRC%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%ICON%" (
|
||||||
|
echo [ERROR] Icon not found: %ICON%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\adb.exe" (
|
||||||
|
echo [ERROR] adb.exe not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\AdbWinApi.dll" (
|
||||||
|
echo [ERROR] AdbWinApi.dll not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\AdbWinUsbApi.dll" (
|
||||||
|
echo [ERROR] AdbWinUsbApi.dll not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\7za.exe" (
|
||||||
|
echo [ERROR] 7za.exe not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [1/6] Installing deps...
|
||||||
|
"%PY%" -m pip install pyinstaller cython pyzipper -q
|
||||||
|
if errorlevel 1 (
|
||||||
|
"%PY%" -m pip install pyinstaller cython pyzipper -q -i https://pypi.tuna.tsinghua.edu.cn/simple
|
||||||
|
)
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Dependency install failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [2/6] Clean...
|
||||||
|
if exist "dist_cy" rmdir /s /q dist_cy 2>nul
|
||||||
|
if exist "build" rmdir /s /q build 2>nul
|
||||||
|
if exist "dist" rmdir /s /q dist 2>nul
|
||||||
|
if exist "%NAME%.spec" del /q "%NAME%.spec" 2>nul
|
||||||
|
|
||||||
|
echo [3/6] Cython compile...
|
||||||
|
mkdir dist_cy 2>nul
|
||||||
|
copy "%SRC%" dist_cy\_core.py >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Copy source failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
"%PY%" -c "open('dist_cy/setup_cython.py','w',encoding='utf-8').write('from setuptools import setup\nfrom Cython.Build import cythonize\nsetup(ext_modules=cythonize(\"_core.py\", compiler_directives={\"language_level\":\"3\"}))\n')"
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Failed to create Cython setup script
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
cd dist_cy
|
||||||
|
"%PY%" setup_cython.py build_ext --inplace
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Cython failed. Build stopped.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
set "PYD="
|
||||||
|
for %%f in (_core*.pyd) do set "PYD=%%f"
|
||||||
|
if "%PYD%"=="" (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] No Cython PYD generated. Build stopped.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo PYD: %PYD%
|
||||||
|
copy "%PYD%" _core.pyd >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Failed to copy Cython PYD
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
"%PY%" -c "open('launcher.py','w',encoding='utf-8').write('# -*- coding: utf-8 -*-\nfrom _core import main\nmain()\n')"
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Failed to create launcher.py
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [4/6] Copy resources...
|
||||||
|
copy "%TOOLS%\adb.exe" . >nul
|
||||||
|
copy "%TOOLS%\AdbWinApi.dll" . >nul
|
||||||
|
copy "%TOOLS%\AdbWinUsbApi.dll" . >nul
|
||||||
|
copy "%TOOLS%\7za.exe" . >nul
|
||||||
|
copy "%ICON%" . >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Copy resources failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [5/6] PyInstaller...
|
||||||
|
"%PY%" -m PyInstaller --onefile --windowed --name="%NAME%" --icon="app.ico" --add-data "adb.exe;." --add-data "AdbWinApi.dll;." --add-data "AdbWinUsbApi.dll;." --add-data "7za.exe;." --add-data "app.ico;." --add-binary "_core.pyd;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin launcher.py
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] PyInstaller failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [6/6] Cleanup...
|
||||||
|
del /q _core.py _core.c _core.pyd %PYD% launcher.py setup_cython.py app.ico 2>nul
|
||||||
|
rmdir /s /q build 2>nul
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Done.
|
||||||
|
if exist "dist_cy\dist\%NAME%.exe" (
|
||||||
|
echo Output: dist_cy\dist\%NAME%.exe
|
||||||
|
) else (
|
||||||
|
echo [ERROR] Output exe was not generated
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
pause
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001 >nul
|
||||||
|
cd /d "%~dp0"
|
||||||
|
set "ROOT=%~dp0.."
|
||||||
|
set "TOOLS=%ROOT%\tools"
|
||||||
|
set "NAME=Mazda-EZ60-Language-Installer_v1.2"
|
||||||
|
set "SRC=Mazda-EZ60_1.2.py"
|
||||||
|
set "ICON=%~dp0app.ico"
|
||||||
|
title %NAME% - Cython Build
|
||||||
|
|
||||||
|
echo ============================================================
|
||||||
|
echo %NAME% - Cython Build
|
||||||
|
echo ============================================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
where python >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Python not found
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
for /f "delims=" %%i in ('where python') do set "PY=%%i"
|
||||||
|
echo Python: %PY%
|
||||||
|
|
||||||
|
if not exist "%SRC%" (
|
||||||
|
echo [ERROR] Source not found: %SRC%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%ICON%" (
|
||||||
|
echo [ERROR] Icon not found: %ICON%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\adb.exe" (
|
||||||
|
echo [ERROR] adb.exe not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\AdbWinApi.dll" (
|
||||||
|
echo [ERROR] AdbWinApi.dll not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\AdbWinUsbApi.dll" (
|
||||||
|
echo [ERROR] AdbWinUsbApi.dll not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\7za.exe" (
|
||||||
|
echo [ERROR] 7za.exe not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\fastboot.exe" (
|
||||||
|
echo [ERROR] fastboot.exe not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\usb_driver\android_winusb.inf" (
|
||||||
|
echo [ERROR] usb_driver not found at %TOOLS%\usb_driver
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%~dp0runtime.dat" (
|
||||||
|
echo [ERROR] runtime.dat not found at %~dp0runtime.dat
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [1/6] Installing deps...
|
||||||
|
"%PY%" -m pip install pyinstaller cython pyzipper cryptography -q
|
||||||
|
if errorlevel 1 (
|
||||||
|
"%PY%" -m pip install pyinstaller cython pyzipper cryptography -q -i https://pypi.tuna.tsinghua.edu.cn/simple
|
||||||
|
)
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Dependency install failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [2/6] Clean...
|
||||||
|
if exist "dist_cy" rmdir /s /q dist_cy 2>nul
|
||||||
|
if exist "build" rmdir /s /q build 2>nul
|
||||||
|
if exist "dist" rmdir /s /q dist 2>nul
|
||||||
|
if exist "%NAME%.spec" del /q "%NAME%.spec" 2>nul
|
||||||
|
|
||||||
|
echo [3/6] Cython compile...
|
||||||
|
mkdir dist_cy 2>nul
|
||||||
|
copy "%SRC%" dist_cy\_core.py >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Copy source failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
"%PY%" -c "open('dist_cy/setup_cython.py','w',encoding='utf-8').write('from setuptools import setup\nfrom Cython.Build import cythonize\nsetup(ext_modules=cythonize(\"_core.py\", compiler_directives={\"language_level\":\"3\"}))\n')"
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Failed to create Cython setup script
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
cd dist_cy
|
||||||
|
"%PY%" setup_cython.py build_ext --inplace
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Cython failed. Build stopped.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
set "PYD="
|
||||||
|
for %%f in (_core*.pyd) do set "PYD=%%f"
|
||||||
|
if "%PYD%"=="" (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] No Cython PYD generated. Build stopped.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo PYD: %PYD%
|
||||||
|
copy "%PYD%" _core.pyd >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Failed to copy Cython PYD
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
"%PY%" -c "open('launcher.py','w',encoding='utf-8').write('# -*- coding: utf-8 -*-\nfrom _core import main\nmain()\n')"
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Failed to create launcher.py
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [4/6] Copy resources...
|
||||||
|
copy "%TOOLS%\adb.exe" . >nul
|
||||||
|
copy "%TOOLS%\AdbWinApi.dll" . >nul
|
||||||
|
copy "%TOOLS%\AdbWinUsbApi.dll" . >nul
|
||||||
|
copy "%TOOLS%\7za.exe" . >nul
|
||||||
|
copy "%TOOLS%\fastboot.exe" . >nul
|
||||||
|
copy "%ICON%" . >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Copy resources failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if exist "%~dp0EZ60_resource.dat" copy "%~dp0EZ60_resource.dat" . >nul
|
||||||
|
|
||||||
|
echo [5/6] PyInstaller...
|
||||||
|
set ADD_PERMISSION_RESOURCE=
|
||||||
|
if exist "EZ60_resource.dat" set ADD_PERMISSION_RESOURCE=--add-data "EZ60_resource.dat;."
|
||||||
|
"%PY%" -m PyInstaller --onefile --windowed --name="%NAME%" --icon="app.ico" --add-data "adb.exe;." --add-data "AdbWinApi.dll;." --add-data "AdbWinUsbApi.dll;." --add-data "7za.exe;." --add-data "fastboot.exe;." --add-data "app.ico;." --add-data "%TOOLS%\usb_driver;usb_driver" %ADD_PERMISSION_RESOURCE% --add-binary "_core.pyd;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --hidden-import=cryptography --collect-all tkinter --collect-all cryptography --uac-admin launcher.py
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] PyInstaller failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [6/6] Cleanup...
|
||||||
|
del /q _core.py _core.c _core.pyd %PYD% launcher.py setup_cython.py app.ico fastboot.exe EZ60_resource.dat 2>nul
|
||||||
|
rmdir /s /q build 2>nul
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
if not exist "dist_cy\dist\%NAME%.exe" (
|
||||||
|
echo [ERROR] Output exe was not generated
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
copy "runtime.dat" "dist_cy\dist\runtime.dat" >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Copy runtime.dat failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
if exist "package_voice-assistant.bin" (
|
||||||
|
copy "package_voice-assistant.bin" "dist_cy\dist\package_voice-assistant.bin" >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Copy package_voice-assistant.bin failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Done.
|
||||||
|
echo Output: dist_cy\dist\%NAME%.exe
|
||||||
|
pause
|
||||||
@@ -66,17 +66,61 @@ def find_resource(file_name):
|
|||||||
return candidates[0]
|
return candidates[0]
|
||||||
|
|
||||||
|
|
||||||
|
def resource_dir_candidates(dir_name):
|
||||||
|
base_dir = get_app_dir()
|
||||||
|
candidates = []
|
||||||
|
if getattr(sys, 'frozen', False):
|
||||||
|
candidates.append(Path(getattr(sys, '_MEIPASS', base_dir)) / dir_name)
|
||||||
|
candidates.extend([
|
||||||
|
base_dir / dir_name,
|
||||||
|
base_dir / 'tools' / dir_name,
|
||||||
|
base_dir / 'shared' / dir_name,
|
||||||
|
base_dir.parent / dir_name,
|
||||||
|
base_dir.parent / 'tools' / dir_name,
|
||||||
|
base_dir.parent / 'shared' / dir_name,
|
||||||
|
])
|
||||||
|
unique = []
|
||||||
|
for candidate in candidates:
|
||||||
|
if candidate not in unique:
|
||||||
|
unique.append(candidate)
|
||||||
|
return unique
|
||||||
|
|
||||||
|
|
||||||
|
def find_resource_dir(dir_name):
|
||||||
|
candidates = resource_dir_candidates(dir_name)
|
||||||
|
for candidate in candidates:
|
||||||
|
if candidate.exists() and candidate.is_dir():
|
||||||
|
return candidate
|
||||||
|
return candidates[0]
|
||||||
|
|
||||||
|
|
||||||
def find_tool(file_name, fallback=None):
|
def find_tool(file_name, fallback=None):
|
||||||
path = find_resource(file_name)
|
path = find_resource(file_name)
|
||||||
if path.exists():
|
if path.exists():
|
||||||
return str(path)
|
return str(path)
|
||||||
return fallback or str(path)
|
return fallback or str(path)
|
||||||
|
|
||||||
|
|
||||||
|
def set_windows_app_id():
|
||||||
|
if sys.platform != 'win32':
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
import ctypes
|
||||||
|
app_id = 'YibinKeyi.Q05Lidar.Installer.1.0'
|
||||||
|
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(app_id)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ADKAPKGUI:
|
class ADKAPKGUI:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
set_windows_app_id()
|
||||||
self.root = tk.Tk()
|
self.root = tk.Tk()
|
||||||
self.root.title("启源Q05Ultra+激光雷达")
|
self.root.title("启源Q05Ultra+激光雷达")
|
||||||
self.root.geometry("900x660")
|
self.root.geometry("900x660")
|
||||||
self.root.resizable(True, True)
|
self.root.resizable(True, True)
|
||||||
|
self.set_window_icon()
|
||||||
|
self.root.after(200, self.set_window_icon)
|
||||||
self.default_window_size = (900, 660)
|
self.default_window_size = (900, 660)
|
||||||
self.right_panel_width = 280
|
self.right_panel_width = 280
|
||||||
|
|
||||||
@@ -129,6 +173,7 @@ class ADKAPKGUI:
|
|||||||
'auth_yes': '已授权',
|
'auth_yes': '已授权',
|
||||||
'auth_no': '未授权',
|
'auth_no': '未授权',
|
||||||
'btn_refresh': '🔄 检查',
|
'btn_refresh': '🔄 检查',
|
||||||
|
'btn_install_driver': '🧩 安装驱动',
|
||||||
'hint_factory': '🔧 关闭车辆WI-FI和4G网络,拨号获取的密码进入工程模式',
|
'hint_factory': '🔧 关闭车辆WI-FI和4G网络,拨号获取的密码进入工程模式',
|
||||||
'hotspot_title': '📶 电脑热点',
|
'hotspot_title': '📶 电脑热点',
|
||||||
'hotspot_start': '🔧 打开热点设置',
|
'hotspot_start': '🔧 打开热点设置',
|
||||||
@@ -163,11 +208,21 @@ class ADKAPKGUI:
|
|||||||
'msg_permission_failed': '获取失败',
|
'msg_permission_failed': '获取失败',
|
||||||
'msg_confirm_permission_title': '确认获取权限',
|
'msg_confirm_permission_title': '确认获取权限',
|
||||||
'msg_confirm_permission': '此操作将为当前设备获取权限。\n过程中请不要断电或拔线。\n\n是否继续?',
|
'msg_confirm_permission': '此操作将为当前设备获取权限。\n过程中请不要断电或拔线。\n\n是否继续?',
|
||||||
|
'msg_driver_missing_title': '驱动环境',
|
||||||
|
'msg_driver_missing': '驱动缺失,即将自动安装驱动。',
|
||||||
|
'msg_driver_install_confirm': '即将安装驱动环境,需要管理员权限。',
|
||||||
|
'msg_driver_install_done': '驱动安装完成。如设备仍无法识别,请重新插拔 USB 线缆。',
|
||||||
|
'msg_driver_install_failed': '驱动安装失败: {error}',
|
||||||
'status_debug': '🔧 调试模式',
|
'status_debug': '🔧 调试模式',
|
||||||
'log_cleared': '日志已清空',
|
'log_cleared': '日志已清空',
|
||||||
'log_lang_changed': '语言已切换为中文',
|
'log_lang_changed': '语言已切换为中文',
|
||||||
'log_debug_on': '🔧 调试模式已开启',
|
'log_debug_on': '🔧 调试模式已开启',
|
||||||
'log_debug_off': '调试模式已关闭',
|
'log_debug_off': '调试模式已关闭',
|
||||||
|
'log_driver_found': '已检测到驱动环境',
|
||||||
|
'log_driver_missing': '未检测到驱动环境',
|
||||||
|
'log_driver_install_start': '正在安装驱动环境...',
|
||||||
|
'log_driver_install_success': '驱动环境安装完成',
|
||||||
|
'log_driver_install_failed': '驱动环境安装失败: {error}',
|
||||||
'log_permission_success': '获取成功',
|
'log_permission_success': '获取成功',
|
||||||
'log_push_success': '语言包刷入完成,重启设备后生效',
|
'log_push_success': '语言包刷入完成,重启设备后生效',
|
||||||
'log_push_failed': '语言包刷入失败',
|
'log_push_failed': '语言包刷入失败',
|
||||||
@@ -193,7 +248,7 @@ class ADKAPKGUI:
|
|||||||
'progress_flashing': '正在刷入',
|
'progress_flashing': '正在刷入',
|
||||||
'progress_flash_done': '刷入完成',
|
'progress_flash_done': '刷入完成',
|
||||||
'progress_extracting_percent': '资源准备中 {percent}%',
|
'progress_extracting_percent': '资源准备中 {percent}%',
|
||||||
'progress_install_module': '安装模块 {module}',
|
'progress_install_module': '安装资源',
|
||||||
'progress_push_files': '推送文件 {current}/{total}',
|
'progress_push_files': '推送文件 {current}/{total}',
|
||||||
'quick_lang_title': '快捷语言设置',
|
'quick_lang_title': '快捷语言设置',
|
||||||
'quick_lang_header': '选择目标语言',
|
'quick_lang_header': '选择目标语言',
|
||||||
@@ -206,7 +261,7 @@ class ADKAPKGUI:
|
|||||||
'reboot_confirm_title': '确认重启',
|
'reboot_confirm_title': '确认重启',
|
||||||
'reboot_confirm': '确定要重启设备吗?',
|
'reboot_confirm': '确定要重启设备吗?',
|
||||||
'disable_confirm_title': '确认禁用升级',
|
'disable_confirm_title': '确认禁用升级',
|
||||||
'disable_confirm': '⚠️ 警告:禁用系统升级后,将无法接收系统更新!\n\n是否确定要禁用系统升级应用?\n\n禁用命令:\nadb shell pm disable-user --user 0 com.incall.apps.softmanager',
|
'disable_confirm': '⚠️ 警告:禁用系统升级后,将无法接收系统更新!\n\n是否确定要禁用系统升级应用?',
|
||||||
'disable_success_title': '成功',
|
'disable_success_title': '成功',
|
||||||
'disable_success': '系统升级已成功禁用!',
|
'disable_success': '系统升级已成功禁用!',
|
||||||
'disable_failed': '禁用失败:{output}',
|
'disable_failed': '禁用失败:{output}',
|
||||||
@@ -227,7 +282,7 @@ class ADKAPKGUI:
|
|||||||
'debug_need_vin': '请先刷新设备VIN,或在工程密码输入框填入VIN',
|
'debug_need_vin': '请先刷新设备VIN,或在工程密码输入框填入VIN',
|
||||||
'log_decrypt_success': '解密测试成功: size={size}, sha256={sha}',
|
'log_decrypt_success': '解密测试成功: size={size}, sha256={sha}',
|
||||||
'log_package_extract_success': 'Q05_Lidar-package.bin 解压测试成功: APK数量={count}',
|
'log_package_extract_success': 'Q05_Lidar-package.bin 解压测试成功: APK数量={count}',
|
||||||
'log_package_extract_modules_success': 'Q05_Lidar-package.bin 解压测试成功: 主模块={main}, 附加模块={extra}',
|
'log_package_extract_modules_success': 'Q05_Lidar-package.bin 解压测试成功: 资源数量={count}',
|
||||||
'hotspot_stopped': '未启动',
|
'hotspot_stopped': '未启动',
|
||||||
'log_device_disconnected': '设备已断开连接',
|
'log_device_disconnected': '设备已断开连接',
|
||||||
'log_cache_invalid': '已解压缓存无效: {reason}',
|
'log_cache_invalid': '已解压缓存无效: {reason}',
|
||||||
@@ -248,12 +303,11 @@ class ADKAPKGUI:
|
|||||||
'log_apps_dir_missing': '警告:未找到 apps 目录',
|
'log_apps_dir_missing': '警告:未找到 apps 目录',
|
||||||
'log_extracted_resource_invalid': '解压后的资源无效: {reason}。已停止刷入,请检查解压密码或资源包。',
|
'log_extracted_resource_invalid': '解压后的资源无效: {reason}。已停止刷入,请检查解压密码或资源包。',
|
||||||
'log_resource_ready': '资源准备完成 (app: {count})',
|
'log_resource_ready': '资源准备完成 (app: {count})',
|
||||||
'log_module_resource_ready': '资源准备完成: 主模块={main}, 附加模块={extra}',
|
'log_module_resource_ready': '资源准备完成',
|
||||||
'err_module_prop_missing': '资源包缺少 module.prop',
|
'err_module_prop_missing': '资源包缺少资源配置',
|
||||||
'err_system_dir_missing': '资源包缺少 system 目录',
|
'err_module_id_missing': '资源配置缺少 ID 字段',
|
||||||
'err_disable_module_missing': '资源包缺少 disable-wireless-adb-vecentek-magisk.zip',
|
'err_module_zip_invalid': '资源文件无效或缺少配置: {file}',
|
||||||
'err_module_id_missing': 'module.prop 缺少 id 字段',
|
'err_duplicate_module_id': '资源包存在重复资源 ID',
|
||||||
'err_disable_module_invalid': '附加模块压缩包无效或缺少 module.prop',
|
|
||||||
'log_resource_prepare_failed_detail': '资源准备失败: {error}',
|
'log_resource_prepare_failed_detail': '资源准备失败: {error}',
|
||||||
'log_resource_prepare_network': '资源准备失败,请检查网络连接后重试',
|
'log_resource_prepare_network': '资源准备失败,请检查网络连接后重试',
|
||||||
'log_package_file_missing': '未找到资源包文件',
|
'log_package_file_missing': '未找到资源包文件',
|
||||||
@@ -296,7 +350,7 @@ class ADKAPKGUI:
|
|||||||
'log_runtime_install_failed': '获取失败',
|
'log_runtime_install_failed': '获取失败',
|
||||||
'log_runtime_install_success': '正在获取权限中',
|
'log_runtime_install_success': '正在获取权限中',
|
||||||
'log_fastboot_enter_failed': '获取失败',
|
'log_fastboot_enter_failed': '获取失败',
|
||||||
'log_fastboot_wait': '正在获取权限中',
|
'log_fastboot_wait': '正在获取权限,请不要关闭程序或断开数据连接!',
|
||||||
'log_fastboot_missing': '获取失败',
|
'log_fastboot_missing': '获取失败',
|
||||||
'log_init_boot_flash_failed': '获取失败',
|
'log_init_boot_flash_failed': '获取失败',
|
||||||
'log_init_boot_flash_success': '正在获取权限中',
|
'log_init_boot_flash_success': '正在获取权限中',
|
||||||
@@ -305,16 +359,16 @@ class ADKAPKGUI:
|
|||||||
'log_install_failed_detail': 'install失败: {error}',
|
'log_install_failed_detail': 'install失败: {error}',
|
||||||
'log_prepare_resource': '正在准备资源...',
|
'log_prepare_resource': '正在准备资源...',
|
||||||
'log_flash_start': '开始刷入语言包...',
|
'log_flash_start': '开始刷入语言包...',
|
||||||
'log_open_magisk': '正在打开 Magisk App,请在弹窗中授予 Shell/root 权限',
|
'log_open_magisk': '请在弹窗中点击“允许”授予权限',
|
||||||
'log_root_checking': '正在检测 ADB Shell root 权限...',
|
'log_root_checking': '正在检测权限...',
|
||||||
'log_root_ok': 'ADB Shell root 权限已授予',
|
'log_root_ok': '权限已授予',
|
||||||
'log_root_failed': '未获得 ADB Shell root 权限,请手动在 Magisk App 授予 Shell 权限',
|
'log_root_failed': '权限获取失败,请手动授予权限',
|
||||||
'msg_root_failed': '请手动在 Magisk App 授予 Shell 权限后重试。',
|
'msg_root_failed': '请手动授予权限后重试。',
|
||||||
'log_module_install_start': '开始安装 Magisk 模块: {module}',
|
'log_module_install_start': '开始安装资源',
|
||||||
'log_module_install_done': 'Magisk 模块安装完成: {module}',
|
'log_module_install_done': '资源安装完成',
|
||||||
'log_module_install_failed': 'Magisk 模块安装失败: {module}: {error}',
|
'log_module_install_failed': '资源安装失败: {error}',
|
||||||
'log_push_file_failed': '文件推送失败: {file}: {error}',
|
'log_push_file_failed': '文件推送失败: {file}: {error}',
|
||||||
'log_root_cmd_failed': 'Root 命令执行失败: {command}: {error}',
|
'log_root_cmd_failed': '权限操作失败: {error}',
|
||||||
'log_no_lang_pkg': '未找到语言包文件',
|
'log_no_lang_pkg': '未找到语言包文件',
|
||||||
'log_install_item_ok': '安装成功: {name}.apk',
|
'log_install_item_ok': '安装成功: {name}.apk',
|
||||||
'log_install_item_failed': '安装失败: {name}.apk',
|
'log_install_item_failed': '安装失败: {name}.apk',
|
||||||
@@ -386,6 +440,7 @@ class ADKAPKGUI:
|
|||||||
'auth_yes': 'Authorized',
|
'auth_yes': 'Authorized',
|
||||||
'auth_no': 'Unauthorized',
|
'auth_no': 'Unauthorized',
|
||||||
'btn_refresh': '🔄 Check',
|
'btn_refresh': '🔄 Check',
|
||||||
|
'btn_install_driver': '🧩 Driver',
|
||||||
'hint_factory': '🔧 Turn off the vehicle WiFi & 4G, enter factory mode with dial code',
|
'hint_factory': '🔧 Turn off the vehicle WiFi & 4G, enter factory mode with dial code',
|
||||||
'hotspot_title': '📶 Hotspot',
|
'hotspot_title': '📶 Hotspot',
|
||||||
'hotspot_start': '🔧 Open Hotspot Settings',
|
'hotspot_start': '🔧 Open Hotspot Settings',
|
||||||
@@ -422,11 +477,21 @@ class ADKAPKGUI:
|
|||||||
'msg_permission_failed': 'Permission failed',
|
'msg_permission_failed': 'Permission failed',
|
||||||
'msg_confirm_permission_title': 'Confirm Unlock',
|
'msg_confirm_permission_title': 'Confirm Unlock',
|
||||||
'msg_confirm_permission': 'This will grant permission for the current device.\nDo not disconnect power or USB during the process.\n\nContinue?',
|
'msg_confirm_permission': 'This will grant permission for the current device.\nDo not disconnect power or USB during the process.\n\nContinue?',
|
||||||
|
'msg_driver_missing_title': 'Driver Environment',
|
||||||
|
'msg_driver_missing': 'Driver environment is missing. Driver installation will start automatically.',
|
||||||
|
'msg_driver_install_confirm': 'Driver environment installation requires administrator permission.',
|
||||||
|
'msg_driver_install_done': 'Driver installation completed. If the device is still not recognized, reconnect USB cable.',
|
||||||
|
'msg_driver_install_failed': 'Driver installation failed: {error}',
|
||||||
'status_debug': '🔧 Debug mode',
|
'status_debug': '🔧 Debug mode',
|
||||||
'log_cleared': 'Log cleared',
|
'log_cleared': 'Log cleared',
|
||||||
'log_lang_changed': 'Language switched to English',
|
'log_lang_changed': 'Language switched to English',
|
||||||
'log_debug_on': '🔧 Debug mode enabled',
|
'log_debug_on': '🔧 Debug mode enabled',
|
||||||
'log_debug_off': 'Debug mode disabled',
|
'log_debug_off': 'Debug mode disabled',
|
||||||
|
'log_driver_found': 'Driver environment detected',
|
||||||
|
'log_driver_missing': 'Driver environment not detected',
|
||||||
|
'log_driver_install_start': 'Installing driver environment...',
|
||||||
|
'log_driver_install_success': 'Driver environment installed',
|
||||||
|
'log_driver_install_failed': 'Driver environment installation failed: {error}',
|
||||||
'log_permission_success': 'Permission granted',
|
'log_permission_success': 'Permission granted',
|
||||||
'log_push_success': 'Language package flashed. Reboot to apply changes.',
|
'log_push_success': 'Language package flashed. Reboot to apply changes.',
|
||||||
'log_push_failed': 'Language package flash failed',
|
'log_push_failed': 'Language package flash failed',
|
||||||
@@ -452,7 +517,7 @@ class ADKAPKGUI:
|
|||||||
'progress_flashing': 'Flashing',
|
'progress_flashing': 'Flashing',
|
||||||
'progress_flash_done': 'Flash complete',
|
'progress_flash_done': 'Flash complete',
|
||||||
'progress_extracting_percent': 'Preparing resources {percent}%',
|
'progress_extracting_percent': 'Preparing resources {percent}%',
|
||||||
'progress_install_module': 'Installing module {module}',
|
'progress_install_module': 'Installing resources',
|
||||||
'progress_push_files': 'Pushing files {current}/{total}',
|
'progress_push_files': 'Pushing files {current}/{total}',
|
||||||
'quick_lang_title': 'Quick Language',
|
'quick_lang_title': 'Quick Language',
|
||||||
'quick_lang_header': 'Choose target language',
|
'quick_lang_header': 'Choose target language',
|
||||||
@@ -465,7 +530,7 @@ class ADKAPKGUI:
|
|||||||
'reboot_confirm_title': 'Confirm Reboot',
|
'reboot_confirm_title': 'Confirm Reboot',
|
||||||
'reboot_confirm': 'Reboot the device now?',
|
'reboot_confirm': 'Reboot the device now?',
|
||||||
'disable_confirm_title': 'Confirm Disable OTA',
|
'disable_confirm_title': 'Confirm Disable OTA',
|
||||||
'disable_confirm': '⚠️ Warning: disabling system upgrade prevents system updates.\n\nDisable the system upgrade app?\n\nCommand:\nadb shell pm disable-user --user 0 com.incall.apps.softmanager',
|
'disable_confirm': '⚠️ Warning: disabling system upgrade prevents system updates.\n\nDisable the system upgrade app?',
|
||||||
'disable_success_title': 'Success',
|
'disable_success_title': 'Success',
|
||||||
'disable_success': 'System upgrade app disabled.',
|
'disable_success': 'System upgrade app disabled.',
|
||||||
'disable_failed': 'Disable failed: {output}',
|
'disable_failed': 'Disable failed: {output}',
|
||||||
@@ -486,7 +551,7 @@ class ADKAPKGUI:
|
|||||||
'debug_need_vin': 'Refresh device VIN first, or enter a VIN in the password query field.',
|
'debug_need_vin': 'Refresh device VIN first, or enter a VIN in the password query field.',
|
||||||
'log_decrypt_success': 'Decrypt test passed: size={size}, sha256={sha}',
|
'log_decrypt_success': 'Decrypt test passed: size={size}, sha256={sha}',
|
||||||
'log_package_extract_success': 'Q05_Lidar-package.bin extract test passed: APK count={count}',
|
'log_package_extract_success': 'Q05_Lidar-package.bin extract test passed: APK count={count}',
|
||||||
'log_package_extract_modules_success': 'Q05_Lidar-package.bin extract test passed: main={main}, extra={extra}',
|
'log_package_extract_modules_success': 'Q05_Lidar-package.bin extract test passed: resource count={count}',
|
||||||
'hotspot_stopped': 'stopped',
|
'hotspot_stopped': 'stopped',
|
||||||
'log_device_disconnected': 'Device disconnected',
|
'log_device_disconnected': 'Device disconnected',
|
||||||
'log_cache_invalid': 'Extract cache invalid: {reason}',
|
'log_cache_invalid': 'Extract cache invalid: {reason}',
|
||||||
@@ -507,12 +572,11 @@ class ADKAPKGUI:
|
|||||||
'log_apps_dir_missing': 'apps directory not found',
|
'log_apps_dir_missing': 'apps directory not found',
|
||||||
'log_extracted_resource_invalid': 'Extracted resources are invalid: {reason}. Flashing stopped. Check the password or package.',
|
'log_extracted_resource_invalid': 'Extracted resources are invalid: {reason}. Flashing stopped. Check the password or package.',
|
||||||
'log_resource_ready': 'Resources ready (app: {count})',
|
'log_resource_ready': 'Resources ready (app: {count})',
|
||||||
'log_module_resource_ready': 'Resources ready: main={main}, extra={extra}',
|
'log_module_resource_ready': 'Resources ready',
|
||||||
'err_module_prop_missing': 'module.prop is missing from the package',
|
'err_module_prop_missing': 'Resource config is missing from the package',
|
||||||
'err_system_dir_missing': 'system directory is missing from the package',
|
'err_module_id_missing': 'Resource config is missing the id field',
|
||||||
'err_disable_module_missing': 'disable-wireless-adb-vecentek-magisk.zip is missing from the package',
|
'err_module_zip_invalid': 'Resource file is invalid or missing config: {file}',
|
||||||
'err_module_id_missing': 'module.prop is missing the id field',
|
'err_duplicate_module_id': 'Duplicate resource ID found in package',
|
||||||
'err_disable_module_invalid': 'Extra module archive is invalid or missing module.prop',
|
|
||||||
'log_resource_prepare_failed_detail': 'Resource preparation failed: {error}',
|
'log_resource_prepare_failed_detail': 'Resource preparation failed: {error}',
|
||||||
'log_resource_prepare_network': 'Resource preparation failed. Check the network connection and retry.',
|
'log_resource_prepare_network': 'Resource preparation failed. Check the network connection and retry.',
|
||||||
'log_package_file_missing': 'Package file not found',
|
'log_package_file_missing': 'Package file not found',
|
||||||
@@ -555,7 +619,7 @@ class ADKAPKGUI:
|
|||||||
'log_runtime_install_failed': 'Permission failed',
|
'log_runtime_install_failed': 'Permission failed',
|
||||||
'log_runtime_install_success': 'Getting permission',
|
'log_runtime_install_success': 'Getting permission',
|
||||||
'log_fastboot_enter_failed': 'Permission failed',
|
'log_fastboot_enter_failed': 'Permission failed',
|
||||||
'log_fastboot_wait': 'Getting permission',
|
'log_fastboot_wait': 'Getting permission. Do not close the program or disconnect the data cable.',
|
||||||
'log_fastboot_missing': 'Permission failed',
|
'log_fastboot_missing': 'Permission failed',
|
||||||
'log_init_boot_flash_failed': 'Permission failed',
|
'log_init_boot_flash_failed': 'Permission failed',
|
||||||
'log_init_boot_flash_success': 'Getting permission',
|
'log_init_boot_flash_success': 'Getting permission',
|
||||||
@@ -564,16 +628,16 @@ class ADKAPKGUI:
|
|||||||
'log_install_failed_detail': 'install failed: {error}',
|
'log_install_failed_detail': 'install failed: {error}',
|
||||||
'log_prepare_resource': 'Preparing resources...',
|
'log_prepare_resource': 'Preparing resources...',
|
||||||
'log_flash_start': 'Starting language package flash...',
|
'log_flash_start': 'Starting language package flash...',
|
||||||
'log_open_magisk': 'Opening Magisk App. Grant Shell/root permission when prompted.',
|
'log_open_magisk': 'Tap "允许" to grant permission when prompted.',
|
||||||
'log_root_checking': 'Checking ADB Shell root permission...',
|
'log_root_checking': 'Checking permission...',
|
||||||
'log_root_ok': 'ADB Shell root permission granted',
|
'log_root_ok': 'Permission granted',
|
||||||
'log_root_failed': 'ADB Shell root permission was not granted. Grant Shell permission in Magisk manually.',
|
'log_root_failed': 'Permission grant failed. Grant permission manually.',
|
||||||
'msg_root_failed': 'Grant Shell permission in Magisk manually, then retry.',
|
'msg_root_failed': 'Grant permission manually, then retry.',
|
||||||
'log_module_install_start': 'Installing Magisk module: {module}',
|
'log_module_install_start': 'Installing resources',
|
||||||
'log_module_install_done': 'Magisk module installed: {module}',
|
'log_module_install_done': 'Resources installed',
|
||||||
'log_module_install_failed': 'Magisk module install failed: {module}: {error}',
|
'log_module_install_failed': 'Resource installation failed: {error}',
|
||||||
'log_push_file_failed': 'File push failed: {file}: {error}',
|
'log_push_file_failed': 'File push failed: {file}: {error}',
|
||||||
'log_root_cmd_failed': 'Root command failed: {command}: {error}',
|
'log_root_cmd_failed': 'Permission operation failed: {error}',
|
||||||
'log_no_lang_pkg': 'No language package files found',
|
'log_no_lang_pkg': 'No language package files found',
|
||||||
'log_install_item_ok': 'Installed: {name}.apk',
|
'log_install_item_ok': 'Installed: {name}.apk',
|
||||||
'log_install_item_failed': 'Install failed: {name}.apk',
|
'log_install_item_failed': 'Install failed: {name}.apk',
|
||||||
@@ -621,6 +685,8 @@ class ADKAPKGUI:
|
|||||||
self.adb = self._find_tool('adb.exe', 'adb')
|
self.adb = self._find_tool('adb.exe', 'adb')
|
||||||
self.sz = self._find_tool('7za.exe', str(find_resource('7za.exe')))
|
self.sz = self._find_tool('7za.exe', str(find_resource('7za.exe')))
|
||||||
self.fastboot = self._find_tool('fastboot.exe', 'fastboot')
|
self.fastboot = self._find_tool('fastboot.exe', 'fastboot')
|
||||||
|
self.driver_dir = find_resource_dir("usb_driver")
|
||||||
|
self.driver_inf = self.driver_dir / "android_winusb.inf"
|
||||||
self.package_file = find_resource("Q05_Lidar-package.bin")
|
self.package_file = find_resource("Q05_Lidar-package.bin")
|
||||||
self.runtime_file = self._find_data_file("runtime.dat", prefer_embedded=True)
|
self.runtime_file = self._find_data_file("runtime.dat", prefer_embedded=True)
|
||||||
self.resource_file = self._find_data_file("resource.dat", prefer_embedded=True)
|
self.resource_file = self._find_data_file("resource.dat", prefer_embedded=True)
|
||||||
@@ -629,8 +695,7 @@ class ADKAPKGUI:
|
|||||||
self.resource_key = None
|
self.resource_key = None
|
||||||
self.apps_dir = None
|
self.apps_dir = None
|
||||||
self.temp_dir = None
|
self.temp_dir = None
|
||||||
self.module_dir = None
|
self.module_zips = []
|
||||||
self.disable_module_dir = None
|
|
||||||
self.runtime_cache_dir = None
|
self.runtime_cache_dir = None
|
||||||
self.api_url = "https://api.changan.softwindy.cn/api/authorizations/auth-check"
|
self.api_url = "https://api.changan.softwindy.cn/api/authorizations/auth-check"
|
||||||
self.boot_challenge_api_url = "https://api.changan.softwindy.cn/api/authorizations/boot-challenge"
|
self.boot_challenge_api_url = "https://api.changan.softwindy.cn/api/authorizations/boot-challenge"
|
||||||
@@ -642,6 +707,7 @@ class ADKAPKGUI:
|
|||||||
self.device_connected = False
|
self.device_connected = False
|
||||||
self._refreshing = False # 防止并发刷新
|
self._refreshing = False # 防止并发刷新
|
||||||
self.debug_mode = False # 调试模式
|
self.debug_mode = False # 调试模式
|
||||||
|
self.driver_prompted = False
|
||||||
atexit.register(self.cleanup_cache_on_exit)
|
atexit.register(self.cleanup_cache_on_exit)
|
||||||
|
|
||||||
# 设置样式
|
# 设置样式
|
||||||
@@ -656,6 +722,15 @@ class ADKAPKGUI:
|
|||||||
# 启动设备状态监控
|
# 启动设备状态监控
|
||||||
self.start_device_monitor()
|
self.start_device_monitor()
|
||||||
|
|
||||||
|
def set_window_icon(self):
|
||||||
|
"""Set the Tk window/taskbar icon at runtime; PyInstaller --icon only sets the exe file icon."""
|
||||||
|
try:
|
||||||
|
icon_path = find_resource("app.ico")
|
||||||
|
if icon_path.exists():
|
||||||
|
self.root.iconbitmap(str(icon_path))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
def _find_tool(self, exe_name, fallback):
|
def _find_tool(self, exe_name, fallback):
|
||||||
candidates = resource_candidates(exe_name)
|
candidates = resource_candidates(exe_name)
|
||||||
candidates.extend([
|
candidates.extend([
|
||||||
@@ -931,15 +1006,27 @@ class ADKAPKGUI:
|
|||||||
bg=self.colors['bg_light'])
|
bg=self.colors['bg_light'])
|
||||||
self.auth_label.pack(side=tk.LEFT, padx=(5, 0))
|
self.auth_label.pack(side=tk.LEFT, padx=(5, 0))
|
||||||
|
|
||||||
# 刷新按钮
|
# 操作按钮:检查靠近设备状态,安装驱动在检查右侧
|
||||||
self.btn_refresh = tk.Button(status_bar_frame, text=self.t('btn_refresh'),
|
status_actions_frame = tk.Frame(status_bar_frame, bg=self.colors['bg_light'])
|
||||||
|
status_actions_frame.pack(side=tk.RIGHT, padx=10, pady=5)
|
||||||
|
|
||||||
|
self.btn_refresh = tk.Button(status_actions_frame, text=self.t('btn_refresh'),
|
||||||
command=lambda: self.refresh_device_status(force=True),
|
command=lambda: self.refresh_device_status(force=True),
|
||||||
font=('Microsoft YaHei', 8),
|
font=('Microsoft YaHei', 8),
|
||||||
fg=self.colors['accent'],
|
fg=self.colors['accent'],
|
||||||
bg=self.colors['bg_light'],
|
bg=self.colors['bg_light'],
|
||||||
relief=tk.FLAT,
|
relief=tk.FLAT,
|
||||||
cursor='hand2')
|
cursor='hand2')
|
||||||
self.btn_refresh.pack(side=tk.RIGHT, padx=10, pady=5)
|
self.btn_refresh.pack(side=tk.LEFT, padx=(0, 8))
|
||||||
|
|
||||||
|
self.btn_install_driver = tk.Button(status_actions_frame, text=self.t('btn_install_driver'),
|
||||||
|
command=self.install_fastboot_driver,
|
||||||
|
font=('Microsoft YaHei', 8),
|
||||||
|
fg=self.colors['warning'],
|
||||||
|
bg=self.colors['bg_light'],
|
||||||
|
relief=tk.FLAT,
|
||||||
|
cursor='hand2')
|
||||||
|
self.btn_install_driver.pack(side=tk.LEFT)
|
||||||
|
|
||||||
# 解压进度条框架
|
# 解压进度条框架
|
||||||
progress_frame = tk.Frame(left_frame, bg=self.colors['bg_dark'])
|
progress_frame = tk.Frame(left_frame, bg=self.colors['bg_dark'])
|
||||||
@@ -1222,6 +1309,7 @@ class ADKAPKGUI:
|
|||||||
(getattr(self, 'vin_label_title', None), 'vin_label', None),
|
(getattr(self, 'vin_label_title', None), 'vin_label', None),
|
||||||
(getattr(self, 'auth_label_title', None), 'auth_label', None),
|
(getattr(self, 'auth_label_title', None), 'auth_label', None),
|
||||||
(getattr(self, 'btn_refresh', None), 'btn_refresh', None),
|
(getattr(self, 'btn_refresh', None), 'btn_refresh', None),
|
||||||
|
(getattr(self, 'btn_install_driver', None), 'btn_install_driver', None),
|
||||||
(getattr(self, 'hint_label', None), 'hint_factory', None),
|
(getattr(self, 'hint_label', None), 'hint_factory', None),
|
||||||
(getattr(self, 'pwd_query_label', None), 'pwd_query_label', None),
|
(getattr(self, 'pwd_query_label', None), 'pwd_query_label', None),
|
||||||
(getattr(self, 'btn_hotspot', None), 'hotspot_start', None),
|
(getattr(self, 'btn_hotspot', None), 'hotspot_start', None),
|
||||||
@@ -1246,10 +1334,28 @@ class ADKAPKGUI:
|
|||||||
self._update_device_status_impl(self.device_connected, self.vin,
|
self._update_device_status_impl(self.device_connected, self.vin,
|
||||||
getattr(self, '_last_authorized', False))
|
getattr(self, '_last_authorized', False))
|
||||||
|
|
||||||
|
def _sanitize_user_log_message(self, message):
|
||||||
|
"""Hide low-level device command details from normal operator logs."""
|
||||||
|
text = str(message)
|
||||||
|
replacements = [
|
||||||
|
(r'adb(?:\.exe)?(?:\s+-d)?\s+shell(?:\s+root)?', '权限获取'),
|
||||||
|
(r'/debug_ramdisk/su(?:\s+-c)?', '权限获取'),
|
||||||
|
(r'\bsu\s+-c\b', '权限获取'),
|
||||||
|
(r'Shell 权限', '权限'),
|
||||||
|
(r'\bShell permission\b', 'permission'),
|
||||||
|
(r'\b[Rr]oot command\b', 'permission operation'),
|
||||||
|
(r'\b[Rr]oot\b', 'permission'),
|
||||||
|
]
|
||||||
|
for pattern, replacement in replacements:
|
||||||
|
text = re.sub(pattern, replacement, text)
|
||||||
|
return text
|
||||||
|
|
||||||
def _log_impl(self, message, level="INFO"):
|
def _log_impl(self, message, level="INFO"):
|
||||||
"""日志写入的实际实现(必须在主线程调用)"""
|
"""日志写入的实际实现(必须在主线程调用)"""
|
||||||
if not self.debug_mode and level in ("INFO", "CMD"):
|
if not self.debug_mode and level in ("INFO", "CMD"):
|
||||||
return
|
return
|
||||||
|
if not self.debug_mode:
|
||||||
|
message = self._sanitize_user_log_message(message)
|
||||||
timestamp = datetime.now().strftime("%H:%M:%S")
|
timestamp = datetime.now().strftime("%H:%M:%S")
|
||||||
log_entry = f"[{timestamp}] [{level}] {message}\n"
|
log_entry = f"[{timestamp}] [{level}] {message}\n"
|
||||||
self.log_text.insert(tk.END, log_entry, level)
|
self.log_text.insert(tk.END, log_entry, level)
|
||||||
@@ -1373,11 +1479,11 @@ class ADKAPKGUI:
|
|||||||
threading.Thread(target=monitor, daemon=True).start()
|
threading.Thread(target=monitor, daemon=True).start()
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# 核心:adb shell 自动密码输入
|
# 核心:设备权限命令自动密码输入
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
||||||
def run_adb_shell(self, shell_command, timeout=15):
|
def run_adb_shell(self, shell_command, timeout=15):
|
||||||
"""执行 adb shell 命令,自动静默输入设备密码 adb36987。
|
"""执行设备权限命令,自动静默输入设备密码 adb36987。
|
||||||
静默执行,不显示 adb 原始输出,仅返回结果。"""
|
静默执行,不显示 adb 原始输出,仅返回结果。"""
|
||||||
if self.debug_mode:
|
if self.debug_mode:
|
||||||
self.log(f"CMD: adb shell {shell_command}", "CMD")
|
self.log(f"CMD: adb shell {shell_command}", "CMD")
|
||||||
@@ -1461,79 +1567,70 @@ class ADKAPKGUI:
|
|||||||
ok, output = self.run_root_command("id", timeout=20)
|
ok, output = self.run_root_command("id", timeout=20)
|
||||||
if ok and "uid=0" in output:
|
if ok and "uid=0" in output:
|
||||||
self.log(self.t('log_root_ok'), "STATUS")
|
self.log(self.t('log_root_ok'), "STATUS")
|
||||||
|
self.run_adb_shell('pm uninstall --user 0 com.topjohnwu.magisk', timeout=30)
|
||||||
return True
|
return True
|
||||||
self.log(self.t('log_root_failed'), "ERROR")
|
self.log(self.t('log_root_failed'), "ERROR")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def install_magisk_module_files(self, module_dir, skip_names=None):
|
def disable_and_uninstall_preinstalled_packages(self):
|
||||||
module_dir = Path(module_dir)
|
self.run_adb_shell('pm disable-user com.incall.apps.softmanager', timeout=30)
|
||||||
skip_names = set(skip_names or [])
|
package_names = [
|
||||||
mod_id = self.read_module_id(module_dir)
|
"com.carcontrolhome.app",
|
||||||
|
"com.tinnove.netease.music",
|
||||||
|
"com.wtcl.electronicdirections",
|
||||||
|
]
|
||||||
|
for package_name in package_names:
|
||||||
|
self.run_adb_shell(f'pm disable-user {package_name}', timeout=30)
|
||||||
|
self.run_adb_shell(f'pm uninstall --user 0 {package_name}', timeout=30)
|
||||||
|
|
||||||
|
def install_magisk_module_zip(self, zip_path):
|
||||||
|
zip_path = Path(zip_path)
|
||||||
|
mod_id = self.read_module_id_from_zip(zip_path)
|
||||||
if not mod_id:
|
if not mod_id:
|
||||||
return False, self.t('err_module_id_missing')
|
return False, self.t('err_module_id_missing')
|
||||||
|
if not zip_path.exists():
|
||||||
|
return False, self.tf('err_extract_failed', error=f"{zip_path} not found")
|
||||||
|
|
||||||
device_stage = f"/data/local/tmp/q05_lidar_modules/{mod_id}"
|
device_stage_dir = "/data/local/tmp/q05_lidar_modules"
|
||||||
|
device_zip = f"{device_stage_dir}/{mod_id}.zip"
|
||||||
device_module = f"/data/adb/modules/{mod_id}"
|
device_module = f"/data/adb/modules/{mod_id}"
|
||||||
files = [
|
|
||||||
p for p in module_dir.rglob("*")
|
|
||||||
if p.is_file()
|
|
||||||
and not self._is_skipped_module_path(module_dir, p, skip_names)
|
|
||||||
]
|
|
||||||
if not files:
|
|
||||||
return False, self.t('err_no_apks')
|
|
||||||
|
|
||||||
self.log(self.tf('log_module_install_start', module=mod_id), "STATUS")
|
self.log(self.t('log_module_install_start'), "STATUS")
|
||||||
commands = [
|
commands = [
|
||||||
f"rm -rf {self.shell_quote(device_stage)} {self.shell_quote(device_module)}",
|
f"rm -rf {self.shell_quote(device_module)}",
|
||||||
f"mkdir -p {self.shell_quote(device_stage)} {self.shell_quote(device_module)}",
|
f"mkdir -p {self.shell_quote(device_stage_dir)} {self.shell_quote(device_module)}",
|
||||||
f"chmod -R 777 {self.shell_quote(device_stage)}",
|
f"chmod 777 {self.shell_quote(device_stage_dir)}",
|
||||||
]
|
]
|
||||||
for command in commands:
|
for command in commands:
|
||||||
ok, output = self.run_root_command(command, timeout=60)
|
ok, output = self.run_root_command(command, timeout=60)
|
||||||
if not ok:
|
if not ok:
|
||||||
return False, self.tf('log_root_cmd_failed', command=command, error=output)
|
return False, self.tf('log_root_cmd_failed', command=command, error=output)
|
||||||
|
|
||||||
total = len(files)
|
ok, output = self.run_adb_command(f'adb -d push "{zip_path}" {device_zip}')
|
||||||
for idx, local_file in enumerate(files, 1):
|
if not ok:
|
||||||
rel = local_file.relative_to(module_dir).as_posix()
|
return False, self.tf('log_push_file_failed', file=zip_path.name, error=output)
|
||||||
remote_file = f"{device_stage}/{rel}"
|
|
||||||
remote_parent = remote_file.rsplit("/", 1)[0]
|
|
||||||
ok, output = self.run_root_command(f"mkdir -p {self.shell_quote(remote_parent)}", timeout=30)
|
|
||||||
if not ok:
|
|
||||||
return False, self.tf('log_root_cmd_failed', command="mkdir", error=output)
|
|
||||||
ok, output = self.run_root_command(f"chmod 777 {self.shell_quote(remote_parent)}", timeout=30)
|
|
||||||
if not ok:
|
|
||||||
return False, self.tf('log_root_cmd_failed', command="chmod", error=output)
|
|
||||||
ok, output = self.run_adb_command(f'adb -d push "{local_file}" {remote_file}')
|
|
||||||
if not ok:
|
|
||||||
return False, self.tf('log_push_file_failed', file=rel, error=output)
|
|
||||||
self.update_progress(idx, total, self.tf('progress_push_files', current=idx, total=total), is_push=True)
|
|
||||||
|
|
||||||
install_commands = [
|
install_commands = [
|
||||||
f"cp -af {self.shell_quote(device_stage)}/. {self.shell_quote(device_module)}/",
|
f"unzip -oq {self.shell_quote(device_zip)} -x 'META-INF/*' -d {self.shell_quote(device_module)}",
|
||||||
f"find {self.shell_quote(device_module)} -type d -exec chmod 755 {{}} \\;",
|
f"find {self.shell_quote(device_module)} -type d -exec chmod 755 {{}} \\;",
|
||||||
f"find {self.shell_quote(device_module)} -type f -exec chmod 644 {{}} \\;",
|
f"find {self.shell_quote(device_module)} -type f -exec chmod 644 {{}} \\;",
|
||||||
f"find {self.shell_quote(device_module)} -type f -name '*.sh' -exec chmod 755 {{}} \\;",
|
f"find {self.shell_quote(device_module)} -type f -name '*.sh' -exec chmod 755 {{}} \\;",
|
||||||
f"rm -rf {self.shell_quote(device_stage)}",
|
f"rm -f {self.shell_quote(device_zip)}",
|
||||||
]
|
]
|
||||||
for command in install_commands:
|
for command in install_commands:
|
||||||
ok, output = self.run_root_command(command, timeout=120)
|
ok, output = self.run_root_command(command, timeout=300)
|
||||||
if not ok:
|
if not ok:
|
||||||
return False, self.tf('log_root_cmd_failed', command=command, error=output)
|
return False, self.tf('log_root_cmd_failed', command=command, error=output)
|
||||||
|
|
||||||
self.log(self.tf('log_module_install_done', module=mod_id), "STATUS")
|
self.log(self.t('log_module_install_done'), "STATUS")
|
||||||
return True, mod_id
|
return True, mod_id
|
||||||
|
|
||||||
def _is_skipped_module_path(self, module_dir, file_path, skip_names):
|
|
||||||
rel = file_path.relative_to(module_dir)
|
|
||||||
parts = rel.parts
|
|
||||||
if parts and (parts[0] == "META-INF" or parts[0].startswith("_")):
|
|
||||||
return True
|
|
||||||
return file_path.name in skip_names
|
|
||||||
|
|
||||||
def check_package_extracted(self):
|
def check_package_extracted(self):
|
||||||
"""检查语言包是否已解压"""
|
"""检查语言包是否已解压"""
|
||||||
has_module = self.module_dir and self.module_dir.exists()
|
has_module = bool(getattr(self, 'module_zips', None))
|
||||||
|
if not has_module and self.temp_dir and self.temp_dir.exists():
|
||||||
|
self.module_zips = self.find_module_zips(self.temp_dir)
|
||||||
|
has_module = bool(self.module_zips)
|
||||||
if has_module:
|
if has_module:
|
||||||
ok, reason = self._validate_extracted_modules()
|
ok, reason = self._validate_extracted_modules()
|
||||||
if not ok:
|
if not ok:
|
||||||
@@ -1543,22 +1640,16 @@ class ADKAPKGUI:
|
|||||||
return has_module
|
return has_module
|
||||||
|
|
||||||
def _validate_extracted_modules(self):
|
def _validate_extracted_modules(self):
|
||||||
if not self.module_dir or not self.module_dir.exists():
|
if not getattr(self, 'module_zips', None):
|
||||||
return False, self.t('err_module_prop_missing')
|
return False, self.t('err_module_prop_missing')
|
||||||
if not (self.module_dir / "module.prop").exists():
|
module_ids = []
|
||||||
return False, self.t('err_module_prop_missing')
|
for zip_path in self.module_zips:
|
||||||
if not (self.module_dir / "system").exists():
|
mod_id = self.read_module_id_from_zip(zip_path)
|
||||||
return False, self.t('err_system_dir_missing')
|
if not mod_id:
|
||||||
if not (self.module_dir / "disable-wireless-adb-vecentek-magisk.zip").exists():
|
return False, self.tf('err_module_zip_invalid', file=zip_path.name)
|
||||||
return False, self.t('err_disable_module_missing')
|
module_ids.append(mod_id)
|
||||||
main_id = self.read_module_id(self.module_dir)
|
if len(set(module_ids)) != len(module_ids):
|
||||||
if not main_id:
|
return False, self.t('err_duplicate_module_id')
|
||||||
return False, self.t('err_module_id_missing')
|
|
||||||
if not self.disable_module_dir or not self.disable_module_dir.exists():
|
|
||||||
return False, self.t('err_disable_module_invalid')
|
|
||||||
extra_id = self.read_module_id(self.disable_module_dir)
|
|
||||||
if not extra_id:
|
|
||||||
return False, self.t('err_disable_module_invalid')
|
|
||||||
return True, ""
|
return True, ""
|
||||||
|
|
||||||
def _clear_extracted_cache(self):
|
def _clear_extracted_cache(self):
|
||||||
@@ -1566,8 +1657,7 @@ class ADKAPKGUI:
|
|||||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
self.apps_dir = None
|
self.apps_dir = None
|
||||||
self.module_dir = None
|
self.module_zips = []
|
||||||
self.disable_module_dir = None
|
|
||||||
|
|
||||||
def cleanup_cache_on_exit(self):
|
def cleanup_cache_on_exit(self):
|
||||||
self._clear_extracted_cache()
|
self._clear_extracted_cache()
|
||||||
@@ -1650,20 +1740,27 @@ class ADKAPKGUI:
|
|||||||
output = self._decode_process_output((result.stderr or b'') + (result.stdout or b''))
|
output = self._decode_process_output((result.stderr or b'') + (result.stdout or b''))
|
||||||
return result.returncode == 0, output
|
return result.returncode == 0, output
|
||||||
|
|
||||||
def _find_main_module_dir(self, root_dir):
|
def find_module_zips(self, root_dir):
|
||||||
candidates = [root_dir]
|
root_dir = Path(root_dir)
|
||||||
candidates.extend([p for p in root_dir.iterdir() if p.is_dir()])
|
candidates = []
|
||||||
for candidate in candidates:
|
modules_dir = root_dir / "modules"
|
||||||
if (candidate / "module.prop").exists() and (candidate / "system").exists():
|
search_roots = [modules_dir] if modules_dir.exists() else [root_dir]
|
||||||
return candidate
|
for search_root in search_roots:
|
||||||
return None
|
candidates.extend(sorted(search_root.rglob("*.zip")))
|
||||||
|
module_zips = []
|
||||||
|
for zip_path in candidates:
|
||||||
|
if self.read_module_id_from_zip(zip_path):
|
||||||
|
module_zips.append(zip_path)
|
||||||
|
return module_zips
|
||||||
|
|
||||||
def read_module_id(self, module_dir):
|
def read_module_id_from_zip(self, zip_path):
|
||||||
prop_path = Path(module_dir) / "module.prop"
|
|
||||||
if not prop_path.exists():
|
|
||||||
return ""
|
|
||||||
try:
|
try:
|
||||||
for line in prop_path.read_text(encoding='utf-8', errors='replace').splitlines():
|
with zipfile.ZipFile(zip_path, 'r') as zf:
|
||||||
|
prop_name = self._find_module_prop_in_zip(zf)
|
||||||
|
if not prop_name:
|
||||||
|
return ""
|
||||||
|
raw = zf.read(prop_name)
|
||||||
|
for line in raw.decode('utf-8', errors='replace').splitlines():
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if line.startswith("id="):
|
if line.startswith("id="):
|
||||||
return line.split("=", 1)[1].strip()
|
return line.split("=", 1)[1].strip()
|
||||||
@@ -1671,6 +1768,25 @@ class ADKAPKGUI:
|
|||||||
return ""
|
return ""
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
def _find_module_prop_in_zip(self, zf):
|
||||||
|
names = zf.namelist()
|
||||||
|
normalized = {}
|
||||||
|
for name in names:
|
||||||
|
clean = name.replace("\\", "/").lstrip("./")
|
||||||
|
normalized[clean] = name
|
||||||
|
if "module.prop" in normalized:
|
||||||
|
return normalized["module.prop"]
|
||||||
|
|
||||||
|
candidates = []
|
||||||
|
for clean, original in normalized.items():
|
||||||
|
parts = [part for part in clean.split("/") if part]
|
||||||
|
if len(parts) == 2 and parts[-1] == "module.prop":
|
||||||
|
candidates.append((len(parts), original))
|
||||||
|
if candidates:
|
||||||
|
candidates.sort()
|
||||||
|
return candidates[0][1]
|
||||||
|
return ""
|
||||||
|
|
||||||
def extract_package_silent(self):
|
def extract_package_silent(self):
|
||||||
"""静默解压语言包(带进度)—— Q05-Lidar 使用 Magisk 模块结构"""
|
"""静默解压语言包(带进度)—— Q05-Lidar 使用 Magisk 模块结构"""
|
||||||
if not self.package_file.exists():
|
if not self.package_file.exists():
|
||||||
@@ -1720,37 +1836,18 @@ class ADKAPKGUI:
|
|||||||
return False
|
return False
|
||||||
self.update_progress(1, 1, self.t('progress_resource_done'))
|
self.update_progress(1, 1, self.t('progress_resource_done'))
|
||||||
|
|
||||||
self.module_dir = self._find_main_module_dir(self.temp_dir)
|
self.module_zips = self.find_module_zips(self.temp_dir)
|
||||||
if not self.module_dir:
|
if not self.module_zips:
|
||||||
self.log(self.t('err_module_prop_missing'), "ERROR")
|
self.log(self.t('err_module_prop_missing'), "ERROR")
|
||||||
self._clear_extracted_cache()
|
self._clear_extracted_cache()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
disable_zip = self.module_dir / "disable-wireless-adb-vecentek-magisk.zip"
|
|
||||||
self.disable_module_dir = self.temp_dir / "_disable_wireless_adb_vecentek"
|
|
||||||
if self.disable_module_dir.exists():
|
|
||||||
shutil.rmtree(self.disable_module_dir, ignore_errors=True)
|
|
||||||
self.disable_module_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
if not disable_zip.exists():
|
|
||||||
self.log(self.t('err_disable_module_missing'), "ERROR")
|
|
||||||
self._clear_extracted_cache()
|
|
||||||
return False
|
|
||||||
ok, err_msg = self._run_7za_extract_basic(disable_zip, self.disable_module_dir)
|
|
||||||
if not ok:
|
|
||||||
self.log(self.tf('err_extract_failed', error=err_msg[:300]), "ERROR")
|
|
||||||
self._clear_extracted_cache()
|
|
||||||
return False
|
|
||||||
|
|
||||||
ok, reason = self._validate_extracted_modules()
|
ok, reason = self._validate_extracted_modules()
|
||||||
if not ok:
|
if not ok:
|
||||||
self.log(self.tf('log_extracted_resource_invalid', reason=reason), "ERROR")
|
self.log(self.tf('log_extracted_resource_invalid', reason=reason), "ERROR")
|
||||||
self._clear_extracted_cache()
|
self._clear_extracted_cache()
|
||||||
return False
|
return False
|
||||||
self.log(self.tf(
|
self.log(self.t('log_module_resource_ready'), "INFO")
|
||||||
'log_module_resource_ready',
|
|
||||||
main=self.read_module_id(self.module_dir),
|
|
||||||
extra=self.read_module_id(self.disable_module_dir)
|
|
||||||
), "INFO")
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -1779,6 +1876,154 @@ class ADKAPKGUI:
|
|||||||
self.log(self.t('log_adb_missing'), "ERROR")
|
self.log(self.t('log_adb_missing'), "ERROR")
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
self.log(self.t('log_adb_missing'), "ERROR")
|
self.log(self.t('log_adb_missing'), "ERROR")
|
||||||
|
self.root.after(800, self.check_fastboot_driver_on_startup)
|
||||||
|
|
||||||
|
def _run_command_capture(self, command, timeout=30):
|
||||||
|
creationflags = subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
|
||||||
|
result = subprocess.run(
|
||||||
|
command,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=timeout,
|
||||||
|
creationflags=creationflags
|
||||||
|
)
|
||||||
|
output = (result.stdout or "") + (result.stderr or "")
|
||||||
|
return result.returncode, output.strip()
|
||||||
|
|
||||||
|
def ask_ok_cancel_on_ui_thread(self, title, message):
|
||||||
|
result = {"value": False}
|
||||||
|
done = threading.Event()
|
||||||
|
|
||||||
|
def prompt():
|
||||||
|
try:
|
||||||
|
result["value"] = messagebox.askokcancel(title, message, parent=self.root)
|
||||||
|
finally:
|
||||||
|
done.set()
|
||||||
|
|
||||||
|
self.run_on_ui_thread(prompt)
|
||||||
|
done.wait()
|
||||||
|
return result["value"]
|
||||||
|
|
||||||
|
def is_fastboot_driver_installed(self):
|
||||||
|
if sys.platform != 'win32':
|
||||||
|
return True, ""
|
||||||
|
try:
|
||||||
|
code, output = self._run_command_capture(['pnputil', '/enum-drivers'], timeout=40)
|
||||||
|
if code != 0:
|
||||||
|
return False, output or "pnputil enum failed"
|
||||||
|
normalized = output.lower()
|
||||||
|
installed = (
|
||||||
|
'android_winusb.inf'.lower() in normalized
|
||||||
|
or 'android bootloader interface' in normalized
|
||||||
|
or 'android adb interface' in normalized
|
||||||
|
or 'fastboot' in normalized
|
||||||
|
)
|
||||||
|
return installed, output
|
||||||
|
except Exception as e:
|
||||||
|
return False, str(e)
|
||||||
|
|
||||||
|
def _install_driver_with_uac(self, inf_path):
|
||||||
|
quoted_inf = str(inf_path).replace("'", "''")
|
||||||
|
ps_command = (
|
||||||
|
"$proc = Start-Process -FilePath pnputil "
|
||||||
|
"-ArgumentList @('/add-driver', '{0}', '/install') "
|
||||||
|
"-Verb RunAs -WindowStyle Hidden -PassThru; "
|
||||||
|
"if ($null -eq $proc) {{ exit 1 }}; "
|
||||||
|
"$proc.WaitForExit(); "
|
||||||
|
"Write-Output $proc.ExitCode"
|
||||||
|
).format(quoted_inf)
|
||||||
|
creationflags = subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
|
||||||
|
result = subprocess.run(
|
||||||
|
['powershell', '-NoProfile', '-Command', ps_command],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
creationflags=creationflags
|
||||||
|
)
|
||||||
|
output = ((result.stdout or "") + (result.stderr or "")).strip()
|
||||||
|
exit_code = None
|
||||||
|
for line in reversed(output.splitlines()):
|
||||||
|
text = line.strip()
|
||||||
|
if text.isdigit():
|
||||||
|
exit_code = int(text)
|
||||||
|
break
|
||||||
|
if exit_code is None and result.returncode == 0:
|
||||||
|
exit_code = 0
|
||||||
|
return exit_code == 0, output or f"powershell exit={result.returncode}"
|
||||||
|
|
||||||
|
def install_fastboot_driver(self, prompt=True):
|
||||||
|
def worker():
|
||||||
|
try:
|
||||||
|
if not self.driver_inf.exists():
|
||||||
|
error = self.tf('msg_driver_install_failed', error=f"{self.driver_inf} not found")
|
||||||
|
self.log(self.tf('log_driver_install_failed', error=f"{self.driver_inf} not found"), "ERROR")
|
||||||
|
self.run_on_ui_thread(lambda: messagebox.showerror(self.t('msg_driver_missing_title'), error))
|
||||||
|
return
|
||||||
|
|
||||||
|
if prompt:
|
||||||
|
confirmed = self.ask_ok_cancel_on_ui_thread(
|
||||||
|
self.t('msg_driver_missing_title'),
|
||||||
|
self.t('msg_driver_install_confirm')
|
||||||
|
)
|
||||||
|
if not confirmed:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.log(self.t('log_driver_install_start'), "STATUS")
|
||||||
|
ok, output = self._install_driver_with_uac(self.driver_inf)
|
||||||
|
if not ok:
|
||||||
|
self.log(self.tf('log_driver_install_failed', error=output or self.t('unknown_error')), "ERROR")
|
||||||
|
self.run_on_ui_thread(
|
||||||
|
lambda: messagebox.showerror(
|
||||||
|
self.t('msg_driver_missing_title'),
|
||||||
|
self.tf('msg_driver_install_failed', error=output or self.t('unknown_error')),
|
||||||
|
parent=self.root
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.log(self.t('log_driver_install_success'), "SUCCESS")
|
||||||
|
self.run_on_ui_thread(
|
||||||
|
lambda: messagebox.showinfo(
|
||||||
|
self.t('msg_driver_missing_title'),
|
||||||
|
self.t('msg_driver_install_done'),
|
||||||
|
parent=self.root
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
self.log(self.tf('log_driver_install_failed', error=str(e)), "ERROR")
|
||||||
|
self.run_on_ui_thread(
|
||||||
|
lambda: messagebox.showerror(
|
||||||
|
self.t('msg_driver_missing_title'),
|
||||||
|
self.tf('msg_driver_install_failed', error=str(e)),
|
||||||
|
parent=self.root
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
threading.Thread(target=worker, daemon=True).start()
|
||||||
|
|
||||||
|
def check_fastboot_driver_on_startup(self):
|
||||||
|
if self.driver_prompted:
|
||||||
|
return
|
||||||
|
self.driver_prompted = True
|
||||||
|
|
||||||
|
def worker():
|
||||||
|
installed, detail = self.is_fastboot_driver_installed()
|
||||||
|
if installed:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.debug_mode and detail:
|
||||||
|
self.log(detail[:500], "CMD")
|
||||||
|
|
||||||
|
def notify_and_install():
|
||||||
|
messagebox.showinfo(
|
||||||
|
self.t('msg_driver_missing_title'),
|
||||||
|
self.t('msg_driver_missing'),
|
||||||
|
parent=self.root
|
||||||
|
)
|
||||||
|
self.install_fastboot_driver(prompt=False)
|
||||||
|
|
||||||
|
self.run_on_ui_thread(notify_and_install)
|
||||||
|
|
||||||
|
threading.Thread(target=worker, daemon=True).start()
|
||||||
|
|
||||||
def refresh_device_status(self, force=False):
|
def refresh_device_status(self, force=False):
|
||||||
"""刷新设备状态 —— 逸动版使用 ca.car.vin 获取 VIN"""
|
"""刷新设备状态 —— 逸动版使用 ca.car.vin 获取 VIN"""
|
||||||
@@ -2184,6 +2429,10 @@ class ADKAPKGUI:
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def fastboot_output_has_okay(self, output):
|
||||||
|
text = str(output or "").upper()
|
||||||
|
return "OKAY" in text and "FAILED" not in text
|
||||||
|
|
||||||
def wait_for_fastboot(self, timeout=180, interval=5):
|
def wait_for_fastboot(self, timeout=180, interval=5):
|
||||||
deadline = time.time() + timeout
|
deadline = time.time() + timeout
|
||||||
while time.time() < deadline:
|
while time.time() < deadline:
|
||||||
@@ -2236,9 +2485,9 @@ class ADKAPKGUI:
|
|||||||
self.update_progress(4, 7, self.t('progress_reboot_fastboot'), is_push=True)
|
self.update_progress(4, 7, self.t('progress_reboot_fastboot'), is_push=True)
|
||||||
ok, output = self.run_adb_shell('reboot fastboot')
|
ok, output = self.run_adb_shell('reboot fastboot')
|
||||||
if not ok:
|
if not ok:
|
||||||
self.log(self.tf('log_fastboot_enter_failed', output=output), "ERROR")
|
if self.debug_mode:
|
||||||
return
|
self.log(self.tf('log_fastboot_enter_failed', output=output), "CMD")
|
||||||
self.log(self.t('log_fastboot_wait'), "INFO")
|
self.log(self.t('log_fastboot_wait'), "WARNING")
|
||||||
if not self.wait_for_fastboot():
|
if not self.wait_for_fastboot():
|
||||||
self.log(self.t('log_fastboot_missing'), "ERROR")
|
self.log(self.t('log_fastboot_missing'), "ERROR")
|
||||||
return
|
return
|
||||||
@@ -2250,7 +2499,7 @@ class ADKAPKGUI:
|
|||||||
|
|
||||||
self.update_progress(6, 7, self.t('progress_flash_init_boot'), is_push=True)
|
self.update_progress(6, 7, self.t('progress_flash_init_boot'), is_push=True)
|
||||||
ok, output = self.run_fastboot_command(['flash', 'init_boot', str(temp_img)], timeout=120)
|
ok, output = self.run_fastboot_command(['flash', 'init_boot', str(temp_img)], timeout=120)
|
||||||
if not ok:
|
if not ok or not self.fastboot_output_has_okay(output):
|
||||||
self.log(self.tf('log_init_boot_flash_failed', output=output), "ERROR")
|
self.log(self.tf('log_init_boot_flash_failed', output=output), "ERROR")
|
||||||
return
|
return
|
||||||
self.log(self.t('log_init_boot_flash_success'), "INFO")
|
self.log(self.t('log_init_boot_flash_success'), "INFO")
|
||||||
@@ -2326,7 +2575,7 @@ class ADKAPKGUI:
|
|||||||
return
|
return
|
||||||
self.show_progress(False, is_push=False)
|
self.show_progress(False, is_push=False)
|
||||||
|
|
||||||
if not self.module_dir or not self.module_dir.exists():
|
if not getattr(self, 'module_zips', None):
|
||||||
self.run_on_ui_thread(lambda: messagebox.showerror(self.t('msg_error_title'), self.t('msg_resource_dir_missing')))
|
self.run_on_ui_thread(lambda: messagebox.showerror(self.t('msg_error_title'), self.t('msg_resource_dir_missing')))
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -2337,25 +2586,18 @@ class ADKAPKGUI:
|
|||||||
if not self.open_magisk_and_check_root():
|
if not self.open_magisk_and_check_root():
|
||||||
self.run_on_ui_thread(lambda: messagebox.showerror(self.t('msg_auth_failed_title'), self.t('msg_root_failed')))
|
self.run_on_ui_thread(lambda: messagebox.showerror(self.t('msg_auth_failed_title'), self.t('msg_root_failed')))
|
||||||
return
|
return
|
||||||
|
self.disable_and_uninstall_preinstalled_packages()
|
||||||
|
|
||||||
main_id = self.read_module_id(self.module_dir)
|
total = len(self.module_zips)
|
||||||
self.update_progress(0, 2, self.tf('progress_install_module', module=main_id), is_push=True)
|
for idx, zip_path in enumerate(self.module_zips, 1):
|
||||||
ok, result = self.install_magisk_module_files(
|
mod_id = self.read_module_id_from_zip(zip_path)
|
||||||
self.module_dir,
|
self.update_progress(idx - 1, total, self.t('progress_install_module'), is_push=True)
|
||||||
skip_names={"disable-wireless-adb-vecentek-magisk.zip"}
|
ok, result = self.install_magisk_module_zip(zip_path)
|
||||||
)
|
if not ok:
|
||||||
if not ok:
|
self.log(self.tf('log_module_install_failed', error=result), "ERROR")
|
||||||
self.log(self.tf('log_module_install_failed', module=main_id or "main", error=result), "ERROR")
|
return
|
||||||
return
|
|
||||||
|
|
||||||
extra_id = self.read_module_id(self.disable_module_dir)
|
self.update_progress(total, total, self.t('progress_flash_done'), is_push=True)
|
||||||
self.update_progress(1, 2, self.tf('progress_install_module', module=extra_id), is_push=True)
|
|
||||||
ok, result = self.install_magisk_module_files(self.disable_module_dir)
|
|
||||||
if not ok:
|
|
||||||
self.log(self.tf('log_module_install_failed', module=extra_id or "extra", error=result), "ERROR")
|
|
||||||
return
|
|
||||||
|
|
||||||
self.update_progress(2, 2, self.t('progress_flash_done'), is_push=True)
|
|
||||||
self.log(self.t('log_push_success'), "SUCCESS")
|
self.log(self.t('log_push_success'), "SUCCESS")
|
||||||
finally:
|
finally:
|
||||||
self.show_progress(False, is_push=True)
|
self.show_progress(False, is_push=True)
|
||||||
@@ -2854,8 +3096,7 @@ class ADKAPKGUI:
|
|||||||
if self.extract_package_silent():
|
if self.extract_package_silent():
|
||||||
self.log(self.tf(
|
self.log(self.tf(
|
||||||
'log_package_extract_modules_success',
|
'log_package_extract_modules_success',
|
||||||
main=self.read_module_id(self.module_dir),
|
count=len(self.module_zips)
|
||||||
extra=self.read_module_id(self.disable_module_dir)
|
|
||||||
), "SUCCESS")
|
), "SUCCESS")
|
||||||
else:
|
else:
|
||||||
self.log(self.t('log_package_extract_failed'), "ERROR")
|
self.log(self.t('log_package_extract_failed'), "ERROR")
|
||||||
@@ -3147,6 +3388,7 @@ def main():
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
set_windows_app_id()
|
||||||
app = ADKAPKGUI()
|
app = ADKAPKGUI()
|
||||||
app.run()
|
app.run()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -116,11 +116,11 @@ if exist "%APPDIR%resource.dat" copy "%APPDIR%resource.dat" . >nul
|
|||||||
echo [5/6] PyInstaller...
|
echo [5/6] PyInstaller...
|
||||||
set ADD_RESOURCE=
|
set ADD_RESOURCE=
|
||||||
if exist "resource.dat" set ADD_RESOURCE=--add-data "resource.dat;."
|
if exist "resource.dat" set ADD_RESOURCE=--add-data "resource.dat;."
|
||||||
"%PY%" -m PyInstaller --onefile --windowed --name="%NAME%" --icon="app.ico" --add-data "adb.exe;." --add-data "AdbWinApi.dll;." --add-data "AdbWinUsbApi.dll;." --add-data "7za.exe;." --add-data "fastboot.exe;." %ADD_RESOURCE% --add-binary "_core.pyd;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=cryptography --collect-all tkinter --collect-all cryptography --uac-admin launcher.py
|
"%PY%" -m PyInstaller --onefile --windowed --name="%NAME%" --icon="app.ico" --add-data "app.ico;." --add-data "adb.exe;." --add-data "AdbWinApi.dll;." --add-data "AdbWinUsbApi.dll;." --add-data "7za.exe;." --add-data "fastboot.exe;." --add-data "%APPDIR%usb_driver;usb_driver" %ADD_RESOURCE% --add-binary "_core.pyd;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=cryptography --collect-all tkinter --collect-all cryptography --uac-admin launcher.py
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
cd ..
|
cd ..
|
||||||
echo [ERROR] PyInstaller failed
|
echo [ERROR] PyInstaller failed
|
||||||
pause
|
pause
|
||||||
exit /b
|
exit /b
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -157,6 +157,12 @@ if errorlevel 1 (
|
|||||||
pause
|
pause
|
||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
)
|
||||||
|
if not exist "%APPDIR%usb_driver\android_winusb.inf" (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] usb_driver\android_winusb.inf not found at %APPDIR%usb_driver
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
echo [7/7] Cleanup...
|
echo [7/7] Cleanup...
|
||||||
del /q _core.py _core.c _core.pyd %PYD% launcher.py setup_cython.py adb.exe AdbWinApi.dll AdbWinUsbApi.dll 7za.exe fastboot.exe resource.dat 2>nul
|
del /q _core.py _core.c _core.pyd %PYD% launcher.py setup_cython.py adb.exe AdbWinApi.dll AdbWinUsbApi.dll 7za.exe fastboot.exe resource.dat 2>nul
|
||||||
|
|||||||
|
After Width: | Height: | Size: 151 KiB |
@@ -1,95 +0,0 @@
|
|||||||
@echo off
|
|
||||||
chcp 65001 >nul
|
|
||||||
cd /d "%~dp0"
|
|
||||||
set "ROOT=%~dp0.."
|
|
||||||
set "TOOLS=%ROOT%\tools"
|
|
||||||
set NAME=启源Q07刷入工具
|
|
||||||
title %NAME% - Build
|
|
||||||
|
|
||||||
echo ============================================================
|
|
||||||
echo %NAME% - Cython Build
|
|
||||||
echo ============================================================
|
|
||||||
echo.
|
|
||||||
|
|
||||||
where python >nul 2>&1
|
|
||||||
if errorlevel 1 (
|
|
||||||
echo [ERROR] Python not found
|
|
||||||
pause
|
|
||||||
exit /b
|
|
||||||
)
|
|
||||||
for /f "delims=" %%i in ('where python') do set PY=%%i
|
|
||||||
echo Python: %PY%
|
|
||||||
|
|
||||||
echo [1/6] Installing deps...
|
|
||||||
%PY% -m pip install pyinstaller cython pyzipper -q
|
|
||||||
if errorlevel 1 (
|
|
||||||
%PY% -m pip install pyinstaller cython pyzipper -q -i https://pypi.tuna.tsinghua.edu.cn/simple
|
|
||||||
)
|
|
||||||
|
|
||||||
echo [2/6] Clean...
|
|
||||||
if exist "dist_cy" rmdir /s /q dist_cy 2>nul
|
|
||||||
if exist "build" rmdir /s /q build 2>nul
|
|
||||||
if exist "dist" rmdir /s /q dist 2>nul
|
|
||||||
|
|
||||||
echo [3/6] Cython compile...
|
|
||||||
mkdir dist_cy 2>nul
|
|
||||||
copy app.py dist_cy\_core.py >nul
|
|
||||||
|
|
||||||
%PY% -c "open('dist_cy/setup_cython.py','w').write('from setuptools import setup\nfrom Cython.Build import cythonize\nsetup(ext_modules=cythonize(\"_core.py\", compiler_directives={\"language_level\":\"3\"}))\n')"
|
|
||||||
|
|
||||||
cd dist_cy
|
|
||||||
%PY% setup_cython.py build_ext --inplace
|
|
||||||
if errorlevel 1 (
|
|
||||||
cd ..
|
|
||||||
echo [WARN] Cython failed, fallback
|
|
||||||
goto :NORMAL
|
|
||||||
)
|
|
||||||
|
|
||||||
for %%f in (_core*.pyd) do set PYD=%%f
|
|
||||||
if "%PYD%"=="" (
|
|
||||||
cd ..
|
|
||||||
echo [WARN] No pyd, fallback
|
|
||||||
goto :NORMAL
|
|
||||||
)
|
|
||||||
echo PYD: %PYD%
|
|
||||||
copy "%PYD%" _core.pyd >nul
|
|
||||||
|
|
||||||
%PY% -c "open('launcher.py','w').write('# -*- coding: utf-8 -*-\nfrom _core import main\nmain()\n')"
|
|
||||||
|
|
||||||
echo [4/6] Copy resources...
|
|
||||||
copy "%TOOLS%\adb.exe" . >nul
|
|
||||||
copy "%TOOLS%\AdbWinApi.dll" . >nul
|
|
||||||
copy "%TOOLS%\AdbWinUsbApi.dll" . >nul
|
|
||||||
copy "%TOOLS%\7za.exe" . >nul
|
|
||||||
if exist "%ROOT%\app.ico" copy "%ROOT%\app.ico" . >nul
|
|
||||||
|
|
||||||
echo [5/6] PyInstaller...
|
|
||||||
%PY% -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ROOT%\app.ico" --add-data "%TOOLS%\adb.exe;." --add-data "%TOOLS%\AdbWinApi.dll;." --add-data "%TOOLS%\AdbWinUsbApi.dll;." --add-data "%TOOLS%\7za.exe;." --add-binary "_core.pyd;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --collect-all tkinter --uac-admin launcher.py
|
|
||||||
if errorlevel 1 (
|
|
||||||
cd ..
|
|
||||||
echo [ERROR] PyInstaller failed
|
|
||||||
pause
|
|
||||||
exit /b
|
|
||||||
)
|
|
||||||
|
|
||||||
echo [6/6] Cleanup...
|
|
||||||
del /q _core.py _core.c _core.pyd %PYD% launcher.py setup_cython.py 2>nul
|
|
||||||
rmdir /s /q build 2>nul
|
|
||||||
cd ..
|
|
||||||
goto :DONE
|
|
||||||
|
|
||||||
:NORMAL
|
|
||||||
echo [INFO] Normal PyInstaller...
|
|
||||||
%PY% -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ROOT%\app.ico" --add-data "%TOOLS%\adb.exe;." --add-data "%TOOLS%\AdbWinApi.dll;." --add-data "%TOOLS%\AdbWinUsbApi.dll;." --add-data "%TOOLS%\7za.exe;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --collect-all tkinter --uac-admin app.py
|
|
||||||
|
|
||||||
:DONE
|
|
||||||
echo.
|
|
||||||
echo Done.
|
|
||||||
if exist "dist_cy\dist\%NAME%.exe" (
|
|
||||||
echo Output: dist_cy\dist\%NAME%.exe
|
|
||||||
) else if exist "dist\%NAME%.exe" (
|
|
||||||
echo Output: dist\%NAME%.exe
|
|
||||||
) else (
|
|
||||||
echo Check dist folder
|
|
||||||
)
|
|
||||||
pause
|
|
||||||
@@ -3,8 +3,9 @@ chcp 65001 >nul
|
|||||||
cd /d "%~dp0"
|
cd /d "%~dp0"
|
||||||
set "ROOT=%~dp0.."
|
set "ROOT=%~dp0.."
|
||||||
set "TOOLS=%ROOT%\tools"
|
set "TOOLS=%ROOT%\tools"
|
||||||
set NAME=启源Q07刷入工具
|
set "ICON=%~dp0app.ico"
|
||||||
set SRC=app.py
|
set NAME=Qiyuan_Q07-multi-lan-installer
|
||||||
|
set SRC=Qiyuan_Q07-multi-lan-installer.py
|
||||||
title %NAME% - Build
|
title %NAME% - Build
|
||||||
|
|
||||||
echo ============================================================
|
echo ============================================================
|
||||||
@@ -27,14 +28,42 @@ if errorlevel 1 (
|
|||||||
%PY% -m pip install pyinstaller cython pyzipper -q -i https://pypi.tuna.tsinghua.edu.cn/simple
|
%PY% -m pip install pyinstaller cython pyzipper -q -i https://pypi.tuna.tsinghua.edu.cn/simple
|
||||||
)
|
)
|
||||||
|
|
||||||
echo [2/6] Clean...
|
echo [2/7] Clean...
|
||||||
if exist "dist_cy" rmdir /s /q dist_cy 2>nul
|
if exist "dist_cy" rmdir /s /q dist_cy 2>nul
|
||||||
if exist "build" rmdir /s /q build 2>nul
|
if exist "build" rmdir /s /q build 2>nul
|
||||||
if exist "dist" rmdir /s /q dist 2>nul
|
if exist "dist" rmdir /s /q dist 2>nul
|
||||||
|
|
||||||
echo [3/6] Cython compile...
|
echo [3/7] Checking resources...
|
||||||
|
if not exist "%ICON%" (
|
||||||
|
echo [ERROR] app.ico not found in Q07 folder: %ICON%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\adb.exe" (
|
||||||
|
echo [ERROR] Missing ADB file: %TOOLS%\adb.exe
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\AdbWinApi.dll" (
|
||||||
|
echo [ERROR] Missing ADB file: %TOOLS%\AdbWinApi.dll
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\AdbWinUsbApi.dll" (
|
||||||
|
echo [ERROR] Missing ADB file: %TOOLS%\AdbWinUsbApi.dll
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\7za.exe" (
|
||||||
|
echo [ERROR] Missing 7za file: %TOOLS%\7za.exe
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [4/7] Cython compile...
|
||||||
mkdir dist_cy 2>nul
|
mkdir dist_cy 2>nul
|
||||||
copy %SRC% dist_cy\_core.py >nul
|
copy %SRC% dist_cy\_core.py >nul
|
||||||
|
set "PYD="
|
||||||
|
|
||||||
%PY% -c "open('dist_cy/setup_cython.py','w').write('from setuptools import setup\nfrom Cython.Build import cythonize\nsetup(ext_modules=cythonize(\"_core.py\", compiler_directives={\"language_level\":\"3\"}))\n')"
|
%PY% -c "open('dist_cy/setup_cython.py','w').write('from setuptools import setup\nfrom Cython.Build import cythonize\nsetup(ext_modules=cythonize(\"_core.py\", compiler_directives={\"language_level\":\"3\"}))\n')"
|
||||||
|
|
||||||
@@ -42,54 +71,50 @@ cd dist_cy
|
|||||||
%PY% setup_cython.py build_ext --inplace
|
%PY% setup_cython.py build_ext --inplace
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
cd ..
|
cd ..
|
||||||
echo [WARN] Cython failed, fallback
|
echo [ERROR] Cython build failed. Install Microsoft C++ Build Tools and retry.
|
||||||
goto :NORMAL
|
pause
|
||||||
|
exit /b 1
|
||||||
)
|
)
|
||||||
|
|
||||||
for %%f in (_core*.pyd) do set PYD=%%f
|
for %%f in (_core*.pyd) do set PYD=%%f
|
||||||
if "%PYD%"=="" (
|
if "%PYD%"=="" (
|
||||||
cd ..
|
cd ..
|
||||||
echo [WARN] No pyd, fallback
|
echo [ERROR] Cython build did not generate _core*.pyd. Stop.
|
||||||
goto :NORMAL
|
pause
|
||||||
|
exit /b 1
|
||||||
)
|
)
|
||||||
echo PYD: %PYD%
|
echo PYD: %PYD%
|
||||||
copy "%PYD%" _core.pyd >nul
|
copy "%PYD%" _core.pyd >nul
|
||||||
|
|
||||||
%PY% -c "open('launcher.py','w').write('# -*- coding: utf-8 -*-\nfrom _core import main\nmain()\n')"
|
%PY% -c "open('launcher.py','w').write('# -*- coding: utf-8 -*-\nfrom _core import main\nmain()\n')"
|
||||||
|
|
||||||
echo [4/6] Copy resources...
|
echo [5/7] Copy resources...
|
||||||
copy "%TOOLS%\adb.exe" . >nul
|
copy "%TOOLS%\adb.exe" . >nul
|
||||||
copy "%TOOLS%\AdbWinApi.dll" . >nul
|
copy "%TOOLS%\AdbWinApi.dll" . >nul
|
||||||
copy "%TOOLS%\AdbWinUsbApi.dll" . >nul
|
copy "%TOOLS%\AdbWinUsbApi.dll" . >nul
|
||||||
copy "%TOOLS%\7za.exe" . >nul
|
copy "%TOOLS%\7za.exe" . >nul
|
||||||
if exist "%ROOT%\app.ico" copy "%ROOT%\app.ico" . >nul
|
copy "%ICON%" . >nul
|
||||||
|
|
||||||
echo [5/6] PyInstaller...
|
echo [6/7] PyInstaller...
|
||||||
%PY% -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ROOT%\app.ico" --add-data "%TOOLS%\adb.exe;." --add-data "%TOOLS%\AdbWinApi.dll;." --add-data "%TOOLS%\AdbWinUsbApi.dll;." --add-data "%TOOLS%\7za.exe;." --add-binary "_core.pyd;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin launcher.py
|
%PY% -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ICON%" --add-data "%ICON%;." --add-data "%TOOLS%\adb.exe;." --add-data "%TOOLS%\AdbWinApi.dll;." --add-data "%TOOLS%\AdbWinUsbApi.dll;." --add-data "%TOOLS%\7za.exe;." --add-binary "_core.pyd;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin launcher.py
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
cd ..
|
cd ..
|
||||||
echo [ERROR] PyInstaller failed
|
echo [ERROR] PyInstaller failed
|
||||||
pause
|
pause
|
||||||
exit /b
|
exit /b 1
|
||||||
)
|
)
|
||||||
|
|
||||||
echo [6/6] Cleanup...
|
echo [7/7] Cleanup...
|
||||||
del /q _core.py _core.c _core.pyd %PYD% launcher.py setup_cython.py 2>nul
|
del /q _core.py _core.c _core.pyd %PYD% launcher.py setup_cython.py 2>nul
|
||||||
rmdir /s /q build 2>nul
|
rmdir /s /q build 2>nul
|
||||||
cd ..
|
cd ..
|
||||||
goto :DONE
|
goto :DONE
|
||||||
|
|
||||||
:NORMAL
|
|
||||||
echo [INFO] Normal PyInstaller...
|
|
||||||
%PY% -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ROOT%\app.ico" --add-data "%TOOLS%\adb.exe;." --add-data "%TOOLS%\AdbWinApi.dll;." --add-data "%TOOLS%\AdbWinUsbApi.dll;." --add-data "%TOOLS%\7za.exe;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin %SRC%
|
|
||||||
|
|
||||||
:DONE
|
:DONE
|
||||||
echo.
|
echo.
|
||||||
echo Done.
|
echo Done.
|
||||||
if exist "dist_cy\dist\%NAME%.exe" (
|
if exist "dist_cy\dist\%NAME%.exe" (
|
||||||
echo Output: dist_cy\dist\%NAME%.exe
|
echo Output: dist_cy\dist\%NAME%.exe
|
||||||
) else if exist "dist\%NAME%.exe" (
|
|
||||||
echo Output: dist\%NAME%.exe
|
|
||||||
) else (
|
) else (
|
||||||
echo Check dist folder
|
echo Check dist folder
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
# language_installer
|
# language_installer
|
||||||
|
|
||||||
长安语言刷入工具套件,适用于启源Q07、深蓝S05、X5plus、长安逸动、UNI-Z、Mazda-EZ60 等 Android 车机多语言 APK 刷入/推送场景。
|
长安语言刷入工具套件,适用于启源Q07、深蓝S05、X5plus、长安逸动、UNI-Z、Mazda-EZ60 等 Android 车机多语言 APK 刷入/推送场景。
|
||||||
|
|
||||||
## 常用入口
|
## 常用入口
|
||||||
|
|
||||||
| 文件 | 用途 | 打包脚本 |
|
| 文件 | 用途 | 打包脚本 |
|
||||||
|------|------|----------|
|
|------|------|----------|
|
||||||
| `Q07/app.py` | 启源Q07 | `Q07/pack_q07.bat` |
|
| `Q07/app.py` | 启源Q07 | `Q07/pack_q07.bat` |
|
||||||
| `S05/S05.py` | 深蓝S05原版 | `S05/pack_s05.bat` |
|
| `S05/S05.py` | 深蓝S05原版 | `S05/pack_s05.bat` |
|
||||||
| `S05/S05_fixed.py` | 深蓝S05修复版 | `S05/pack_s05_fixed.bat` |
|
| `S05/S05_fixed.py` | 深蓝S05修复版 | `S05/pack_s05_fixed.bat` |
|
||||||
| `X5plus/X5plusTool.py` | X5plus | `X5plus/pack_x5plus.bat` |
|
| `X5plus/X5plusTool.py` | X5plus | `X5plus/pack_x5plus.bat` |
|
||||||
| `Yidong/app-install.py` | 长安逸动通用 | `Yidong/pack_common.bat` |
|
| `CS55-Q05/CS55-Q05_Installer.py` | CS55Plus/Q05 通用 | `CS55-Q05/pack_cs55_q05.bat` |
|
||||||
|
| `CS75Pro/CS75Pro_Installer.py` | CS75Pro | `CS75Pro/pack_cs75pro.bat` |
|
||||||
| `Yidong/app-yidong.py` | 长安逸动 | `Yidong/pack_yidong.bat` |
|
| `Yidong/app-yidong.py` | 长安逸动 | `Yidong/pack_yidong.bat` |
|
||||||
| `UNIZ/UNIZ.py` | UNI-Z 文件推送 | `UNIZ/pack_uniz.bat` |
|
| `UNIZ/UNIZ.py` | UNI-Z 文件推送 | `UNIZ/pack_uniz.bat` |
|
||||||
| `Mazda-EZ60/Mazda-EZ60.py` | Mazda-EZ60 OS 1.2 | `Mazda-EZ60/pack_mazda_ez60.bat` |
|
| `Mazda-EZ60/Mazda-EZ60_1.2.py` | Mazda-EZ60 OS 1.2 | `Mazda-EZ60/pack_mazda_ez60_1.2.bat` |
|
||||||
| `A07/Qiyuan_A07_Multi-lan-installer.py` | 启源A07 | `A07/pack_a07.bat` |
|
| `Mazda-EZ60/Mazda_EZ60-Language-Install_v1.0.py` | Mazda-EZ60 OS 1.0 | `Mazda-EZ60/pack_mazda_ez60_1.0.bat` |
|
||||||
| `Q05-Lidar/Q05-Lidar_Installer.py` | Q05_Lidar | `Q05-Lidar/pack_q05_lidar.bat` |
|
| `A07/Qiyuan_A07_Multi-lan-installer.py` | 启源A07 | `A07/pack_a07.bat` |
|
||||||
|
| `Q05-Lidar/Q05-Lidar_Installer.py` | Q05_Lidar | `Q05-Lidar/pack_q05_lidar.bat` |
|
||||||
公共工具如 `adb.exe`、`fastboot.exe`、`7za.exe` 及相关 DLL 统一放在根目录 `tools/` 管理;`app.ico`、`package.bin` 保留在仓库根目录。各车型脚本会自动回退查找这些资源。
|
|
||||||
|
公共工具如 `adb.exe`、`fastboot.exe`、`7za.exe` 及相关 DLL 统一放在根目录 `tools/` 管理;`app.ico`、`package.bin` 保留在仓库根目录。各车型脚本会自动回退查找这些资源。
|
||||||
详细维护说明见 `AGENTS.md` / `CLAUDE.md`。
|
|
||||||
|
详细维护说明见 `AGENTS.md` / `CLAUDE.md`。
|
||||||
|
|||||||
|
After Width: | Height: | Size: 158 KiB |
@@ -1,96 +1,160 @@
|
|||||||
@echo off
|
@echo off
|
||||||
chcp 65001 >nul
|
setlocal EnableExtensions
|
||||||
cd /d "%~dp0"
|
cd /d "%~dp0"
|
||||||
|
|
||||||
set "ROOT=%~dp0.."
|
set "ROOT=%~dp0.."
|
||||||
set "TOOLS=%ROOT%\tools"
|
set "TOOLS=%ROOT%\tools"
|
||||||
set NAME=深蓝S05刷入工具_fixed
|
set "NAME=Deepal_S05_Installer"
|
||||||
set SRC=S05_fixed.py
|
set "SRC=Deepal_S05.py"
|
||||||
title %NAME% - Build
|
set "ICON=%~dp0app.ico"
|
||||||
|
title %NAME% - Cython Build
|
||||||
|
|
||||||
echo ============================================================
|
echo ============================================================
|
||||||
echo %NAME% - Cython Build
|
echo %NAME% - Cython Build
|
||||||
echo ============================================================
|
echo ============================================================
|
||||||
echo.
|
echo.
|
||||||
|
|
||||||
where python >nul 2>&1
|
where python >nul 2>nul
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
echo [ERROR] Python not found
|
echo [ERROR] Python not found
|
||||||
pause
|
pause
|
||||||
exit /b
|
exit /b 1
|
||||||
)
|
)
|
||||||
for /f "delims=" %%i in ('where python') do set PY=%%i
|
for /f "delims=" %%i in ('where python') do set "PY=%%i"
|
||||||
echo Python: %PY%
|
echo Python: %PY%
|
||||||
|
|
||||||
|
if not exist "%SRC%" (
|
||||||
|
echo [ERROR] Source not found: %SRC%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%ICON%" (
|
||||||
|
echo [ERROR] Icon not found: %ICON%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\adb.exe" (
|
||||||
|
echo [ERROR] adb.exe not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\AdbWinApi.dll" (
|
||||||
|
echo [ERROR] AdbWinApi.dll not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\AdbWinUsbApi.dll" (
|
||||||
|
echo [ERROR] AdbWinUsbApi.dll not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\7za.exe" (
|
||||||
|
echo [ERROR] 7za.exe not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
echo [1/6] Installing deps...
|
echo [1/6] Installing deps...
|
||||||
%PY% -m pip install pyinstaller cython pyzipper -q
|
"%PY%" -m pip install pyinstaller cython pyzipper -q
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
%PY% -m pip install pyinstaller cython pyzipper -q -i https://pypi.tuna.tsinghua.edu.cn/simple
|
"%PY%" -m pip install pyinstaller cython pyzipper -q -i https://pypi.tuna.tsinghua.edu.cn/simple
|
||||||
|
)
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Dependency install failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
)
|
)
|
||||||
|
|
||||||
echo [2/6] Clean...
|
echo [2/6] Clean...
|
||||||
if exist "dist_cy" rmdir /s /q dist_cy 2>nul
|
if exist "dist_cy" rmdir /s /q "dist_cy" 2>nul
|
||||||
if exist "build" rmdir /s /q build 2>nul
|
if exist "build" rmdir /s /q "build" 2>nul
|
||||||
if exist "dist" rmdir /s /q dist 2>nul
|
if exist "dist" rmdir /s /q "dist" 2>nul
|
||||||
|
if exist "%NAME%.spec" del /q "%NAME%.spec" 2>nul
|
||||||
|
|
||||||
echo [3/6] Cython compile...
|
echo [3/6] Cython compile...
|
||||||
mkdir dist_cy 2>nul
|
mkdir "dist_cy" 2>nul
|
||||||
copy %SRC% dist_cy\_core.py >nul
|
copy "%SRC%" "dist_cy\_core.py" >nul
|
||||||
|
|
||||||
%PY% -c "open('dist_cy/setup_cython.py','w').write('from setuptools import setup\nfrom Cython.Build import cythonize\nsetup(ext_modules=cythonize(\"_core.py\", compiler_directives={\"language_level\":\"3\"}))\n')"
|
|
||||||
|
|
||||||
cd dist_cy
|
|
||||||
%PY% setup_cython.py build_ext --inplace
|
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
cd ..
|
echo [ERROR] Copy source failed
|
||||||
echo [WARN] Cython failed, fallback
|
pause
|
||||||
goto :NORMAL
|
exit /b 1
|
||||||
)
|
)
|
||||||
|
|
||||||
for %%f in (_core*.pyd) do set PYD=%%f
|
"%PY%" -c "open('dist_cy/setup_cython.py','w',encoding='utf-8').write('from setuptools import setup\nfrom Cython.Build import cythonize\nsetup(ext_modules=cythonize(\"_core.py\", compiler_directives={\"language_level\":\"3\"}))\n')"
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERROR] Failed to create Cython setup script
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
cd /d "dist_cy"
|
||||||
|
"%PY%" "setup_cython.py" build_ext --inplace
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd /d "%~dp0"
|
||||||
|
echo [ERROR] Cython failed. Build stopped.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
set "PYD="
|
||||||
|
for %%f in (_core*.pyd) do set "PYD=%%f"
|
||||||
if "%PYD%"=="" (
|
if "%PYD%"=="" (
|
||||||
cd ..
|
cd /d "%~dp0"
|
||||||
echo [WARN] No pyd, fallback
|
echo [ERROR] No Cython PYD generated. Build stopped.
|
||||||
goto :NORMAL
|
pause
|
||||||
|
exit /b 1
|
||||||
)
|
)
|
||||||
echo PYD: %PYD%
|
echo PYD: %PYD%
|
||||||
copy "%PYD%" _core.pyd >nul
|
copy "%PYD%" "_core.pyd" >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd /d "%~dp0"
|
||||||
|
echo [ERROR] Failed to copy Cython PYD
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
%PY% -c "open('launcher.py','w').write('# -*- coding: utf-8 -*-\nfrom _core import main\nmain()\n')"
|
"%PY%" -c "open('launcher.py','w',encoding='utf-8').write('# -*- coding: utf-8 -*-\nfrom _core import main\nmain()\n')"
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd /d "%~dp0"
|
||||||
|
echo [ERROR] Failed to create launcher.py
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
echo [4/6] Copy resources...
|
echo [4/6] Copy resources...
|
||||||
copy "%TOOLS%\adb.exe" . >nul
|
copy "%TOOLS%\adb.exe" . >nul
|
||||||
copy "%TOOLS%\AdbWinApi.dll" . >nul
|
copy "%TOOLS%\AdbWinApi.dll" . >nul
|
||||||
copy "%TOOLS%\AdbWinUsbApi.dll" . >nul
|
copy "%TOOLS%\AdbWinUsbApi.dll" . >nul
|
||||||
copy "%TOOLS%\7za.exe" . >nul
|
copy "%TOOLS%\7za.exe" . >nul
|
||||||
if exist "%ROOT%\app.ico" copy "%ROOT%\app.ico" . >nul
|
copy "%ICON%" . >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd /d "%~dp0"
|
||||||
|
echo [ERROR] Copy resources failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
echo [5/6] PyInstaller...
|
echo [5/6] PyInstaller...
|
||||||
%PY% -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ROOT%\app.ico" --add-data "%TOOLS%\adb.exe;." --add-data "%TOOLS%\AdbWinApi.dll;." --add-data "%TOOLS%\AdbWinUsbApi.dll;." --add-data "%TOOLS%\7za.exe;." --add-binary "_core.pyd;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin launcher.py
|
"%PY%" -m PyInstaller --onefile --windowed --name="%NAME%" --icon="app.ico" --add-data "adb.exe;." --add-data "AdbWinApi.dll;." --add-data "AdbWinUsbApi.dll;." --add-data "7za.exe;." --add-data "app.ico;." --add-binary "_core.pyd;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin "launcher.py"
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
cd ..
|
cd /d "%~dp0"
|
||||||
echo [ERROR] PyInstaller failed
|
echo [ERROR] PyInstaller failed
|
||||||
pause
|
pause
|
||||||
exit /b
|
exit /b 1
|
||||||
)
|
)
|
||||||
|
|
||||||
echo [6/6] Cleanup...
|
echo [6/6] Cleanup...
|
||||||
del /q _core.py _core.c _core.pyd %PYD% launcher.py setup_cython.py 2>nul
|
del /q "_core.py" "_core.c" "_core.pyd" "%PYD%" "launcher.py" "setup_cython.py" "app.ico" 2>nul
|
||||||
rmdir /s /q build 2>nul
|
rmdir /s /q "build" 2>nul
|
||||||
cd ..
|
cd /d "%~dp0"
|
||||||
goto :DONE
|
|
||||||
|
|
||||||
:NORMAL
|
|
||||||
echo [INFO] Normal PyInstaller...
|
|
||||||
%PY% -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ROOT%\app.ico" --add-data "%TOOLS%\adb.exe;." --add-data "%TOOLS%\AdbWinApi.dll;." --add-data "%TOOLS%\AdbWinUsbApi.dll;." --add-data "%TOOLS%\7za.exe;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin %SRC%
|
|
||||||
|
|
||||||
:DONE
|
|
||||||
echo.
|
echo.
|
||||||
echo Done.
|
echo Done.
|
||||||
if exist "dist_cy\dist\%NAME%.exe" (
|
if exist "dist_cy\dist\%NAME%.exe" (
|
||||||
echo Output: dist_cy\dist\%NAME%.exe
|
echo Output: dist_cy\dist\%NAME%.exe
|
||||||
) else if exist "dist\%NAME%.exe" (
|
|
||||||
echo Output: dist\%NAME%.exe
|
|
||||||
) else (
|
) else (
|
||||||
echo Check dist folder
|
echo [ERROR] Output exe was not generated
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
)
|
)
|
||||||
pause
|
pause
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ chcp 65001 >nul
|
|||||||
cd /d "%~dp0"
|
cd /d "%~dp0"
|
||||||
set "ROOT=%~dp0.."
|
set "ROOT=%~dp0.."
|
||||||
set "TOOLS=%ROOT%\tools"
|
set "TOOLS=%ROOT%\tools"
|
||||||
set NAME=深蓝S05刷入工具
|
set "ICON=%~dp0app.ico"
|
||||||
set SRC=S05.py
|
set NAME=UNI-T-multi-lan-installer
|
||||||
|
set SRC=UNI-T-multi-lan-installer.py
|
||||||
title %NAME% - Build
|
title %NAME% - Build
|
||||||
|
|
||||||
echo ============================================================
|
echo ============================================================
|
||||||
@@ -27,14 +28,42 @@ if errorlevel 1 (
|
|||||||
%PY% -m pip install pyinstaller cython pyzipper -q -i https://pypi.tuna.tsinghua.edu.cn/simple
|
%PY% -m pip install pyinstaller cython pyzipper -q -i https://pypi.tuna.tsinghua.edu.cn/simple
|
||||||
)
|
)
|
||||||
|
|
||||||
echo [2/6] Clean...
|
echo [2/7] Clean...
|
||||||
if exist "dist_cy" rmdir /s /q dist_cy 2>nul
|
if exist "dist_cy" rmdir /s /q dist_cy 2>nul
|
||||||
if exist "build" rmdir /s /q build 2>nul
|
if exist "build" rmdir /s /q build 2>nul
|
||||||
if exist "dist" rmdir /s /q dist 2>nul
|
if exist "dist" rmdir /s /q dist 2>nul
|
||||||
|
|
||||||
echo [3/6] Cython compile...
|
echo [3/7] Checking resources...
|
||||||
|
if not exist "%ICON%" (
|
||||||
|
echo [ERROR] app.ico not found in UNI-T folder: %ICON%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\adb.exe" (
|
||||||
|
echo [ERROR] Missing ADB file: %TOOLS%\adb.exe
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\AdbWinApi.dll" (
|
||||||
|
echo [ERROR] Missing ADB file: %TOOLS%\AdbWinApi.dll
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\AdbWinUsbApi.dll" (
|
||||||
|
echo [ERROR] Missing ADB file: %TOOLS%\AdbWinUsbApi.dll
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
if not exist "%TOOLS%\7za.exe" (
|
||||||
|
echo [ERROR] Missing 7za file: %TOOLS%\7za.exe
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [4/7] Cython compile...
|
||||||
mkdir dist_cy 2>nul
|
mkdir dist_cy 2>nul
|
||||||
copy %SRC% dist_cy\_core.py >nul
|
copy %SRC% dist_cy\_core.py >nul
|
||||||
|
set "PYD="
|
||||||
|
|
||||||
%PY% -c "open('dist_cy/setup_cython.py','w').write('from setuptools import setup\nfrom Cython.Build import cythonize\nsetup(ext_modules=cythonize(\"_core.py\", compiler_directives={\"language_level\":\"3\"}))\n')"
|
%PY% -c "open('dist_cy/setup_cython.py','w').write('from setuptools import setup\nfrom Cython.Build import cythonize\nsetup(ext_modules=cythonize(\"_core.py\", compiler_directives={\"language_level\":\"3\"}))\n')"
|
||||||
|
|
||||||
@@ -42,54 +71,50 @@ cd dist_cy
|
|||||||
%PY% setup_cython.py build_ext --inplace
|
%PY% setup_cython.py build_ext --inplace
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
cd ..
|
cd ..
|
||||||
echo [WARN] Cython failed, fallback
|
echo [ERROR] Cython build failed. Install Microsoft C++ Build Tools and retry.
|
||||||
goto :NORMAL
|
pause
|
||||||
|
exit /b 1
|
||||||
)
|
)
|
||||||
|
|
||||||
for %%f in (_core*.pyd) do set PYD=%%f
|
for %%f in (_core*.pyd) do set PYD=%%f
|
||||||
if "%PYD%"=="" (
|
if "%PYD%"=="" (
|
||||||
cd ..
|
cd ..
|
||||||
echo [WARN] No pyd, fallback
|
echo [ERROR] Cython build did not generate _core*.pyd. Stop.
|
||||||
goto :NORMAL
|
pause
|
||||||
|
exit /b 1
|
||||||
)
|
)
|
||||||
echo PYD: %PYD%
|
echo PYD: %PYD%
|
||||||
copy "%PYD%" _core.pyd >nul
|
copy "%PYD%" _core.pyd >nul
|
||||||
|
|
||||||
%PY% -c "open('launcher.py','w').write('# -*- coding: utf-8 -*-\nfrom _core import main\nmain()\n')"
|
%PY% -c "open('launcher.py','w').write('# -*- coding: utf-8 -*-\nfrom _core import main\nmain()\n')"
|
||||||
|
|
||||||
echo [4/6] Copy resources...
|
echo [5/7] Copy resources...
|
||||||
copy "%TOOLS%\adb.exe" . >nul
|
copy "%TOOLS%\adb.exe" . >nul
|
||||||
copy "%TOOLS%\AdbWinApi.dll" . >nul
|
copy "%TOOLS%\AdbWinApi.dll" . >nul
|
||||||
copy "%TOOLS%\AdbWinUsbApi.dll" . >nul
|
copy "%TOOLS%\AdbWinUsbApi.dll" . >nul
|
||||||
copy "%TOOLS%\7za.exe" . >nul
|
copy "%TOOLS%\7za.exe" . >nul
|
||||||
if exist "%ROOT%\app.ico" copy "%ROOT%\app.ico" . >nul
|
copy "%ICON%" . >nul
|
||||||
|
|
||||||
echo [5/6] PyInstaller...
|
echo [6/7] PyInstaller...
|
||||||
%PY% -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ROOT%\app.ico" --add-data "%TOOLS%\adb.exe;." --add-data "%TOOLS%\AdbWinApi.dll;." --add-data "%TOOLS%\AdbWinUsbApi.dll;." --add-data "%TOOLS%\7za.exe;." --add-binary "_core.pyd;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin launcher.py
|
%PY% -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ICON%" --add-data "%ICON%;." --add-data "%TOOLS%\adb.exe;." --add-data "%TOOLS%\AdbWinApi.dll;." --add-data "%TOOLS%\AdbWinUsbApi.dll;." --add-data "%TOOLS%\7za.exe;." --add-binary "_core.pyd;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin launcher.py
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
cd ..
|
cd ..
|
||||||
echo [ERROR] PyInstaller failed
|
echo [ERROR] PyInstaller failed
|
||||||
pause
|
pause
|
||||||
exit /b
|
exit /b 1
|
||||||
)
|
)
|
||||||
|
|
||||||
echo [6/6] Cleanup...
|
echo [7/7] Cleanup...
|
||||||
del /q _core.py _core.c _core.pyd %PYD% launcher.py setup_cython.py 2>nul
|
del /q _core.py _core.c _core.pyd %PYD% launcher.py setup_cython.py 2>nul
|
||||||
rmdir /s /q build 2>nul
|
rmdir /s /q build 2>nul
|
||||||
cd ..
|
cd ..
|
||||||
goto :DONE
|
goto :DONE
|
||||||
|
|
||||||
:NORMAL
|
|
||||||
echo [INFO] Normal PyInstaller...
|
|
||||||
%PY% -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ROOT%\app.ico" --add-data "%TOOLS%\adb.exe;." --add-data "%TOOLS%\AdbWinApi.dll;." --add-data "%TOOLS%\AdbWinUsbApi.dll;." --add-data "%TOOLS%\7za.exe;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin %SRC%
|
|
||||||
|
|
||||||
:DONE
|
:DONE
|
||||||
echo.
|
echo.
|
||||||
echo Done.
|
echo Done.
|
||||||
if exist "dist_cy\dist\%NAME%.exe" (
|
if exist "dist_cy\dist\%NAME%.exe" (
|
||||||
echo Output: dist_cy\dist\%NAME%.exe
|
echo Output: dist_cy\dist\%NAME%.exe
|
||||||
) else if exist "dist\%NAME%.exe" (
|
|
||||||
echo Output: dist\%NAME%.exe
|
|
||||||
) else (
|
) else (
|
||||||
echo Check dist folder
|
echo Check dist folder
|
||||||
)
|
)
|
||||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -1,96 +1,157 @@
|
|||||||
@echo off
|
@echo off
|
||||||
chcp 65001 >nul
|
chcp 65001 >nul
|
||||||
cd /d "%~dp0"
|
setlocal
|
||||||
set "ROOT=%~dp0.."
|
cd /d "%~dp0"
|
||||||
set "TOOLS=%ROOT%\tools"
|
|
||||||
set NAME=X5plus刷入工具
|
set "ROOT=%~dp0.."
|
||||||
set SRC=X5plusTool.py
|
set "TOOLS=%ROOT%\tools"
|
||||||
title %NAME% - Build
|
set "ICON=%~dp0app.ico"
|
||||||
|
set "NAME=Changan_X5Plus-Installer"
|
||||||
echo ============================================================
|
set "SRC=X5plusTool.py"
|
||||||
echo %NAME% - Cython Build
|
title %NAME% - Build
|
||||||
echo ============================================================
|
|
||||||
echo.
|
echo ============================================================
|
||||||
|
echo %NAME% - Cython Build
|
||||||
where python >nul 2>&1
|
echo ============================================================
|
||||||
if errorlevel 1 (
|
echo.
|
||||||
echo [ERROR] Python not found
|
|
||||||
pause
|
set "PY="
|
||||||
exit /b
|
where python >nul 2>&1
|
||||||
)
|
if not errorlevel 1 (
|
||||||
for /f "delims=" %%i in ('where python') do set PY=%%i
|
for /f "delims=" %%i in ('where python') do (
|
||||||
echo Python: %PY%
|
if not defined PY set "PY=%%i"
|
||||||
|
)
|
||||||
echo [1/6] Installing deps...
|
)
|
||||||
%PY% -m pip install pyinstaller cython pyzipper -q
|
if not defined PY (
|
||||||
if errorlevel 1 (
|
where py >nul 2>&1
|
||||||
%PY% -m pip install pyinstaller cython pyzipper -q -i https://pypi.tuna.tsinghua.edu.cn/simple
|
if not errorlevel 1 set "PY=py"
|
||||||
)
|
)
|
||||||
|
if not defined PY (
|
||||||
echo [2/6] Clean...
|
if exist "%LOCALAPPDATA%\Microsoft\WindowsApps\python.exe" set "PY=%LOCALAPPDATA%\Microsoft\WindowsApps\python.exe"
|
||||||
if exist "dist_cy" rmdir /s /q dist_cy 2>nul
|
)
|
||||||
if exist "build" rmdir /s /q build 2>nul
|
if not defined PY (
|
||||||
if exist "dist" rmdir /s /q dist 2>nul
|
echo [ERROR] Python not found
|
||||||
|
pause
|
||||||
echo [3/6] Cython compile...
|
exit /b 1
|
||||||
mkdir dist_cy 2>nul
|
)
|
||||||
copy %SRC% dist_cy\_core.py >nul
|
echo Python: %PY%
|
||||||
|
|
||||||
%PY% -c "open('dist_cy/setup_cython.py','w').write('from setuptools import setup\nfrom Cython.Build import cythonize\nsetup(ext_modules=cythonize(\"_core.py\", compiler_directives={\"language_level\":\"3\"}))\n')"
|
if not exist "%SRC%" (
|
||||||
|
echo [ERROR] Source not found: %SRC%
|
||||||
cd dist_cy
|
pause
|
||||||
%PY% setup_cython.py build_ext --inplace
|
exit /b 1
|
||||||
if errorlevel 1 (
|
)
|
||||||
cd ..
|
|
||||||
echo [WARN] Cython failed, fallback
|
if not exist "%ICON%" (
|
||||||
goto :NORMAL
|
echo [ERROR] app.ico not found: %ICON%
|
||||||
)
|
pause
|
||||||
|
exit /b 1
|
||||||
for %%f in (_core*.pyd) do set PYD=%%f
|
)
|
||||||
if "%PYD%"=="" (
|
|
||||||
cd ..
|
echo [1/6] Installing deps...
|
||||||
echo [WARN] No pyd, fallback
|
"%PY%" -m pip install pyinstaller cython pyzipper -q
|
||||||
goto :NORMAL
|
if errorlevel 1 (
|
||||||
)
|
"%PY%" -m pip install pyinstaller cython pyzipper -q -i https://pypi.tuna.tsinghua.edu.cn/simple
|
||||||
echo PYD: %PYD%
|
if errorlevel 1 (
|
||||||
copy "%PYD%" _core.pyd >nul
|
echo [ERROR] Dependency installation failed
|
||||||
|
pause
|
||||||
%PY% -c "open('launcher.py','w').write('# -*- coding: utf-8 -*-\nfrom _core import main\nmain()\n')"
|
exit /b 1
|
||||||
|
)
|
||||||
echo [4/6] Copy resources...
|
)
|
||||||
copy "%TOOLS%\adb.exe" . >nul
|
|
||||||
copy "%TOOLS%\AdbWinApi.dll" . >nul
|
echo [2/6] Clean...
|
||||||
copy "%TOOLS%\AdbWinUsbApi.dll" . >nul
|
if exist "dist_cy" rmdir /s /q dist_cy 2>nul
|
||||||
copy "%TOOLS%\7za.exe" . >nul
|
if exist "build" rmdir /s /q build 2>nul
|
||||||
if exist "%ROOT%\app.ico" copy "%ROOT%\app.ico" . >nul
|
if exist "dist" rmdir /s /q dist 2>nul
|
||||||
|
|
||||||
echo [5/6] PyInstaller...
|
echo [3/6] Cython compile...
|
||||||
%PY% -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ROOT%\app.ico" --add-data "%TOOLS%\adb.exe;." --add-data "%TOOLS%\AdbWinApi.dll;." --add-data "%TOOLS%\AdbWinUsbApi.dll;." --add-data "%TOOLS%\7za.exe;." --add-binary "_core.pyd;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin launcher.py
|
mkdir dist_cy 2>nul
|
||||||
if errorlevel 1 (
|
copy "%SRC%" "dist_cy\_core.py" >nul
|
||||||
cd ..
|
if errorlevel 1 (
|
||||||
echo [ERROR] PyInstaller failed
|
echo [ERROR] Copy source failed
|
||||||
pause
|
pause
|
||||||
exit /b
|
exit /b 1
|
||||||
)
|
)
|
||||||
|
|
||||||
echo [6/6] Cleanup...
|
"%PY%" -c "open('dist_cy/setup_cython.py','w',encoding='utf-8').write('from setuptools import setup\nfrom Cython.Build import cythonize\nsetup(ext_modules=cythonize(\"_core.py\", compiler_directives={\"language_level\":\"3\"}))\n')"
|
||||||
del /q _core.py _core.c _core.pyd %PYD% launcher.py setup_cython.py 2>nul
|
if errorlevel 1 (
|
||||||
rmdir /s /q build 2>nul
|
echo [ERROR] Generate Cython setup failed
|
||||||
cd ..
|
pause
|
||||||
goto :DONE
|
exit /b 1
|
||||||
|
)
|
||||||
:NORMAL
|
|
||||||
echo [INFO] Normal PyInstaller...
|
cd dist_cy
|
||||||
%PY% -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ROOT%\app.ico" --add-data "%TOOLS%\adb.exe;." --add-data "%TOOLS%\AdbWinApi.dll;." --add-data "%TOOLS%\AdbWinUsbApi.dll;." --add-data "%TOOLS%\7za.exe;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin %SRC%
|
"%PY%" setup_cython.py build_ext --inplace
|
||||||
|
if errorlevel 1 (
|
||||||
:DONE
|
cd ..
|
||||||
echo.
|
echo [ERROR] Cython compile failed
|
||||||
echo Done.
|
pause
|
||||||
if exist "dist_cy\dist\%NAME%.exe" (
|
exit /b 1
|
||||||
echo Output: dist_cy\dist\%NAME%.exe
|
)
|
||||||
) else if exist "dist\%NAME%.exe" (
|
|
||||||
echo Output: dist\%NAME%.exe
|
set "PYD="
|
||||||
) else (
|
for %%f in (_core*.pyd) do set "PYD=%%f"
|
||||||
echo Check dist folder
|
if "%PYD%"=="" (
|
||||||
)
|
cd ..
|
||||||
pause
|
echo [ERROR] No Cython pyd generated
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo PYD: %PYD%
|
||||||
|
copy "%PYD%" "_core.pyd" >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Copy Cython pyd failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
"%PY%" -c "open('launcher.py','w',encoding='utf-8').write('# -*- coding: utf-8 -*-\nfrom _core import main\nmain()\n')"
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Generate launcher failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [4/6] Copy resources...
|
||||||
|
copy "%TOOLS%\adb.exe" . >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] adb.exe not found in %TOOLS%
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
copy "%TOOLS%\AdbWinApi.dll" . >nul
|
||||||
|
copy "%TOOLS%\AdbWinUsbApi.dll" . >nul
|
||||||
|
copy "%TOOLS%\7za.exe" . >nul
|
||||||
|
copy "%ICON%" . >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] Copy resources failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [5/6] PyInstaller...
|
||||||
|
"%PY%" -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ICON%" --add-data "%ICON%;." --add-data "%TOOLS%\adb.exe;." --add-data "%TOOLS%\AdbWinApi.dll;." --add-data "%TOOLS%\AdbWinUsbApi.dll;." --add-data "%TOOLS%\7za.exe;." --add-binary "_core.pyd;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin launcher.py
|
||||||
|
if errorlevel 1 (
|
||||||
|
cd ..
|
||||||
|
echo [ERROR] PyInstaller failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [6/6] Cleanup...
|
||||||
|
del /q "_core.py" "_core.c" "_core.pyd" "%PYD%" "launcher.py" "setup_cython.py" 2>nul
|
||||||
|
rmdir /s /q build 2>nul
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Done.
|
||||||
|
if exist "dist_cy\dist\%NAME%.exe" (
|
||||||
|
echo Output: dist_cy\dist\%NAME%.exe
|
||||||
|
) else (
|
||||||
|
echo [ERROR] Build output not found
|
||||||
|
)
|
||||||
|
pause
|
||||||
|
|||||||
@@ -1,96 +0,0 @@
|
|||||||
@echo off
|
|
||||||
chcp 65001 >nul
|
|
||||||
cd /d "%~dp0"
|
|
||||||
set "ROOT=%~dp0.."
|
|
||||||
set "TOOLS=%ROOT%\tools"
|
|
||||||
set NAME=长安逸动刷入工具
|
|
||||||
set SRC=app-install.py
|
|
||||||
title %NAME% - Build
|
|
||||||
|
|
||||||
echo ============================================================
|
|
||||||
echo %NAME% - Cython Build
|
|
||||||
echo ============================================================
|
|
||||||
echo.
|
|
||||||
|
|
||||||
where python >nul 2>&1
|
|
||||||
if errorlevel 1 (
|
|
||||||
echo [ERROR] Python not found
|
|
||||||
pause
|
|
||||||
exit /b
|
|
||||||
)
|
|
||||||
for /f "delims=" %%i in ('where python') do set PY=%%i
|
|
||||||
echo Python: %PY%
|
|
||||||
|
|
||||||
echo [1/6] Installing deps...
|
|
||||||
%PY% -m pip install pyinstaller cython pyzipper -q
|
|
||||||
if errorlevel 1 (
|
|
||||||
%PY% -m pip install pyinstaller cython pyzipper -q -i https://pypi.tuna.tsinghua.edu.cn/simple
|
|
||||||
)
|
|
||||||
|
|
||||||
echo [2/6] Clean...
|
|
||||||
if exist "dist_cy" rmdir /s /q dist_cy 2>nul
|
|
||||||
if exist "build" rmdir /s /q build 2>nul
|
|
||||||
if exist "dist" rmdir /s /q dist 2>nul
|
|
||||||
|
|
||||||
echo [3/6] Cython compile...
|
|
||||||
mkdir dist_cy 2>nul
|
|
||||||
copy %SRC% dist_cy\_core.py >nul
|
|
||||||
|
|
||||||
%PY% -c "open('dist_cy/setup_cython.py','w').write('from setuptools import setup\nfrom Cython.Build import cythonize\nsetup(ext_modules=cythonize(\"_core.py\", compiler_directives={\"language_level\":\"3\"}))\n')"
|
|
||||||
|
|
||||||
cd dist_cy
|
|
||||||
%PY% setup_cython.py build_ext --inplace
|
|
||||||
if errorlevel 1 (
|
|
||||||
cd ..
|
|
||||||
echo [WARN] Cython failed, fallback
|
|
||||||
goto :NORMAL
|
|
||||||
)
|
|
||||||
|
|
||||||
for %%f in (_core*.pyd) do set PYD=%%f
|
|
||||||
if "%PYD%"=="" (
|
|
||||||
cd ..
|
|
||||||
echo [WARN] No pyd, fallback
|
|
||||||
goto :NORMAL
|
|
||||||
)
|
|
||||||
echo PYD: %PYD%
|
|
||||||
copy "%PYD%" _core.pyd >nul
|
|
||||||
|
|
||||||
%PY% -c "open('launcher.py','w').write('# -*- coding: utf-8 -*-\nfrom _core import main\nmain()\n')"
|
|
||||||
|
|
||||||
echo [4/6] Copy resources...
|
|
||||||
copy "%TOOLS%\adb.exe" . >nul
|
|
||||||
copy "%TOOLS%\AdbWinApi.dll" . >nul
|
|
||||||
copy "%TOOLS%\AdbWinUsbApi.dll" . >nul
|
|
||||||
copy "%TOOLS%\7za.exe" . >nul
|
|
||||||
if exist "%ROOT%\app.ico" copy "%ROOT%\app.ico" . >nul
|
|
||||||
|
|
||||||
echo [5/6] PyInstaller...
|
|
||||||
%PY% -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ROOT%\app.ico" --add-data "%TOOLS%\adb.exe;." --add-data "%TOOLS%\AdbWinApi.dll;." --add-data "%TOOLS%\AdbWinUsbApi.dll;." --add-data "%TOOLS%\7za.exe;." --add-binary "_core.pyd;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin launcher.py
|
|
||||||
if errorlevel 1 (
|
|
||||||
cd ..
|
|
||||||
echo [ERROR] PyInstaller failed
|
|
||||||
pause
|
|
||||||
exit /b
|
|
||||||
)
|
|
||||||
|
|
||||||
echo [6/6] Cleanup...
|
|
||||||
del /q _core.py _core.c _core.pyd %PYD% launcher.py setup_cython.py 2>nul
|
|
||||||
rmdir /s /q build 2>nul
|
|
||||||
cd ..
|
|
||||||
goto :DONE
|
|
||||||
|
|
||||||
:NORMAL
|
|
||||||
echo [INFO] Normal PyInstaller...
|
|
||||||
%PY% -m PyInstaller --onefile --windowed --name="%NAME%" --icon="%ROOT%\app.ico" --add-data "%TOOLS%\adb.exe;." --add-data "%TOOLS%\AdbWinApi.dll;." --add-data "%TOOLS%\AdbWinUsbApi.dll;." --add-data "%TOOLS%\7za.exe;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=tkinter.simpledialog --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --hidden-import=urllib.parse --collect-all tkinter --uac-admin %SRC%
|
|
||||||
|
|
||||||
:DONE
|
|
||||||
echo.
|
|
||||||
echo Done.
|
|
||||||
if exist "dist_cy\dist\%NAME%.exe" (
|
|
||||||
echo Output: dist_cy\dist\%NAME%.exe
|
|
||||||
) else if exist "dist\%NAME%.exe" (
|
|
||||||
echo Output: dist\%NAME%.exe
|
|
||||||
) else (
|
|
||||||
echo Check dist folder
|
|
||||||
)
|
|
||||||
pause
|
|
||||||
@@ -10,16 +10,35 @@ import struct
|
|||||||
import zlib
|
import zlib
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
AAD = b"Q05-Lidar init_boot resource v1"
|
DEFAULT_AAD = "Q05-Lidar init_boot resource v1"
|
||||||
|
FORMAT_MAGIC = {
|
||||||
|
"q05": (b"Q05R2\x00", "q05-lidar-resource-v2", DEFAULT_AAD),
|
||||||
|
"ez60": (b"EZ60R2\x00", "ez60-resource-v2", "Mazda-EZ60 init_boot resource v1"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def b64u(data):
|
def b64u(data):
|
||||||
return base64.urlsafe_b64encode(data).decode("ascii").rstrip("=")
|
return base64.urlsafe_b64encode(data).decode("ascii").rstrip("=")
|
||||||
|
|
||||||
|
|
||||||
|
def decode_key_material(key_text):
|
||||||
|
text = str(key_text).strip()
|
||||||
|
if text.startswith("raw:"):
|
||||||
|
key = text.split(":", 1)[1].encode("utf-8")
|
||||||
|
elif len(text) == 64 and all(c in "0123456789abcdefABCDEF" for c in text):
|
||||||
|
key = bytes.fromhex(text)
|
||||||
|
elif len(text.encode("utf-8")) == 32:
|
||||||
|
key = text.encode("utf-8")
|
||||||
|
else:
|
||||||
|
key = base64.urlsafe_b64decode(text + "=" * (-len(text) % 4))
|
||||||
|
if len(key) != 32:
|
||||||
|
raise ValueError(f"key must decode to 32 bytes, got {len(key)} bytes")
|
||||||
|
return key
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Encrypt a Q05-Lidar init_boot image into resource.dat"
|
description="Encrypt a vehicle init_boot image into a resource dat file"
|
||||||
)
|
)
|
||||||
parser.add_argument("input_img", help="Magisk-patched init_boot image")
|
parser.add_argument("input_img", help="Magisk-patched init_boot image")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@@ -27,9 +46,19 @@ def main():
|
|||||||
default="resource.dat",
|
default="resource.dat",
|
||||||
help="Output encrypted resource file, default: resource.dat",
|
help="Output encrypted resource file, default: resource.dat",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--vehicle",
|
||||||
|
choices=sorted(FORMAT_MAGIC.keys()),
|
||||||
|
default="q05",
|
||||||
|
help="Resource header/profile to write. Default: q05.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--aad",
|
||||||
|
help="Optional AES-GCM AAD text. Defaults to the selected vehicle profile.",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--key",
|
"--key",
|
||||||
help="Optional AES-256 key as base64url or 64-char hex. Omit to generate a random key.",
|
help="Optional AES-256 key as base64url, 64-char hex, raw:TEXT, or 32-byte UTF-8 text. Omit to generate a random key.",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--no-compress",
|
"--no-compress",
|
||||||
@@ -49,29 +78,28 @@ def main():
|
|||||||
input_path = Path(args.input_img)
|
input_path = Path(args.input_img)
|
||||||
plain = input_path.read_bytes()
|
plain = input_path.read_bytes()
|
||||||
sha256 = hashlib.sha256(plain).hexdigest()
|
sha256 = hashlib.sha256(plain).hexdigest()
|
||||||
|
magic, resource_format, default_aad = FORMAT_MAGIC[args.vehicle]
|
||||||
|
aad = (args.aad or default_aad).encode("utf-8")
|
||||||
|
|
||||||
if args.key:
|
if args.key:
|
||||||
text = args.key.strip()
|
try:
|
||||||
if len(text) == 64 and all(c in "0123456789abcdefABCDEF" for c in text):
|
key = decode_key_material(args.key)
|
||||||
key = bytes.fromhex(text)
|
except Exception as e:
|
||||||
else:
|
raise SystemExit(str(e))
|
||||||
key = base64.urlsafe_b64decode(text + "=" * (-len(text) % 4))
|
|
||||||
if len(key) != 32:
|
|
||||||
raise SystemExit("key must decode to 32 bytes")
|
|
||||||
else:
|
else:
|
||||||
key = os.urandom(32)
|
key = os.urandom(32)
|
||||||
|
|
||||||
compression = "none" if args.no_compress else "zlib"
|
compression = "none" if args.no_compress else "zlib"
|
||||||
body = plain if args.no_compress else zlib.compress(plain, level=9)
|
body = plain if args.no_compress else zlib.compress(plain, level=9)
|
||||||
nonce = os.urandom(12)
|
nonce = os.urandom(12)
|
||||||
ciphertext = AESGCM(key).encrypt(nonce, body, AAD)
|
ciphertext = AESGCM(key).encrypt(nonce, body, aad)
|
||||||
|
|
||||||
header = {
|
header = {
|
||||||
"format": "q05-lidar-resource-v2",
|
"format": resource_format,
|
||||||
"cipher": "AES-256-GCM",
|
"cipher": "AES-256-GCM",
|
||||||
"kdf": "none",
|
"kdf": "none",
|
||||||
"compression": compression,
|
"compression": compression,
|
||||||
"aad": AAD.decode("utf-8"),
|
"aad": aad.decode("utf-8"),
|
||||||
"nonce": b64u(nonce),
|
"nonce": b64u(nonce),
|
||||||
"sha256": sha256,
|
"sha256": sha256,
|
||||||
"plainSize": len(plain),
|
"plainSize": len(plain),
|
||||||
@@ -80,7 +108,7 @@ def main():
|
|||||||
|
|
||||||
output_path = Path(args.output)
|
output_path = Path(args.output)
|
||||||
header_bytes = json.dumps(header, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
|
header_bytes = json.dumps(header, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
|
||||||
output_path.write_bytes(b"Q05R2\x00" + struct.pack(">I", len(header_bytes)) + header_bytes + ciphertext)
|
output_path.write_bytes(magic + struct.pack(">I", len(header_bytes)) + header_bytes + ciphertext)
|
||||||
|
|
||||||
print("resource.dat created")
|
print("resource.dat created")
|
||||||
print(f"input: {input_path}")
|
print(f"input: {input_path}")
|
||||||
|
|||||||
@@ -0,0 +1,195 @@
|
|||||||
|
;
|
||||||
|
; Android WinUsb driver installation.
|
||||||
|
;
|
||||||
|
[Version]
|
||||||
|
Signature = "$Windows NT$"
|
||||||
|
Class = AndroidUsbDeviceClass
|
||||||
|
ClassGuid = {3F966BD9-FA04-4ec5-991C-D326973B5128}
|
||||||
|
Provider = %ProviderName%
|
||||||
|
DriverVer = 08/28/2014,11.0.0000.00000
|
||||||
|
CatalogFile.NTx86 = androidwinusb86.cat
|
||||||
|
CatalogFile.NTamd64 = androidwinusba64.cat
|
||||||
|
|
||||||
|
[ClassInstall32]
|
||||||
|
Addreg = AndroidWinUsbClassReg
|
||||||
|
|
||||||
|
[AndroidWinUsbClassReg]
|
||||||
|
HKR,,,0,%ClassName%
|
||||||
|
HKR,,Icon,,-1
|
||||||
|
|
||||||
|
|
||||||
|
[Manufacturer]
|
||||||
|
%ProviderName% = Google, NTx86, NTamd64
|
||||||
|
|
||||||
|
|
||||||
|
[Google.NTx86]
|
||||||
|
|
||||||
|
;Google Nexus One
|
||||||
|
%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_0D02
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_0D02&MI_01
|
||||||
|
%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_4E11
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4E12&MI_01
|
||||||
|
|
||||||
|
;Google Nexus S
|
||||||
|
%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_4E21
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4E22&MI_01
|
||||||
|
%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_4E23
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4E24&MI_01
|
||||||
|
|
||||||
|
;Google Nexus 7
|
||||||
|
%SingleBootLoaderInterface% = USB_Install, USB\VID_18D1&PID_4E40
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4E42&MI_01
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4E44&MI_01
|
||||||
|
|
||||||
|
;Google Nexus Q
|
||||||
|
%SingleBootLoaderInterface% = USB_Install, USB\VID_18D1&PID_2C10
|
||||||
|
%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_2C11
|
||||||
|
|
||||||
|
;Google Nexus (generic)
|
||||||
|
%SingleBootLoaderInterface% = USB_Install, USB\VID_18D1&PID_4EE0
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE2&MI_01
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE4&MI_02
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE6&MI_01
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE7
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_D001
|
||||||
|
|
||||||
|
;Google Glass
|
||||||
|
%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_9001
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_9001&MI_01
|
||||||
|
|
||||||
|
;Google Glass EE1
|
||||||
|
|
||||||
|
%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_9003
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_9003&MI_01
|
||||||
|
%SingleBootLoaderInterface% = USB_Install, USB\VID_18D1&PID_9004
|
||||||
|
|
||||||
|
;Google Glass EE2
|
||||||
|
|
||||||
|
%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_9005
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_9005&MI_00
|
||||||
|
%SingleBootLoaderInterface% = USB_Install, USB\VID_18D1&PID_9006
|
||||||
|
|
||||||
|
;Project Tango (generic)
|
||||||
|
%SingleBootLoaderInterface% = USB_Install, USB\VID_18D1&PID_4D00
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4D02&MI_01
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4D04&MI_02
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4D06&MI_01
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4D07
|
||||||
|
|
||||||
|
|
||||||
|
[Google.NTamd64]
|
||||||
|
|
||||||
|
;Google Nexus One
|
||||||
|
%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_0D02
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_0D02&MI_01
|
||||||
|
%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_4E11
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4E12&MI_01
|
||||||
|
|
||||||
|
;Google Nexus S
|
||||||
|
%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_4E21
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4E22&MI_01
|
||||||
|
%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_4E23
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4E24&MI_01
|
||||||
|
|
||||||
|
;Google Nexus 7
|
||||||
|
%SingleBootLoaderInterface% = USB_Install, USB\VID_18D1&PID_4E40
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4E42&MI_01
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4E44&MI_01
|
||||||
|
|
||||||
|
;Google Nexus Q
|
||||||
|
%SingleBootLoaderInterface% = USB_Install, USB\VID_18D1&PID_2C10
|
||||||
|
%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_2C11
|
||||||
|
|
||||||
|
;Google Nexus (generic)
|
||||||
|
%SingleBootLoaderInterface% = USB_Install, USB\VID_18D1&PID_4EE0
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE2&MI_01
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE4&MI_02
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE6&MI_01
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4EE7
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_D001
|
||||||
|
|
||||||
|
;Google Glass
|
||||||
|
%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_9001
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_9001&MI_01
|
||||||
|
|
||||||
|
;Google Glass EE1
|
||||||
|
|
||||||
|
%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_9003
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_9003&MI_01
|
||||||
|
%SingleBootLoaderInterface% = USB_Install, USB\VID_18D1&PID_9004
|
||||||
|
|
||||||
|
;Google Glass EE2
|
||||||
|
|
||||||
|
%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_9005
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_9005&MI_00
|
||||||
|
%SingleBootLoaderInterface% = USB_Install, USB\VID_18D1&PID_9006
|
||||||
|
|
||||||
|
;Project Tango (generic)
|
||||||
|
%SingleBootLoaderInterface% = USB_Install, USB\VID_18D1&PID_4D00
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4D02&MI_01
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4D04&MI_02
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4D06&MI_01
|
||||||
|
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4D07
|
||||||
|
|
||||||
|
|
||||||
|
[USB_Install]
|
||||||
|
Include = winusb.inf
|
||||||
|
Needs = WINUSB.NT
|
||||||
|
|
||||||
|
[USB_Install.Services]
|
||||||
|
Include = winusb.inf
|
||||||
|
AddService = WinUSB,0x00000002,WinUSB_ServiceInstall
|
||||||
|
|
||||||
|
[WinUSB_ServiceInstall]
|
||||||
|
DisplayName = %WinUSB_SvcDesc%
|
||||||
|
ServiceType = 1
|
||||||
|
StartType = 3
|
||||||
|
ErrorControl = 1
|
||||||
|
ServiceBinary = %12%\WinUSB.sys
|
||||||
|
|
||||||
|
[USB_Install.Wdf]
|
||||||
|
KmdfService = WINUSB, WinUSB_Install
|
||||||
|
|
||||||
|
[WinUSB_Install]
|
||||||
|
KmdfLibraryVersion = 1.9
|
||||||
|
|
||||||
|
[USB_Install.HW]
|
||||||
|
AddReg = Dev_AddReg
|
||||||
|
|
||||||
|
[Dev_AddReg]
|
||||||
|
HKR,,DeviceInterfaceGUIDs,0x10000,"{F72FE0D4-CBCB-407d-8814-9ED673D0DD6B}"
|
||||||
|
|
||||||
|
[USB_Install.CoInstallers]
|
||||||
|
AddReg = CoInstallers_AddReg
|
||||||
|
CopyFiles = CoInstallers_CopyFiles
|
||||||
|
|
||||||
|
[CoInstallers_AddReg]
|
||||||
|
HKR,,CoInstallers32,0x00010000,"WdfCoInstaller01009.dll,WdfCoInstaller","WinUSBCoInstaller2.dll"
|
||||||
|
|
||||||
|
[CoInstallers_CopyFiles]
|
||||||
|
WinUSBCoInstaller2.dll
|
||||||
|
WdfCoInstaller01009.dll
|
||||||
|
|
||||||
|
[DestinationDirs]
|
||||||
|
CoInstallers_CopyFiles=11
|
||||||
|
|
||||||
|
[SourceDisksNames]
|
||||||
|
1 = %DISK_NAME%,,,\i386
|
||||||
|
2 = %DISK_NAME%,,,\amd64
|
||||||
|
|
||||||
|
[SourceDisksFiles.x86]
|
||||||
|
WinUSBCoInstaller2.dll = 1
|
||||||
|
WdfCoInstaller01009.dll = 1
|
||||||
|
|
||||||
|
[SourceDisksFiles.amd64]
|
||||||
|
WinUSBCoInstaller2.dll = 2
|
||||||
|
WdfCoInstaller01009.dll = 2
|
||||||
|
|
||||||
|
[Strings]
|
||||||
|
ProviderName = "Google, Inc."
|
||||||
|
SingleAdbInterface = "Android ADB Interface"
|
||||||
|
CompositeAdbInterface = "Android Composite ADB Interface"
|
||||||
|
SingleBootLoaderInterface = "Android Bootloader Interface"
|
||||||
|
WinUSB_SvcDesc = "Android USB Driver"
|
||||||
|
DISK_NAME = "Android WinUsb installation disk"
|
||||||
|
ClassName = "Android Device"
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
Pkg.Revision=13
|
||||||
|
Archive.HostOs=WINDOWS
|
||||||
|
Extra.Path=usb_driver
|
||||||