Sync extraction fixes across installers

This commit is contained in:
2026-05-20 18:01:34 +08:00
parent d0a96ad578
commit 7340ecd283
8 changed files with 1037 additions and 632 deletions
+240 -150
View File
@@ -5,12 +5,14 @@ import os
import sys
import subprocess
import json
import re
import threading
import tkinter as tk
from tkinter import ttk, scrolledtext, filedialog, messagebox
from tkinter import ttk, scrolledtext, filedialog, messagebox, simpledialog
from pathlib import Path
from urllib.request import urlopen, Request
from urllib.error import URLError, HTTPError
from urllib.parse import urlencode
from datetime import datetime
import zipfile
try:
@@ -451,6 +453,7 @@ class ADKAPKGUI:
# 调试模式快捷键
self.root.bind('<Control-Shift-D>', self._toggle_debug)
self.root.bind('<Control-Shift-E>', self._debug_test_extract)
# 绑定悬停效果
self.bind_hover_effects()
@@ -518,6 +521,9 @@ class ADKAPKGUI:
"""将函数调度到主线程执行,确保线程安全"""
self.root.after(0, func, *args, **kwargs)
def _adb_cmd(self):
return subprocess.list2cmdline([self.adb])
def t(self, key):
return self.T.get(self.lang, self.T['zh']).get(key, key)
@@ -683,7 +689,7 @@ class ADKAPKGUI:
def monitor():
while True:
try:
result = subprocess.run(f'{self.adb} -d devices', shell=True, capture_output=True, text=True)
result = subprocess.run(f'{self._adb_cmd()} -d devices', shell=True, capture_output=True, text=True)
lines = result.stdout.strip().split('\n')
devices = [line for line in lines[1:] if line.strip() and 'device' in line and 'offline' not in line]
@@ -707,70 +713,115 @@ class ADKAPKGUI:
has_priv = self.priv_apps_dir and self.priv_apps_dir.exists() and len(list(self.priv_apps_dir.glob("*.apk"))) > 0
return has_app or has_priv
def _decode_7z_output(self, output):
for enc in ('gbk', 'utf-8'):
try:
return output.decode(enc)
except UnicodeDecodeError:
continue
return output.decode('utf-8', errors='replace')
def _seven_zip_supports_progress_stream(self):
try:
result = subprocess.run(
[self.sz],
capture_output=True,
text=True,
errors='ignore',
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
)
return '-bs{o|e|p}' in (result.stdout + result.stderr)
except Exception:
return False
def _extract_with_7za_progress(self):
self.update_progress(0, 100, "Loading resources...")
cmd = [
self.sz, 'x', str(self.package_file),
f'-p{self.extract_password}',
f'-o{self.temp_dir}', '-y'
]
if self._seven_zip_supports_progress_stream():
cmd.extend(['-bsp1', '-bso0', '-bse1'])
proc = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
stdin=subprocess.DEVNULL,
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0,
bufsize=0
)
output = bytearray()
last_percent = -1
while True:
chunk = proc.stdout.read(1) if proc.stdout else b''
if not chunk:
if proc.poll() is not None:
break
time.sleep(0.05)
continue
output.extend(chunk)
if len(output) > 60000:
del output[:-60000]
matches = re.findall(rb'(\d{1,3})%', bytes(output[-512:]))
if matches:
percent = min(100, int(matches[-1]))
if percent != last_percent:
last_percent = percent
self.update_progress(percent, 100, "Loading resources...")
return_code = proc.wait()
decoded_output = self._decode_7z_output(bytes(output))
if return_code == 0:
self.update_progress(100, 100, "Resources loaded")
return True, decoded_output
return False, decoded_output
def extract_package_silent(self):
"""静默解压语言包(带进度)"""
"""Extract package.bin silently with progress."""
if not self.package_file.exists():
self.log(f"错误:未找到资源包 ({self.package_file})", "ERROR")
self.log(f"Error: package not found ({self.package_file})", "ERROR")
return False
if not self.extract_password:
self.log("错误:解压密码未设置", "ERROR")
self.log("Error: extract password is not set", "ERROR")
return False
if not os.path.exists(self.sz):
self.log(f"错误:未找到 7za.exe ({self.sz})", "ERROR")
self.log(f"Error: 7za.exe not found ({self.sz})", "ERROR")
return False
try:
# 使用用户目录,无需管理员权限
local_appdata = os.environ.get('LOCALAPPDATA', os.path.expanduser('~\\AppData\\Local'))
hidden_path = Path(local_appdata) / ".cache" / "system" / ".android"
hidden_path.mkdir(parents=True, exist_ok=True)
self.temp_dir = hidden_path / "apps_cache_X5plus"
# 如果已存在,先清理
if self.temp_dir.exists():
shutil.rmtree(self.temp_dir, ignore_errors=True)
time.sleep(0.5)
self.temp_dir.mkdir(parents=True, exist_ok=True)
# 设置隐藏属性(Windows
if sys.platform == 'win32':
subprocess.run(f'attrib +h "{self.temp_dir.parent}"', shell=True, capture_output=True)
subprocess.run(f'attrib +h "{self.temp_dir}"', shell=True, capture_output=True)
self.log(f"正在解压资源包...", "INFO")
self.log("Preparing package...", "INFO")
# 使用 7za 解压(不用 text=True 避免编码问题)
self.update_progress(0, 1, "资源加载中...")
result = subprocess.run(
[self.sz, 'x', str(self.package_file),
f'-p{self.extract_password}',
f'-o{self.temp_dir}', '-y'],
capture_output=True,
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
)
if result.returncode != 0:
err_msg = ""
for output in [result.stderr, result.stdout]:
if output:
for enc in ['gbk', 'utf-8']:
try:
err_msg += output.decode(enc, errors='replace')
break
except:
continue
ok, err_msg = self._extract_with_7za_progress()
if not ok:
if err_msg.strip():
self.log(f"解压失败: {err_msg.strip()[:300]}", "ERROR")
self.log(f"Package preparation failed: {err_msg.strip()[:300]}", "ERROR")
else:
self.log(f"解压失败 (返回码: {result.returncode}),请检查密码是否正确", "ERROR")
self.log("Package preparation failed", "ERROR")
return False
self.update_progress(1, 1, "资源加载完成")
# 查找app和priv-app目录
self.apps_dir = None
self.priv_apps_dir = None
@@ -783,27 +834,25 @@ class ADKAPKGUI:
self.priv_apps_dir = priv_app_candidates[0]
if not self.apps_dir and not self.priv_apps_dir:
self.log("警告:未找到 app/priv-app 目录", "WARNING")
self.log("Warning: app/priv-app directory not found", "WARNING")
return False
apk_count = len(list(self.apps_dir.glob("*.apk"))) if self.apps_dir else 0
priv_count = len(list(self.priv_apps_dir.glob("*.apk"))) if self.priv_apps_dir else 0
self.log(f"资源准备完成 (app: {apk_count}, priv-app: {priv_count})", "SUCCESS")
self.log("Package prepared", "SUCCESS")
return True
except Exception as e:
if getattr(self, 'debug_mode', False):
self.log(f"资源准备失败: {str(e)}", "ERROR")
self.log(f"Package preparation failed: {str(e)}", "ERROR")
import traceback
self.log(traceback.format_exc(), "ERROR")
else:
self.log("资源准备失败,请检查网络连接后重试", "ERROR")
self.log("Package preparation failed, please check network and retry", "ERROR")
return False
def check_environment(self):
"""检查环境"""
try:
result = subprocess.run(f'{self.adb} version', shell=True, capture_output=True, text=True)
result = subprocess.run(f'{self._adb_cmd()} version', shell=True, capture_output=True, text=True)
if result.returncode == 0:
self.refresh_device_status()
if not self.package_file.exists():
@@ -843,88 +892,85 @@ class ADKAPKGUI:
# self.log("已复用缓存的资源文件", "INFO")
def refresh_device_status(self):
"""刷新设备状态"""
# 防止并发刷新
"""Refresh device status."""
if self._refreshing:
return
self._refreshing = True
def refresh():
was_connected = self.device_connected
try:
was_connected = self.device_connected
# 检查设备连接
result = subprocess.run(f'{self.adb} -d devices', shell=True, capture_output=True, text=True)
lines = result.stdout.strip().split('\n')
devices = [line for line in lines[1:] if line.strip() and 'device' in line and 'offline' not in line]
result = subprocess.run(f'{self._adb_cmd()} -d devices', shell=True, capture_output=True, text=True)
lines = result.stdout.strip().splitlines()
devices = [line for line in lines[1:] if line.strip() and 'device' in line and 'offline' not in line]
if devices:
# 只在首次连接时打日志
if not was_connected:
self.log("设备已连接", "SUCCESS")
if devices:
if not was_connected:
self.log("Device connected", "SUCCESS")
# 获取VIN — 兼容两种 key,过滤 Android null 返回值
vin = ''
for key in ('ca_vin_info', 'VIN'):
vin_result = subprocess.run(
f'{self.adb} -d shell settings get system {key}',
shell=True, capture_output=True, text=True)
vin = vin_result.stdout.strip()
if vin and vin != 'null':
break
vin = ''
if vin:
self.log(f"当前车辆VIN: {vin}", "INFO")
# 验证授权
authorized = self.check_authorization(vin)
self.update_device_status(True, vin, authorized)
for key in ('ca_vin_info', 'VIN'):
vin_result = subprocess.run(
f'{self._adb_cmd()} -d shell settings get system {key}',
shell=True, capture_output=True, text=True)
vin = vin_result.stdout.strip()
if vin and vin != 'null':
break
vin = ''
if vin:
self.log(f"Current VIN: {vin}", "INFO")
authorized = self.check_authorization(vin)
self.update_device_status(True, vin, authorized)
else:
self.log("Unable to read VIN", "WARNING")
self.update_device_status(True, None, False)
else:
self.log("无法获取VIN", "WARNING")
self.update_device_status(True, None, False)
else:
if was_connected:
self.log("设备未连接", "WARNING")
self.update_device_status(False)
self._refreshing = False
if was_connected:
self.log("Device disconnected", "WARNING")
self.update_device_status(False)
except Exception as e:
self.log(f"Refresh device status failed: {str(e)}", "ERROR")
finally:
self._refreshing = False
threading.Thread(target=refresh, daemon=True).start()
def check_authorization(self, vin):
"""检查授权"""
if self.debug_mode:
self.log("调试模式: 跳过授权验证", "WARNING")
"""Check authorization."""
if getattr(self, 'debug_mode', False):
self.log("Debug mode: skip authorization", "WARNING")
return True
self.log("正在验证授权...", "INFO")
self.log("Checking authorization...", "INFO")
try:
url = f"{self.api_url}?vin={vin}"
url = f"{self.api_url}?{urlencode({'vin': vin})}"
req = Request(url, method='GET', headers={'User-Agent': 'Mozilla/5.0'})
with urlopen(req, timeout=10) as response:
data = json.loads(response.read().decode('utf-8'))
if data.get('authorized') == True:
self.log("✅ 授权验证通过!", "SUCCESS")
self.log("Authorization passed", "SUCCESS")
if 'data' in data and 'vehicleName' in data['data']:
self.log(f"车辆名称: {data['data']['vehicleName']}", "INFO")
self.log(f"Vehicle name: {data['data']['vehicleName']}", "INFO")
return True
else:
self.log(f"❌ 授权验证失败", "ERROR")
self.log("Authorization failed", "ERROR")
return False
except Exception as e:
self.log(f"❌ 授权验证失败", "ERROR")
except Exception:
self.log("Authorization failed", "ERROR")
return False
def fetch_package_password(self):
"""从服务端获取资源包解压密码"""
"""Fetch package password from server."""
if not self.vin:
self.log("请先连接adb", "ERROR")
self.log("Please connect adb first", "ERROR")
return False
try:
pwd_api_url = "https://api.changan.softwindy.cn/api/authorizations/package-key"
url = f"{pwd_api_url}?vin={self.vin}"
url = f"{pwd_api_url}?{urlencode({'vin': self.vin})}"
req = Request(url, method='GET', headers={'User-Agent': 'Mozilla/5.0'})
with urlopen(req, timeout=10) as response:
@@ -934,16 +980,16 @@ class ADKAPKGUI:
self.extract_password = data['data']['password']
return True
else:
self.log(f"数据准备失败: {data.get('message', '未知错误')}", "ERROR")
self.log(f"Data preparation failed: {data.get('message', 'unknown error')}", "ERROR")
return False
except Exception as e:
self.log(f"数据准备失败: {str(e)}", "ERROR")
self.log(f"Data preparation failed: {str(e)}", "ERROR")
return False
def run_adb_command(self, command):
"""执行 adb 命令,静默执行,仅返回结果"""
command = command.replace('adb', self.adb, 1)
command = command.replace('adb', self._adb_cmd(), 1)
if self.debug_mode:
self.log(f"CMD: {command}", "CMD")
try:
@@ -1126,30 +1172,33 @@ class ADKAPKGUI:
self.show_progress(True, is_push=True)
total = len(apk_files)
self.log(f"开始批量安装 {total} 个APK...", "INFO")
self.run_adb_command('adb -d shell setprop vecentek.model 1')
success_count = 0
for i, apk_path in enumerate(apk_files, 1):
self.update_progress(i, total, "安装中...", is_push=True)
success, _ = self.run_adb_command(f'adb -d install -r "{apk_path}"')
if success:
success_count += 1
try:
self.run_adb_command('adb -d shell setprop vecentek.model 1')
self.run_adb_command('adb -d shell setprop vecentek.model 0')
self.update_progress(total, total, "安装完成", is_push=True)
self.show_progress(False, is_push=True)
for i, apk_path in enumerate(apk_files, 1):
self.update_progress(i, total, "安装中...", is_push=True)
success, _ = self.run_adb_command(f'adb -d install -r "{apk_path}"')
if success:
success_count += 1
if success_count == total:
self.log(f"安装完成:全部 {total} 个成功", "SUCCESS")
messagebox.showinfo("安装完成", f"成功安装 {total} 个APK")
elif success_count > 0:
self.log(f"安装完成:{success_count}/{total} 成功", "WARNING")
messagebox.showwarning("部分成功", f"成功: {success_count}\n失败: {total - success_count}")
else:
self.log("安装失败", "ERROR")
messagebox.showerror("安装失败", "所有APK安装失败!")
self.update_progress(total, total, "安装完成", is_push=True)
self.show_progress(False, is_push=True)
if success_count == total:
self.log(f"安装完成:全部 {total} 个成功", "SUCCESS")
self.run_on_ui_thread(messagebox.showinfo, "安装完成", f"成功安装 {total} 个APK")
elif success_count > 0:
self.log(f"安装完成:{success_count}/{total} 成功", "WARNING")
self.run_on_ui_thread(messagebox.showwarning, "部分成功", f"成功: {success_count}\n失败: {total - success_count}")
else:
self.log("安装失败", "ERROR")
self.run_on_ui_thread(messagebox.showerror, "安装失败", "所有APK安装失败!")
except Exception as e:
self.log(f"安装过程异常: {str(e)}", "ERROR")
self.run_on_ui_thread(messagebox.showerror, "安装失败", f"安装过程异常:{str(e)}")
finally:
self.run_adb_command('adb -d shell setprop vecentek.model 0')
self.show_progress(False, is_push=True)
threading.Thread(target=install, daemon=True).start()
@@ -1170,15 +1219,19 @@ class ADKAPKGUI:
def install():
self.show_progress(True, is_push=True)
self.update_progress(50, 100, f"安装中", is_push=True)
self.run_adb_command('adb -d shell setprop vecentek.model 1')
success, _ = self.run_adb_command(f'adb -d install -r "{file_path}"')
self.run_adb_command('adb -d shell setprop vecentek.model 0')
self.update_progress(100, 100, f"完成", is_push=True)
if success:
self.log("✓ 安装成功", "SUCCESS")
else:
self.log("✗ 安装失败", "ERROR")
self.show_progress(False, is_push=True)
try:
self.run_adb_command('adb -d shell setprop vecentek.model 1')
success, _ = self.run_adb_command(f'adb -d install -r "{file_path}"')
self.update_progress(100, 100, f"完成", is_push=True)
if success:
self.log("✓ 安装成功", "SUCCESS")
else:
self.log("✗ 安装失败", "ERROR")
except Exception as e:
self.log(f"安装过程异常: {str(e)}", "ERROR")
finally:
self.run_adb_command('adb -d shell setprop vecentek.model 0')
self.show_progress(False, is_push=True)
threading.Thread(target=install, daemon=True).start()
@@ -1287,13 +1340,14 @@ class ADKAPKGUI:
if success:
self.log(f"✓ 语言已设置为 {language_name}", "SUCCESS")
messagebox.showinfo(
self.run_on_ui_thread(
messagebox.showinfo,
"设置成功",
f"系统语言已设置为 {language_name}\n\n⚠️ 请重启设备使其生效。"
)
else:
self.log(f"✗ 语言设置失败: {output}", "ERROR")
messagebox.showerror("设置失败", f"语言设置失败!\n\n{output}")
self.run_on_ui_thread(messagebox.showerror, "设置失败", f"语言设置失败!\n\n{output}")
threading.Thread(target=do_set, daemon=True).start()
@@ -1319,7 +1373,7 @@ class ADKAPKGUI:
if not self.check_device_connection():
return
if messagebox.askyesno("确认重启", "确定要重启设备吗?"):
subprocess.Popen(f'{self.adb} -d shell reboot', shell=True,
subprocess.Popen(f'{self._adb_cmd()} -d shell reboot', shell=True,
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
self.log("设备正在重启...", "INFO")
self.update_device_status(False)
@@ -1349,10 +1403,10 @@ class ADKAPKGUI:
'adb -d shell pm disable-user --user 0 com.incall.apps.softmanager')
if success:
self.log("系统升级已禁用", "SUCCESS")
messagebox.showinfo("成功", "系统升级已成功禁用!")
self.run_on_ui_thread(messagebox.showinfo, "成功", "系统升级已成功禁用!")
else:
self.log("禁用系统升级失败", "ERROR")
messagebox.showerror("错误", f"禁用失败:{output}")
self.run_on_ui_thread(messagebox.showerror, "错误", f"禁用失败:{output}")
self.show_progress(False, is_push=False)
threading.Thread(target=disable, daemon=True).start()
@@ -1366,7 +1420,7 @@ class ADKAPKGUI:
self.refresh_device_status()
return
pwd = tk.simpledialog.askstring("调试模式", "请输入调试密码:", show='*', parent=self.root)
pwd = simpledialog.askstring("调试模式", "请输入调试密码:", show='*', parent=self.root)
if pwd == "zxch5200":
self.debug_mode = True
self.update_device_status(True, "", True)
@@ -1395,35 +1449,71 @@ class ADKAPKGUI:
def install():
self.show_progress(True, is_push=True)
self.log(f"开始安装 {count} 个APK...", "INFO")
self.run_adb_command('adb -d shell setprop vecentek.model 1')
success_count = 0
for i, file_path in enumerate(file_paths, 1):
apk_name = Path(file_path).stem
self.update_progress(i, count, f"安装中 ({apk_name})", is_push=True)
success, _ = self.run_adb_command(f'adb -d install -r "{file_path}"')
if success:
self.log(f"{apk_name}.apk", "SUCCESS")
success_count += 1
try:
self.run_adb_command('adb -d shell setprop vecentek.model 1')
for i, file_path in enumerate(file_paths, 1):
apk_name = Path(file_path).stem
self.update_progress(i, count, f"安装中 ({apk_name})", is_push=True)
success, _ = self.run_adb_command(f'adb -d install -r "{file_path}"')
if success:
self.log(f"{apk_name}.apk", "SUCCESS")
success_count += 1
else:
self.log(f"{apk_name}.apk", "ERROR")
self.update_progress(count, count, "安装完成", is_push=True)
if success_count == count:
self.log(f"安装完成:全部 {count} 个成功", "SUCCESS")
self.run_on_ui_thread(messagebox.showinfo, "安装完成", f"成功安装 {count} 个APK")
elif success_count > 0:
self.log(f"安装完成:{success_count}/{count} 成功", "WARNING")
self.run_on_ui_thread(messagebox.showwarning, "部分成功", f"成功: {success_count}\n失败: {count - success_count}")
else:
self.log(f"{apk_name}.apk", "ERROR")
self.run_adb_command('adb -d shell setprop vecentek.model 0')
self.update_progress(count, count, "安装完成", is_push=True)
self.show_progress(False, is_push=True)
if success_count == count:
self.log(f"安装完成:全部 {count} 个成功", "SUCCESS")
messagebox.showinfo("安装完成", f"成功安装 {count} 个APK")
elif success_count > 0:
self.log(f"安装完成:{success_count}/{count} 成功", "WARNING")
messagebox.showwarning("部分成功", f"成功: {success_count}\n失败: {count - success_count}")
else:
self.log("安装失败", "ERROR")
messagebox.showerror("安装失败", "所有APK安装失败!")
self.log("安装失败", "ERROR")
self.run_on_ui_thread(messagebox.showerror, "安装失败", "所有APK安装失败!")
except Exception as e:
self.log(f"安装过程异常: {str(e)}", "ERROR")
self.run_on_ui_thread(messagebox.showerror, "安装失败", f"安装过程异常:{str(e)}")
finally:
self.run_adb_command('adb -d shell setprop vecentek.model 0')
self.show_progress(False, is_push=True)
threading.Thread(target=install, daemon=True).start()
def _debug_test_extract(self, event=None):
"""Debug-only package extraction test."""
if not getattr(self, 'debug_mode', False):
messagebox.showwarning("Debug mode", "Press Ctrl+Shift+D to enable debug mode first")
return
pwd = simpledialog.askstring("Test extraction", "Enter package.bin password:", show='*', parent=self.root)
if not pwd:
return
def do_extract():
old_password = self.extract_password
self.extract_password = pwd
try:
self.show_progress(True, is_push=False)
if self.extract_package_silent():
self.log("Test extraction succeeded", "SUCCESS")
self.run_on_ui_thread(
messagebox.showinfo,
"Test extraction succeeded",
f"Resources extracted to:\n{self.temp_dir}"
)
else:
self.log("Test extraction failed", "ERROR")
self.run_on_ui_thread(messagebox.showerror, "Test extraction failed", "Check the 7za output in logs")
finally:
self.extract_password = old_password
self.show_progress(False, is_push=False)
threading.Thread(target=do_extract, daemon=True).start()
def run(self):
"""运行程序"""
self.root.mainloop()
+266 -172
View File
@@ -5,12 +5,14 @@ import os
import sys
import subprocess
import json
import re
import threading
import tkinter as tk
from tkinter import ttk, scrolledtext, filedialog, messagebox
from tkinter import ttk, scrolledtext, filedialog, messagebox, simpledialog
from pathlib import Path
from urllib.request import urlopen, Request
from urllib.error import URLError, HTTPError
from urllib.parse import urlencode
from datetime import datetime
import zipfile
try:
@@ -477,6 +479,7 @@ class ADKAPKGUI:
# 调试模式快捷键
self.root.bind('<Control-Shift-D>', self._toggle_debug)
self.root.bind('<Control-Shift-E>', self._debug_test_extract)
# ========== 右侧提示面板 ==========
# 热点信息卡片
@@ -591,6 +594,9 @@ class ADKAPKGUI:
"""将函数调度到主线程执行,确保线程安全"""
self.root.after(0, func, *args, **kwargs)
def _adb_cmd(self):
return subprocess.list2cmdline([self.adb])
def t(self, key):
return self.T.get(self.lang, self.T['zh']).get(key, key)
@@ -758,7 +764,7 @@ class ADKAPKGUI:
def monitor():
while True:
try:
result = subprocess.run(f'{self.adb} -d devices', shell=True, capture_output=True, text=True)
result = subprocess.run(f'{self._adb_cmd()} -d devices', shell=True, capture_output=True, text=True)
lines = result.stdout.strip().split('\n')
devices = [line for line in lines[1:] if line.strip() and 'device' in line and 'offline' not in line]
@@ -787,7 +793,7 @@ class ADKAPKGUI:
self.log(f"CMD: adb shell {shell_command}", "CMD")
try:
proc = subprocess.Popen(
f'{self.adb} -d shell {shell_command}',
f'{self._adb_cmd()} -d shell {shell_command}',
shell=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
@@ -830,7 +836,7 @@ class ADKAPKGUI:
def run_adb_command(self, command):
"""执行原始 adb 命令(adb push / adb install 等,无需 shell 密码)。
静默执行,不显示 adb 原始输出,仅返回结果。"""
command = command.replace('adb', self.adb, 1)
command = command.replace('adb', self._adb_cmd(), 1)
if self.debug_mode:
self.log(f"CMD: {command}", "CMD")
try:
@@ -853,91 +859,135 @@ class ADKAPKGUI:
has_app = self.apps_dir and self.apps_dir.exists() and len(list(self.apps_dir.glob("*.apk"))) > 0
return has_app
def _decode_7z_output(self, output):
for enc in ('gbk', 'utf-8'):
try:
return output.decode(enc)
except UnicodeDecodeError:
continue
return output.decode('utf-8', errors='replace')
def _seven_zip_supports_progress_stream(self):
try:
result = subprocess.run(
[self.sz],
capture_output=True,
text=True,
errors='ignore',
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
)
return '-bs{o|e|p}' in (result.stdout + result.stderr)
except Exception:
return False
def _extract_with_7za_progress(self):
self.update_progress(0, 100, "Loading resources...")
cmd = [
self.sz, 'x', str(self.package_file),
f'-p{self.extract_password}',
f'-o{self.temp_dir}', '-y'
]
if self._seven_zip_supports_progress_stream():
cmd.extend(['-bsp1', '-bso0', '-bse1'])
proc = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
stdin=subprocess.DEVNULL,
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0,
bufsize=0
)
output = bytearray()
last_percent = -1
while True:
chunk = proc.stdout.read(1) if proc.stdout else b''
if not chunk:
if proc.poll() is not None:
break
time.sleep(0.05)
continue
output.extend(chunk)
if len(output) > 60000:
del output[:-60000]
matches = re.findall(rb'(\d{1,3})%', bytes(output[-512:]))
if matches:
percent = min(100, int(matches[-1]))
if percent != last_percent:
last_percent = percent
self.update_progress(percent, 100, "Loading resources...")
return_code = proc.wait()
decoded_output = self._decode_7z_output(bytes(output))
if return_code == 0:
self.update_progress(100, 100, "Resources loaded")
return True, decoded_output
return False, decoded_output
def extract_package_silent(self):
"""静默解压语言包(带进度)—— 逸动版仅处理 app 目录"""
"""Extract package.bin silently with progress; app directory only."""
if not self.package_file.exists():
self.log(f"错误:未找到资源包 ({self.package_file})", "ERROR")
self.log(f"Error: package not found ({self.package_file})", "ERROR")
return False
if not self.extract_password:
self.log("错误:解压密码未设置", "ERROR")
self.log("Error: extract password is not set", "ERROR")
return False
if not os.path.exists(self.sz):
self.log(f"错误:未找到 7za.exe ({self.sz})", "ERROR")
self.log(f"Error: 7za.exe not found ({self.sz})", "ERROR")
return False
try:
# 使用用户目录,无需管理员权限
local_appdata = os.environ.get('LOCALAPPDATA', os.path.expanduser('~\\AppData\\Local'))
hidden_path = Path(local_appdata) / ".cache" / "system" / ".android"
hidden_path.mkdir(parents=True, exist_ok=True)
self.temp_dir = hidden_path / "apps_cache_common"
# 如果已存在,先清理
if self.temp_dir.exists():
shutil.rmtree(self.temp_dir, ignore_errors=True)
time.sleep(0.5)
self.temp_dir.mkdir(parents=True, exist_ok=True)
# 设置隐藏属性(Windows
if sys.platform == 'win32':
subprocess.run(f'attrib +h "{self.temp_dir.parent}"', shell=True, capture_output=True)
subprocess.run(f'attrib +h "{self.temp_dir}"', shell=True, capture_output=True)
self.log(f"正在解压资源包...", "INFO")
self.log("Preparing package...", "INFO")
# 使用 7za 解压(不用 text=True 避免编码问题)
self.update_progress(0, 1, "资源加载中...")
result = subprocess.run(
[self.sz, 'x', str(self.package_file),
f'-p{self.extract_password}',
f'-o{self.temp_dir}', '-y'],
capture_output=True,
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
)
if result.returncode != 0:
err_msg = ""
for output in [result.stderr, result.stdout]:
if output:
for enc in ['gbk', 'utf-8']:
try:
err_msg += output.decode(enc, errors='replace')
break
except:
continue
ok, err_msg = self._extract_with_7za_progress()
if not ok:
if err_msg.strip():
self.log(f"解压失败: {err_msg.strip()[:300]}", "ERROR")
self.log(f"Package preparation failed: {err_msg.strip()[:300]}", "ERROR")
else:
self.log(f"解压失败 (返回码: {result.returncode}),请检查密码是否正确", "ERROR")
self.log("Package preparation failed", "ERROR")
return False
self.update_progress(1, 1, "资源加载完成")
# 查找 app 目录(逸动无 priv-app
self.apps_dir = None
app_candidates = list(self.temp_dir.rglob("apps"))
app_candidates = list(self.temp_dir.rglob("apps")) or list(self.temp_dir.rglob("app"))
if app_candidates:
self.apps_dir = app_candidates[0]
if not self.apps_dir:
self.log("警告:未找到 apps 目录", "WARNING")
self.log("Warning: app/apps directory not found", "WARNING")
return False
apk_count = len(list(self.apps_dir.glob("*.apk")))
self.log(f"资源准备完成 (app: {apk_count})", "SUCCESS")
self.log(f"Package prepared (app: {apk_count})", "SUCCESS")
return True
except Exception as e:
if getattr(self, 'debug_mode', False):
self.log(f"资源准备失败: {str(e)}", "ERROR")
self.log(f"Package preparation failed: {str(e)}", "ERROR")
import traceback
self.log(traceback.format_exc(), "ERROR")
else:
self.log("资源准备失败,请检查网络连接后重试", "ERROR")
self.log("Package preparation failed, please check network and retry", "ERROR")
return False
def check_environment(self):
@@ -947,7 +997,7 @@ class ADKAPKGUI:
# 刷新热点显示
self.refresh_hotspot_display()
try:
result = subprocess.run(f'{self.adb} version', shell=True, capture_output=True, text=True)
result = subprocess.run(f'{self._adb_cmd()} version', shell=True, capture_output=True, text=True)
if result.returncode == 0:
self.refresh_device_status()
if not self.package_file.exists():
@@ -958,81 +1008,81 @@ class ADKAPKGUI:
self.log("未找到adb命令,请将ADB文件放入本目录", "ERROR")
def refresh_device_status(self, force=False):
"""刷新设备状态 —— 逸动版使用 ca.car.vin 获取 VIN"""
# 防止并发刷新(手动点击「检查」时强制忽略锁)
"""Refresh device status; Yidong uses ca.car.vin."""
if self._refreshing and not force:
return
self._refreshing = True
def refresh():
was_connected = self.device_connected
try:
was_connected = self.device_connected
# 检查设备连接
result = subprocess.run(f'{self.adb} -d devices', shell=True, capture_output=True, text=True)
lines = result.stdout.strip().split('\n')
devices = [line for line in lines[1:] if line.strip() and 'device' in line and 'offline' not in line]
result = subprocess.run(f'{self._adb_cmd()} -d devices', shell=True, capture_output=True, text=True)
lines = result.stdout.strip().splitlines()
devices = [line for line in lines[1:] if line.strip() and 'device' in line and 'offline' not in line]
if devices:
if not was_connected:
self.log("设备已连接", "SUCCESS")
if devices:
if not was_connected:
self.log("Device connected", "SUCCESS")
# 获取VIN —— 逸动车型使用 ca.car.vin
success, vin_output = self.run_adb_shell(
'settings get system ca.car.vin')
vin = vin_output.strip() if success else ''
success, vin_output = self.run_adb_shell('settings get system ca.car.vin')
vin = vin_output.strip() if success else ''
if vin == 'null':
vin = ''
if vin:
self.log(f"VIN: {vin}", "INFO")
authorized = self.check_authorization(vin)
self.update_device_status(True, vin, authorized)
if vin:
self.log(f"VIN: {vin}", "INFO")
authorized = self.check_authorization(vin)
self.update_device_status(True, vin, authorized)
else:
self.log("Unable to read VIN, please confirm factory mode", "WARNING")
self.update_device_status(True, None, False)
else:
self.log("无法获取VIN,请确认设备已进入工厂模式", "WARNING")
self.update_device_status(True, None, False)
else:
if was_connected:
self.log("设备未连接", "WARNING")
self.update_device_status(False)
self._refreshing = False
if was_connected:
self.log("Device disconnected", "WARNING")
self.update_device_status(False)
except Exception as e:
self.log(f"Refresh device status failed: {str(e)}", "ERROR")
finally:
self._refreshing = False
threading.Thread(target=refresh, daemon=True).start()
def check_authorization(self, vin):
"""检查授权"""
if self.debug_mode:
self.log("调试模式: 跳过授权验证", "WARNING")
"""Check authorization."""
if getattr(self, 'debug_mode', False):
self.log("Debug mode: skip authorization", "WARNING")
return True
self.log("正在验证授权状态...", "INFO")
self.log("Checking authorization...", "INFO")
try:
url = f"{self.api_url}?vin={vin}"
url = f"{self.api_url}?{urlencode({'vin': vin})}"
req = Request(url, method='GET', headers={'User-Agent': 'Mozilla/5.0'})
with urlopen(req, timeout=10) as response:
data = json.loads(response.read().decode('utf-8'))
if data.get('authorized') == True:
self.log("✅ 授权验证通过!", "SUCCESS")
self.log("Authorization passed", "SUCCESS")
if 'data' in data and 'vehicleName' in data['data']:
self.log(f"车辆名称: {data['data']['vehicleName']}", "INFO")
self.log(f"Vehicle name: {data['data']['vehicleName']}", "INFO")
return True
else:
self.log(f"❌ 授权验证失败", "ERROR")
self.log("Authorization failed", "ERROR")
return False
except Exception as e:
self.log(f"❌ 授权验证失败", "ERROR")
except Exception:
self.log("Authorization failed", "ERROR")
return False
def fetch_package_password(self):
"""从服务端获取资源包解压密码"""
"""Fetch package password from server."""
if not self.vin:
self.log("请先连接adb", "ERROR")
self.log("Please connect adb first", "ERROR")
return False
try:
pwd_api_url = "https://api.changan.softwindy.cn/api/authorizations/package-key"
url = f"{pwd_api_url}?vin={self.vin}"
url = f"{pwd_api_url}?{urlencode({'vin': self.vin})}"
req = Request(url, method='GET', headers={'User-Agent': 'Mozilla/5.0'})
with urlopen(req, timeout=10) as response:
@@ -1042,11 +1092,11 @@ class ADKAPKGUI:
self.extract_password = data['data']['password']
return True
else:
self.log(f"数据准备失败: {data.get('message', '未知错误')}", "ERROR")
self.log(f"Data preparation failed: {data.get('message', 'unknown error')}", "ERROR")
return False
except Exception as e:
self.log(f"数据准备失败: {str(e)}", "ERROR")
self.log(f"Data preparation failed: {str(e)}", "ERROR")
return False
def push_single_apk(self, apk_path, apk_name):
@@ -1111,33 +1161,33 @@ class ADKAPKGUI:
self.run_adb_shell('mkdir -p /data/local/tmp')
self.run_adb_shell('setprop vecentek.model 1')
all_apks = list(self.apps_dir.glob("*.apk"))
if not all_apks:
self.log("未找到语言包文件", "WARNING")
self.show_progress(False, is_push=True)
return
total = len(all_apks)
success_count = 0
for i, apk_path in enumerate(all_apks, 1):
apk_name = apk_path.stem
ok, _ = self.push_single_apk(apk_path, apk_name)
if ok:
self.log(f"安装成功: {apk_name}.apk", "SUCCESS")
success_count += 1
try:
all_apks = list(self.apps_dir.glob("*.apk"))
if not all_apks:
self.log("未找到语言包文件", "WARNING")
return
total = len(all_apks)
for i, apk_path in enumerate(all_apks, 1):
apk_name = apk_path.stem
ok, _ = self.push_single_apk(apk_path, apk_name)
if ok:
self.log(f"安装成功: {apk_name}.apk", "SUCCESS")
success_count += 1
else:
self.log(f"安装失败: {apk_name}.apk", "ERROR")
self.update_progress(i, total, "正在刷入...", is_push=True)
self.update_progress(total, total, "刷入完成", is_push=True)
if success_count > 0:
self.log("语言包刷入完成,重启设备后生效", "SUCCESS")
else:
self.log(f"安装失败: {apk_name}.apk", "ERROR")
self.update_progress(i, total, "正在刷入...", is_push=True)
self.update_progress(total, total, "刷入完成", is_push=True)
self.run_adb_shell('setprop vecentek.model 0')
if success_count > 0:
self.log("语言包刷入完成,重启设备后生效", "SUCCESS")
else:
self.log("语言包刷入失败", "ERROR")
self.show_progress(False, is_push=True)
self.log("语言包刷入失败", "ERROR")
finally:
self.run_adb_shell('setprop vecentek.model 0')
self.show_progress(False, is_push=True)
threading.Thread(target=do_push_all, daemon=True).start()
@@ -1179,30 +1229,34 @@ class ADKAPKGUI:
total = len(apk_files)
self.log(f"开始批量安装 {total} 个APK...", "INFO")
self.run_adb_shell('setprop vecentek.model 1')
success_count = 0
for i, apk_path in enumerate(apk_files, 1):
apk_name = apk_path.stem
self.update_progress(i, total, "安装中...", is_push=True)
if self._push_and_install(apk_path, apk_name):
self.log(f"安装成功: {apk_name}.apk", "SUCCESS")
success_count += 1
try:
self.run_adb_shell('setprop vecentek.model 1')
for i, apk_path in enumerate(apk_files, 1):
apk_name = apk_path.stem
self.update_progress(i, total, "安装中...", is_push=True)
if self._push_and_install(apk_path, apk_name):
self.log(f"安装成功: {apk_name}.apk", "SUCCESS")
success_count += 1
else:
self.log(f"安装失败: {apk_name}.apk", "ERROR")
self.update_progress(total, total, "安装完成", is_push=True)
if success_count == total:
self.run_on_ui_thread(messagebox.showinfo, "安装完成", f"成功安装 {total} 个APK")
elif success_count > 0:
self.run_on_ui_thread(messagebox.showwarning, "部分成功", f"成功: {success_count}\n失败: {total - success_count}")
else:
self.log(f"安装失败: {apk_name}.apk", "ERROR")
self.run_adb_shell('setprop vecentek.model 0')
self.update_progress(total, total, "安装完成", is_push=True)
self.show_progress(False, is_push=True)
if success_count == total:
messagebox.showinfo("安装完成", f"成功安装 {total} 个APK")
elif success_count > 0:
messagebox.showwarning("部分成功", f"成功: {success_count}\n失败: {total - success_count}")
messagebox.showwarning("部分成功", f"成功: {success_count}\n失败: {total - success_count}")
else:
self.log("安装失败", "ERROR")
messagebox.showerror("安装失败", "所有APK安装失败!")
self.log("安装失败", "ERROR")
self.run_on_ui_thread(messagebox.showerror, "安装失败", "所有APK安装失败!")
except Exception as e:
self.log(f"安装过程异常: {str(e)}", "ERROR")
self.run_on_ui_thread(messagebox.showerror, "安装失败", f"安装过程异常:{str(e)}")
finally:
self.run_adb_shell('setprop vecentek.model 0')
self.show_progress(False, is_push=True)
threading.Thread(target=install, daemon=True).start()
@@ -1230,16 +1284,19 @@ class ADKAPKGUI:
self.show_progress(True, is_push=True)
self.update_progress(30, 100, "安装中", is_push=True)
self.run_adb_shell('setprop vecentek.model 1')
success = self._push_and_install(file_path, apk_name)
self.run_adb_shell('setprop vecentek.model 0')
self.update_progress(100, 100, "完成", is_push=True)
if success:
self.log("安装成功", "SUCCESS")
else:
self.log("安装失败", "ERROR")
self.show_progress(False, is_push=True)
try:
self.run_adb_shell('setprop vecentek.model 1')
success = self._push_and_install(file_path, apk_name)
self.update_progress(100, 100, "完成", is_push=True)
if success:
self.log("安装成功", "SUCCESS")
else:
self.log("安装失败", "ERROR")
except Exception as e:
self.log(f"安装过程异常: {str(e)}", "ERROR")
finally:
self.run_adb_shell('setprop vecentek.model 0')
self.show_progress(False, is_push=True)
threading.Thread(target=install, daemon=True).start()
@@ -1348,13 +1405,14 @@ class ADKAPKGUI:
if success:
self.log(f"✓ 语言已设置为 {language_name}", "SUCCESS")
messagebox.showinfo(
self.run_on_ui_thread(
messagebox.showinfo,
"设置成功",
f"系统语言已设置为 {language_name}\n\n⚠️ 请重启设备使其生效。"
)
else:
self.log(f"✗ 语言设置失败: {output}", "ERROR")
messagebox.showerror("设置失败", f"语言设置失败!\n\n{output}")
self.run_on_ui_thread(messagebox.showerror, "设置失败", f"语言设置失败!\n\n{output}")
threading.Thread(target=do_set, daemon=True).start()
@@ -1380,7 +1438,7 @@ class ADKAPKGUI:
if not self.check_device_connection():
return
if messagebox.askyesno("确认重启", "确定要重启设备吗?"):
proc = subprocess.Popen(f'{self.adb} -d shell reboot', shell=True,
proc = subprocess.Popen(f'{self._adb_cmd()} -d shell reboot', shell=True,
stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
try:
proc.stdin.write(b'adb36987\n')
@@ -1417,10 +1475,10 @@ class ADKAPKGUI:
if success:
self.log("系统升级已禁用", "SUCCESS")
messagebox.showinfo("成功", "系统升级已成功禁用!")
self.run_on_ui_thread(messagebox.showinfo, "成功", "系统升级已成功禁用!")
else:
self.log("禁用系统升级失败", "ERROR")
messagebox.showerror("错误", f"禁用失败:{output}")
self.run_on_ui_thread(messagebox.showerror, "错误", f"禁用失败:{output}")
self.show_progress(False, is_push=False)
@@ -1448,7 +1506,7 @@ class ADKAPKGUI:
def do_query():
try:
api_url = "https://api.changan.softwindy.cn/api/authorizations/generate-password-by-vin"
url = f"{api_url}?vin={vin}"
url = f"{api_url}?{urlencode({'vin': vin})}"
req = Request(url, method='GET', headers={'User-Agent': 'Mozilla/5.0'})
with urlopen(req, timeout=10) as response:
@@ -1492,7 +1550,7 @@ class ADKAPKGUI:
self.refresh_device_status()
return
pwd = tk.simpledialog.askstring("调试模式", "请输入调试密码:", show='*', parent=self.root)
pwd = simpledialog.askstring("调试模式", "请输入调试密码:", show='*', parent=self.root)
if pwd == "zxch5200":
self.debug_mode = True
self.update_device_status(True, "", True)
@@ -1526,31 +1584,36 @@ class ADKAPKGUI:
def install():
self.show_progress(True, is_push=True)
self.log(f"开始安装 {count} 个APK...", "INFO")
self.run_adb_shell('setprop vecentek.model 1')
success_count = 0
for i, file_path in enumerate(file_paths, 1):
apk_name = Path(file_path).stem
self.update_progress(i, count, f"安装中 ({apk_name})", is_push=True)
if self._push_and_install(file_path, apk_name):
self.log(f"{apk_name}.apk", "SUCCESS")
success_count += 1
try:
self.run_adb_shell('setprop vecentek.model 1')
for i, file_path in enumerate(file_paths, 1):
apk_name = Path(file_path).stem
self.update_progress(i, count, f"安装中 ({apk_name})", is_push=True)
if self._push_and_install(file_path, apk_name):
self.log(f"{apk_name}.apk", "SUCCESS")
success_count += 1
else:
self.log(f"{apk_name}.apk", "ERROR")
self.update_progress(count, count, "安装完成", is_push=True)
if success_count == count:
self.log(f"安装完成:全部 {count} 个成功", "SUCCESS")
self.run_on_ui_thread(messagebox.showinfo, "安装完成", f"成功安装 {count} 个APK")
elif success_count > 0:
self.log(f"安装完成:{success_count}/{count} 成功", "WARNING")
self.run_on_ui_thread(messagebox.showwarning, "部分成功", f"成功: {success_count}\n失败: {count - success_count}")
else:
self.log(f"{apk_name}.apk", "ERROR")
self.run_adb_shell('setprop vecentek.model 0')
self.update_progress(count, count, "安装完成", is_push=True)
self.show_progress(False, is_push=True)
if success_count == count:
self.log(f"安装完成:全部 {count} 个成功", "SUCCESS")
messagebox.showinfo("安装完成", f"成功安装 {count} 个APK")
elif success_count > 0:
self.log(f"安装完成:{success_count}/{count} 成功", "WARNING")
messagebox.showwarning("部分成功", f"成功: {success_count}\n失败: {count - success_count}")
else:
self.log("安装失败", "ERROR")
messagebox.showerror("安装失败", "所有APK安装失败!")
self.log("安装失败", "ERROR")
self.run_on_ui_thread(messagebox.showerror, "安装失败", "所有APK安装失败!")
except Exception as e:
self.log(f"安装过程异常: {str(e)}", "ERROR")
self.run_on_ui_thread(messagebox.showerror, "安装失败", f"安装过程异常:{str(e)}")
finally:
self.run_adb_shell('setprop vecentek.model 0')
self.show_progress(False, is_push=True)
threading.Thread(target=install, daemon=True).start()
@@ -1738,6 +1801,37 @@ class ADKAPKGUI:
fg=self.colors['success'] if '已启动' in status else self.colors['warning']
)
def _debug_test_extract(self, event=None):
"""Debug-only package extraction test."""
if not getattr(self, 'debug_mode', False):
messagebox.showwarning("Debug mode", "Press Ctrl+Shift+D to enable debug mode first")
return
pwd = simpledialog.askstring("Test extraction", "Enter package.bin password:", show='*', parent=self.root)
if not pwd:
return
def do_extract():
old_password = self.extract_password
self.extract_password = pwd
try:
self.show_progress(True, is_push=False)
if self.extract_package_silent():
self.log("Test extraction succeeded", "SUCCESS")
self.run_on_ui_thread(
messagebox.showinfo,
"Test extraction succeeded",
f"Resources extracted to:\n{self.temp_dir}"
)
else:
self.log("Test extraction failed", "ERROR")
self.run_on_ui_thread(messagebox.showerror, "Test extraction failed", "Check the 7za output in logs")
finally:
self.extract_password = old_password
self.show_progress(False, is_push=False)
threading.Thread(target=do_extract, daemon=True).start()
def run(self):
"""运行程序"""
self.root.mainloop()
+277 -144
View File
@@ -5,12 +5,14 @@ import os
import sys
import subprocess
import json
import re
import threading
import tkinter as tk
from tkinter import ttk, scrolledtext, filedialog, messagebox
from tkinter import ttk, scrolledtext, filedialog, messagebox, simpledialog
from pathlib import Path
from urllib.request import urlopen, Request
from urllib.error import URLError, HTTPError
from urllib.parse import urlencode
from datetime import datetime
import zipfile
try:
@@ -142,6 +144,7 @@ class ADKAPKGUI:
self.api_url = "https://api.changan.softwindy.cn/api/authorizations/auth-check"
self.vin = None
self.device_connected = False
self.debug_mode = False # debug mode
self._refreshing = False # 防止并发刷新
# 设置样式
@@ -438,6 +441,10 @@ class ADKAPKGUI:
relief=tk.FLAT, cursor='hand2')
self.btn_lang_switch.pack(side=tk.RIGHT, padx=5)
# Debug shortcuts
self.root.bind('<Control-Shift-D>', self._toggle_debug)
self.root.bind('<Control-Shift-E>', self._debug_test_extract)
# ========== 右侧提示面板 ==========
# 热点信息卡片
hotspot_card = tk.Frame(right_frame, bg=self.colors['bg_dark'], relief=tk.RAISED, bd=1)
@@ -551,6 +558,9 @@ class ADKAPKGUI:
"""将函数调度到主线程执行,确保线程安全"""
self.root.after(0, func, *args, **kwargs)
def _adb_cmd(self):
return subprocess.list2cmdline([self.adb])
def t(self, key):
return self.T.get(self.lang, self.T['zh']).get(key, key)
@@ -705,6 +715,8 @@ class ADKAPKGUI:
def check_device_connection(self):
"""检查设备是否连接"""
if self.debug_mode:
return True
if not self.device_connected:
messagebox.showwarning("设备未连接", "请先连接设备并点击「检查」按钮刷新状态!")
return False
@@ -715,7 +727,7 @@ class ADKAPKGUI:
def monitor():
while True:
try:
result = subprocess.run('adb -d devices', shell=True, capture_output=True, text=True)
result = subprocess.run(f'{self._adb_cmd()} -d devices', shell=True, capture_output=True, text=True)
lines = result.stdout.strip().split('\n')
devices = [line for line in lines[1:] if line.strip() and 'device' in line and 'offline' not in line]
@@ -740,9 +752,11 @@ class ADKAPKGUI:
def run_adb_shell(self, shell_command):
"""执行 adb shell 命令,自动静默输入设备密码 adb36987。
静默执行,不显示 adb 原始输出,仅返回结果。"""
if self.debug_mode:
self.log(f"CMD: adb shell {shell_command}", "CMD")
try:
proc = subprocess.Popen(
f'adb -d shell {shell_command}',
f'{self._adb_cmd()} -d shell {shell_command}',
shell=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
@@ -763,15 +777,23 @@ class ADKAPKGUI:
output = '\n'.join(output_lines).strip()
if proc.returncode == 0:
if self.debug_mode:
self.log(f"CMD OK: {output[:200]}", "CMD")
return True, output
else:
if self.debug_mode:
self.log(f"CMD FAIL: {stderr.strip()[:200]}", "CMD")
return False, stderr.strip()
except subprocess.TimeoutExpired:
proc.kill()
proc.communicate()
if self.debug_mode:
self.log("CMD TIMEOUT", "CMD")
return False, "命令超时"
except Exception as e:
if self.debug_mode:
self.log(f"CMD ERROR: {str(e)}", "CMD")
return False, str(e)
# ============================================================
@@ -782,12 +804,21 @@ class ADKAPKGUI:
"""执行原始 adb 命令(adb push / adb install 等,无需 shell 密码)。
静默执行,不显示 adb 原始输出,仅返回结果。"""
try:
command = command.replace('adb', self._adb_cmd(), 1)
if self.debug_mode:
self.log(f"CMD: {command}", "CMD")
result = subprocess.run(command, shell=True, capture_output=True, text=True, encoding='utf-8')
if result.returncode == 0:
if self.debug_mode:
self.log(f"CMD OK: {result.stdout.strip()[:200]}", "CMD")
return True, result.stdout.strip()
else:
if self.debug_mode:
self.log(f"CMD FAIL: {result.stderr.strip()[:200]}", "CMD")
return False, result.stderr.strip()
except Exception as e:
if self.debug_mode:
self.log(f"CMD ERROR: {str(e)}", "CMD")
return False, str(e)
def check_package_extracted(self):
@@ -795,91 +826,135 @@ class ADKAPKGUI:
has_app = self.apps_dir and self.apps_dir.exists() and len(list(self.apps_dir.glob("*.apk"))) > 0
return has_app
def _decode_7z_output(self, output):
for enc in ('gbk', 'utf-8'):
try:
return output.decode(enc)
except UnicodeDecodeError:
continue
return output.decode('utf-8', errors='replace')
def _seven_zip_supports_progress_stream(self):
try:
result = subprocess.run(
[self.sz],
capture_output=True,
text=True,
errors='ignore',
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
)
return '-bs{o|e|p}' in (result.stdout + result.stderr)
except Exception:
return False
def _extract_with_7za_progress(self):
self.update_progress(0, 100, "Loading resources...")
cmd = [
self.sz, 'x', str(self.package_file),
f'-p{self.extract_password}',
f'-o{self.temp_dir}', '-y'
]
if self._seven_zip_supports_progress_stream():
cmd.extend(['-bsp1', '-bso0', '-bse1'])
proc = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
stdin=subprocess.DEVNULL,
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0,
bufsize=0
)
output = bytearray()
last_percent = -1
while True:
chunk = proc.stdout.read(1) if proc.stdout else b''
if not chunk:
if proc.poll() is not None:
break
time.sleep(0.05)
continue
output.extend(chunk)
if len(output) > 60000:
del output[:-60000]
matches = re.findall(rb'(\d{1,3})%', bytes(output[-512:]))
if matches:
percent = min(100, int(matches[-1]))
if percent != last_percent:
last_percent = percent
self.update_progress(percent, 100, "Loading resources...")
return_code = proc.wait()
decoded_output = self._decode_7z_output(bytes(output))
if return_code == 0:
self.update_progress(100, 100, "Resources loaded")
return True, decoded_output
return False, decoded_output
def extract_package_silent(self):
"""静默解压语言包(带进度)—— 逸动版仅处理 app 目录"""
"""Extract package.bin silently with progress; app directory only."""
if not self.package_file.exists():
self.log(f"错误:未找到资源包 ({self.package_file})", "ERROR")
self.log(f"Error: package not found ({self.package_file})", "ERROR")
return False
if not self.extract_password:
self.log("错误:解压密码未设置", "ERROR")
self.log("Error: extract password is not set", "ERROR")
return False
if not os.path.exists(self.sz):
self.log(f"错误:未找到 7za.exe ({self.sz})", "ERROR")
self.log(f"Error: 7za.exe not found ({self.sz})", "ERROR")
return False
try:
# 使用用户目录,无需管理员权限
local_appdata = os.environ.get('LOCALAPPDATA', os.path.expanduser('~\\AppData\\Local'))
hidden_path = Path(local_appdata) / ".cache" / "system" / ".android"
hidden_path.mkdir(parents=True, exist_ok=True)
self.temp_dir = hidden_path / "apps_cache_yidong"
# 如果已存在,先清理
if self.temp_dir.exists():
shutil.rmtree(self.temp_dir, ignore_errors=True)
time.sleep(0.5)
self.temp_dir.mkdir(parents=True, exist_ok=True)
# 设置隐藏属性(Windows
if sys.platform == 'win32':
subprocess.run(f'attrib +h "{self.temp_dir.parent}"', shell=True, capture_output=True)
subprocess.run(f'attrib +h "{self.temp_dir}"', shell=True, capture_output=True)
self.log(f"正在解压资源包...", "INFO")
self.log("Preparing package...", "INFO")
# 使用 7za 解压(不用 text=True 避免编码问题)
self.update_progress(0, 1, "资源加载中...")
result = subprocess.run(
[self.sz, 'x', str(self.package_file),
f'-p{self.extract_password}',
f'-o{self.temp_dir}', '-y'],
capture_output=True,
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
)
if result.returncode != 0:
err_msg = ""
for output in [result.stderr, result.stdout]:
if output:
for enc in ['gbk', 'utf-8']:
try:
err_msg += output.decode(enc, errors='replace')
break
except:
continue
ok, err_msg = self._extract_with_7za_progress()
if not ok:
if err_msg.strip():
self.log(f"解压失败: {err_msg.strip()[:300]}", "ERROR")
self.log(f"Package preparation failed: {err_msg.strip()[:300]}", "ERROR")
else:
self.log(f"解压失败 (返回码: {result.returncode}),请检查密码是否正确", "ERROR")
self.log("Package preparation failed", "ERROR")
return False
self.update_progress(1, 1, "资源加载完成")
# 查找 app 目录(逸动无 priv-app
self.apps_dir = None
app_candidates = list(self.temp_dir.rglob("apps"))
app_candidates = list(self.temp_dir.rglob("apps")) or list(self.temp_dir.rglob("app"))
if app_candidates:
self.apps_dir = app_candidates[0]
if not self.apps_dir:
self.log("警告:未找到 apps 目录", "WARNING")
self.log("Warning: app/apps directory not found", "WARNING")
return False
apk_count = len(list(self.apps_dir.glob("*.apk")))
self.log(f"资源准备完成 (app: {apk_count})", "SUCCESS")
self.log(f"Package prepared (app: {apk_count})", "SUCCESS")
return True
except Exception as e:
if getattr(self, 'debug_mode', False):
self.log(f"资源准备失败: {str(e)}", "ERROR")
self.log(f"Package preparation failed: {str(e)}", "ERROR")
import traceback
self.log(traceback.format_exc(), "ERROR")
else:
self.log("资源准备失败,请检查网络连接后重试", "ERROR")
self.log("Package preparation failed, please check network and retry", "ERROR")
return False
def check_environment(self):
@@ -889,7 +964,7 @@ class ADKAPKGUI:
# 刷新热点显示
self.refresh_hotspot_display()
try:
result = subprocess.run('adb version', shell=True, capture_output=True, text=True)
result = subprocess.run(f'{self._adb_cmd()} version', shell=True, capture_output=True, text=True)
if result.returncode == 0:
self.refresh_device_status()
if not self.package_file.exists():
@@ -900,78 +975,81 @@ class ADKAPKGUI:
self.log("未找到adb命令,请将ADB文件放入本目录", "ERROR")
def refresh_device_status(self, force=False):
"""刷新设备状态 —— 逸动版使用 ca.car.vin 获取 VIN"""
# 防止并发刷新(手动点击「检查」时强制忽略锁)
"""Refresh device status; Yidong uses ca.car.vin."""
if self._refreshing and not force:
return
self._refreshing = True
def refresh():
was_connected = self.device_connected
try:
was_connected = self.device_connected
# 检查设备连接
result = subprocess.run('adb -d devices', shell=True, capture_output=True, text=True)
lines = result.stdout.strip().split('\n')
devices = [line for line in lines[1:] if line.strip() and 'device' in line and 'offline' not in line]
result = subprocess.run(f'{self._adb_cmd()} -d devices', shell=True, capture_output=True, text=True)
lines = result.stdout.strip().splitlines()
devices = [line for line in lines[1:] if line.strip() and 'device' in line and 'offline' not in line]
if devices:
if not was_connected:
self.log("设备已连接", "SUCCESS")
if devices:
if not was_connected:
self.log("Device connected", "SUCCESS")
# 获取VIN —— 逸动车型使用 ca.car.vin
success, vin_output = self.run_adb_shell(
'settings get system ca.car.vin')
vin = vin_output.strip() if success else ''
success, vin_output = self.run_adb_shell('settings get system ca.car.vin')
vin = vin_output.strip() if success else ''
if vin == 'null':
vin = ''
if vin:
self.log(f"VIN: {vin}", "INFO")
authorized = self.check_authorization(vin)
self.update_device_status(True, vin, authorized)
if vin:
self.log(f"VIN: {vin}", "INFO")
authorized = self.check_authorization(vin)
self.update_device_status(True, vin, authorized)
else:
self.log("Unable to read VIN, please confirm factory mode", "WARNING")
self.update_device_status(True, None, False)
else:
self.log("无法获取VIN,请确认设备已进入工厂模式", "WARNING")
self.update_device_status(True, None, False)
else:
if was_connected:
self.log("设备未连接", "WARNING")
self.update_device_status(False)
self._refreshing = False
if was_connected:
self.log("Device disconnected", "WARNING")
self.update_device_status(False)
except Exception as e:
self.log(f"Refresh device status failed: {str(e)}", "ERROR")
finally:
self._refreshing = False
threading.Thread(target=refresh, daemon=True).start()
def check_authorization(self, vin):
"""检查授权"""
self.log("正在验证授权状态...", "INFO")
"""Check authorization."""
if getattr(self, 'debug_mode', False):
self.log("Debug mode: skip authorization", "WARNING")
return True
self.log("Checking authorization...", "INFO")
try:
url = f"{self.api_url}?vin={vin}"
url = f"{self.api_url}?{urlencode({'vin': vin})}"
req = Request(url, method='GET', headers={'User-Agent': 'Mozilla/5.0'})
with urlopen(req, timeout=10) as response:
data = json.loads(response.read().decode('utf-8'))
if data.get('authorized') == True:
self.log("✅ 授权验证通过!", "SUCCESS")
self.log("Authorization passed", "SUCCESS")
if 'data' in data and 'vehicleName' in data['data']:
self.log(f"车辆名称: {data['data']['vehicleName']}", "INFO")
self.log(f"Vehicle name: {data['data']['vehicleName']}", "INFO")
return True
else:
self.log(f"❌ 授权验证失败", "ERROR")
self.log("Authorization failed", "ERROR")
return False
except Exception as e:
self.log(f"❌ 授权验证失败", "ERROR")
except Exception:
self.log("Authorization failed", "ERROR")
return False
def fetch_package_password(self):
"""从服务端获取资源包解压密码"""
"""Fetch package password from server."""
if not self.vin:
self.log("请先连接adb", "ERROR")
self.log("Please connect adb first", "ERROR")
return False
try:
pwd_api_url = "https://api.changan.softwindy.cn/api/authorizations/package-key"
url = f"{pwd_api_url}?vin={self.vin}"
url = f"{pwd_api_url}?{urlencode({'vin': self.vin})}"
req = Request(url, method='GET', headers={'User-Agent': 'Mozilla/5.0'})
with urlopen(req, timeout=10) as response:
@@ -981,11 +1059,11 @@ class ADKAPKGUI:
self.extract_password = data['data']['password']
return True
else:
self.log(f"数据准备失败: {data.get('message', '未知错误')}", "ERROR")
self.log(f"Data preparation failed: {data.get('message', 'unknown error')}", "ERROR")
return False
except Exception as e:
self.log(f"数据准备失败: {str(e)}", "ERROR")
self.log(f"Data preparation failed: {str(e)}", "ERROR")
return False
def push_single_apk(self, apk_path, apk_name):
@@ -1050,33 +1128,33 @@ class ADKAPKGUI:
self.run_adb_shell('mkdir -p /data/local/tmp')
self.run_adb_shell('setprop vecentek.model 1')
all_apks = list(self.apps_dir.glob("*.apk"))
if not all_apks:
self.log("未找到语言包文件", "WARNING")
self.show_progress(False, is_push=True)
return
total = len(all_apks)
success_count = 0
for i, apk_path in enumerate(all_apks, 1):
apk_name = apk_path.stem
ok, _ = self.push_single_apk(apk_path, apk_name)
if ok:
self.log(f"安装成功: {apk_name}.apk", "SUCCESS")
success_count += 1
try:
all_apks = list(self.apps_dir.glob("*.apk"))
if not all_apks:
self.log("未找到语言包文件", "WARNING")
return
total = len(all_apks)
for i, apk_path in enumerate(all_apks, 1):
apk_name = apk_path.stem
ok, _ = self.push_single_apk(apk_path, apk_name)
if ok:
self.log(f"安装成功: {apk_name}.apk", "SUCCESS")
success_count += 1
else:
self.log(f"安装失败: {apk_name}.apk", "ERROR")
self.update_progress(i, total, "正在刷入...", is_push=True)
self.update_progress(total, total, "刷入完成", is_push=True)
if success_count > 0:
self._enable_overlays()
else:
self.log(f"安装失败: {apk_name}.apk", "ERROR")
self.update_progress(i, total, "正在刷入...", is_push=True)
self.update_progress(total, total, "刷入完成", is_push=True)
self.run_adb_shell('setprop vecentek.model 0')
if success_count > 0:
self._enable_overlays()
else:
self.log("语言包刷入失败", "ERROR")
self.show_progress(False, is_push=True)
self.log("语言包刷入失败", "ERROR")
finally:
self.run_adb_shell('setprop vecentek.model 0')
self.show_progress(False, is_push=True)
threading.Thread(target=do_push_all, daemon=True).start()
@@ -1131,30 +1209,34 @@ class ADKAPKGUI:
total = len(apk_files)
self.log(f"开始批量安装 {total} 个APK...", "INFO")
self.run_adb_shell('setprop vecentek.model 1')
success_count = 0
for i, apk_path in enumerate(apk_files, 1):
apk_name = apk_path.stem
self.update_progress(i, total, "安装中...", is_push=True)
if self._push_and_install(apk_path, apk_name):
self.log(f"安装成功: {apk_name}.apk", "SUCCESS")
success_count += 1
try:
self.run_adb_shell('setprop vecentek.model 1')
for i, apk_path in enumerate(apk_files, 1):
apk_name = apk_path.stem
self.update_progress(i, total, "安装中...", is_push=True)
if self._push_and_install(apk_path, apk_name):
self.log(f"安装成功: {apk_name}.apk", "SUCCESS")
success_count += 1
else:
self.log(f"安装失败: {apk_name}.apk", "ERROR")
self.update_progress(total, total, "安装完成", is_push=True)
if success_count == total:
self.run_on_ui_thread(messagebox.showinfo, "安装完成", f"成功安装 {total} 个APK")
elif success_count > 0:
self.run_on_ui_thread(messagebox.showwarning, "部分成功", f"成功: {success_count}\n失败: {total - success_count}")
else:
self.log(f"安装失败: {apk_name}.apk", "ERROR")
self.run_adb_shell('setprop vecentek.model 0')
self.update_progress(total, total, "安装完成", is_push=True)
self.show_progress(False, is_push=True)
if success_count == total:
messagebox.showinfo("安装完成", f"成功安装 {total} 个APK")
elif success_count > 0:
messagebox.showwarning("部分成功", f"成功: {success_count}\n失败: {total - success_count}")
messagebox.showwarning("部分成功", f"成功: {success_count}\n失败: {total - success_count}")
else:
self.log("安装失败", "ERROR")
messagebox.showerror("安装失败", "所有APK安装失败!")
self.log("安装失败", "ERROR")
self.run_on_ui_thread(messagebox.showerror, "安装失败", "所有APK安装失败!")
except Exception as e:
self.log(f"安装过程异常: {str(e)}", "ERROR")
self.run_on_ui_thread(messagebox.showerror, "安装失败", f"安装过程异常:{str(e)}")
finally:
self.run_adb_shell('setprop vecentek.model 0')
self.show_progress(False, is_push=True)
threading.Thread(target=install, daemon=True).start()
@@ -1182,16 +1264,19 @@ class ADKAPKGUI:
self.show_progress(True, is_push=True)
self.update_progress(30, 100, "安装中", is_push=True)
self.run_adb_shell('setprop vecentek.model 1')
success = self._push_and_install(file_path, apk_name)
self.run_adb_shell('setprop vecentek.model 0')
self.update_progress(100, 100, "完成", is_push=True)
if success:
self.log("安装成功", "SUCCESS")
else:
self.log("安装失败", "ERROR")
self.show_progress(False, is_push=True)
try:
self.run_adb_shell('setprop vecentek.model 1')
success = self._push_and_install(file_path, apk_name)
self.update_progress(100, 100, "完成", is_push=True)
if success:
self.log("安装成功", "SUCCESS")
else:
self.log("安装失败", "ERROR")
except Exception as e:
self.log(f"安装过程异常: {str(e)}", "ERROR")
finally:
self.run_adb_shell('setprop vecentek.model 0')
self.show_progress(False, is_push=True)
threading.Thread(target=install, daemon=True).start()
@@ -1300,13 +1385,14 @@ class ADKAPKGUI:
if success:
self.log(f"✓ 语言已设置为 {language_name}", "SUCCESS")
messagebox.showinfo(
self.run_on_ui_thread(
messagebox.showinfo,
"设置成功",
f"系统语言已设置为 {language_name}\n\n⚠️ 请重启设备使其生效。"
)
else:
self.log(f"✗ 语言设置失败: {output}", "ERROR")
messagebox.showerror("设置失败", f"语言设置失败!\n\n{output}")
self.run_on_ui_thread(messagebox.showerror, "设置失败", f"语言设置失败!\n\n{output}")
threading.Thread(target=do_set, daemon=True).start()
@@ -1332,7 +1418,7 @@ class ADKAPKGUI:
if not self.check_device_connection():
return
if messagebox.askyesno("确认重启", "确定要重启设备吗?"):
proc = subprocess.Popen(f'{self.adb} -d shell reboot', shell=True,
proc = subprocess.Popen(f'{self._adb_cmd()} -d shell reboot', shell=True,
stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
try:
proc.stdin.write(b'adb36987\n')
@@ -1369,10 +1455,10 @@ class ADKAPKGUI:
if success:
self.log("系统升级已禁用", "SUCCESS")
messagebox.showinfo("成功", "系统升级已成功禁用!")
self.run_on_ui_thread(messagebox.showinfo, "成功", "系统升级已成功禁用!")
else:
self.log("禁用系统升级失败", "ERROR")
messagebox.showerror("错误", f"禁用失败:{output}")
self.run_on_ui_thread(messagebox.showerror, "错误", f"禁用失败:{output}")
self.show_progress(False, is_push=False)
@@ -1562,6 +1648,53 @@ class ADKAPKGUI:
threading.Thread(target=poll, daemon=True).start()
def _toggle_debug(self, event=None):
"""Toggle debug mode."""
if self.debug_mode:
self.debug_mode = False
self.log("Debug mode disabled", "WARNING")
self.refresh_device_status(force=True)
return
pwd = simpledialog.askstring("Debug mode", "Enter debug password:", show='*', parent=self.root)
if pwd == "zxch5200":
self.debug_mode = True
self.log("Debug mode enabled", "WARNING")
self.update_device_status(True, self.vin or "DEBUG-VIN", True)
elif pwd:
messagebox.showwarning("Error", "Wrong password")
def _debug_test_extract(self, event=None):
"""Debug-only package extraction test."""
if not getattr(self, 'debug_mode', False):
messagebox.showwarning("Debug mode", "Press Ctrl+Shift+D to enable debug mode first")
return
pwd = simpledialog.askstring("Test extraction", "Enter package.bin password:", show='*', parent=self.root)
if not pwd:
return
def do_extract():
old_password = self.extract_password
self.extract_password = pwd
try:
self.show_progress(True, is_push=False)
if self.extract_package_silent():
self.log("Test extraction succeeded", "SUCCESS")
self.run_on_ui_thread(
messagebox.showinfo,
"Test extraction succeeded",
f"Resources extracted to:\n{self.temp_dir}"
)
else:
self.log("Test extraction failed", "ERROR")
self.run_on_ui_thread(messagebox.showerror, "Test extraction failed", "Check the 7za output in logs")
finally:
self.extract_password = old_password
self.show_progress(False, is_push=False)
threading.Thread(target=do_extract, daemon=True).start()
def run(self):
"""运行程序"""
self.root.mainloop()
+246 -158
View File
@@ -5,12 +5,14 @@ import os
import sys
import subprocess
import json
import re
import threading
import tkinter as tk
from tkinter import ttk, scrolledtext, filedialog, messagebox
from tkinter import ttk, scrolledtext, filedialog, messagebox, simpledialog
from pathlib import Path
from urllib.request import urlopen, Request
from urllib.error import URLError, HTTPError
from urllib.parse import urlencode
from datetime import datetime
import zipfile
try:
@@ -527,6 +529,7 @@ class ADKAPKGUI:
# 调试模式快捷键
self.root.bind('<Control-Shift-D>', self._toggle_debug)
self.root.bind('<Control-Shift-E>', self._debug_test_extract)
# 绑定悬停效果
self.bind_hover_effects()
@@ -575,6 +578,9 @@ class ADKAPKGUI:
"""将函数调度到主线程执行,确保线程安全"""
self.root.after(0, func, *args, **kwargs)
def _adb_cmd(self):
return subprocess.list2cmdline([self.adb])
def t(self, key):
"""获取翻译文本"""
return self.T.get(self.lang, self.T['zh']).get(key, key)
@@ -751,7 +757,7 @@ class ADKAPKGUI:
def monitor():
while True:
try:
result = subprocess.run(f'{self.adb} -d devices', shell=True, capture_output=True, text=True)
result = subprocess.run(f'{self._adb_cmd()} -d devices', shell=True, capture_output=True, text=True)
lines = result.stdout.strip().split('\n')
devices = [line for line in lines[1:] if line.strip() and 'device' in line and 'offline' not in line]
@@ -787,7 +793,7 @@ class ADKAPKGUI:
time.sleep(1)
# 执行 adb -d remount(需同时捕获 stdout 和 stderr
remount_result = subprocess.run(f'{self.adb} -d remount', shell=True,
remount_result = subprocess.run(f'{self._adb_cmd()} -d remount', shell=True,
capture_output=True, text=True)
if remount_result.returncode != 0:
self.log("获取权限失败", "ERROR")
@@ -817,72 +823,115 @@ class ADKAPKGUI:
has_priv = self.priv_apps_dir and self.priv_apps_dir.exists() and len(list(self.priv_apps_dir.glob("*.apk"))) > 0
return has_app or has_priv
def _decode_7z_output(self, output):
for enc in ('gbk', 'utf-8'):
try:
return output.decode(enc)
except UnicodeDecodeError:
continue
return output.decode('utf-8', errors='replace')
def _seven_zip_supports_progress_stream(self):
try:
result = subprocess.run(
[self.sz],
capture_output=True,
text=True,
errors='ignore',
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
)
return '-bs{o|e|p}' in (result.stdout + result.stderr)
except Exception:
return False
def _extract_with_7za_progress(self):
self.update_progress(0, 100, "Loading resources...")
cmd = [
self.sz, 'x', str(self.package_file),
f'-p{self.extract_password}',
f'-o{self.temp_dir}', '-y'
]
if self._seven_zip_supports_progress_stream():
cmd.extend(['-bsp1', '-bso0', '-bse1'])
proc = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
stdin=subprocess.DEVNULL,
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0,
bufsize=0
)
output = bytearray()
last_percent = -1
while True:
chunk = proc.stdout.read(1) if proc.stdout else b''
if not chunk:
if proc.poll() is not None:
break
time.sleep(0.05)
continue
output.extend(chunk)
if len(output) > 60000:
del output[:-60000]
matches = re.findall(rb'(\d{1,3})%', bytes(output[-512:]))
if matches:
percent = min(100, int(matches[-1]))
if percent != last_percent:
last_percent = percent
self.update_progress(percent, 100, "Loading resources...")
return_code = proc.wait()
decoded_output = self._decode_7z_output(bytes(output))
if return_code == 0:
self.update_progress(100, 100, "Resources loaded")
return True, decoded_output
return False, decoded_output
def extract_package_silent(self):
"""静默解压语言包(带进度)"""
"""Extract package.bin silently with progress."""
if not self.package_file.exists():
self.log(f"错误:未找到资源包 ({self.package_file})", "ERROR")
self.log(f"Error: package not found ({self.package_file})", "ERROR")
return False
if not self.extract_password:
self.log("错误:解压密码未设置", "ERROR")
self.log("Error: extract password is not set", "ERROR")
return False
# 检查 7za 是否存在
if not os.path.exists(self.sz):
self.log(f"错误:未找到 7za.exe ({self.sz})", "ERROR")
self.log(f"Error: 7za.exe not found ({self.sz})", "ERROR")
return False
try:
# 使用用户目录,无需管理员权限
local_appdata = os.environ.get('LOCALAPPDATA', os.path.expanduser('~\\AppData\\Local'))
hidden_path = Path(local_appdata) / ".cache" / "system" / ".android"
hidden_path.mkdir(parents=True, exist_ok=True)
self.temp_dir = hidden_path / "apps_cache_Q07"
# 如果已存在,先清理
if self.temp_dir.exists():
shutil.rmtree(self.temp_dir, ignore_errors=True)
time.sleep(0.5)
self.temp_dir.mkdir(parents=True, exist_ok=True)
# 设置隐藏属性(Windows
if sys.platform == 'win32':
subprocess.run(f'attrib +h "{self.temp_dir.parent}"', shell=True, capture_output=True)
subprocess.run(f'attrib +h "{self.temp_dir}"', shell=True, capture_output=True)
self.log(f"正在准备资源包", "INFO")
self.log("Preparing package...", "INFO")
# 使用 7za 解压(不用 text=True 避免编码问题)
self.update_progress(0, 1, "资源加载中...")
result = subprocess.run(
[self.sz, 'x', str(self.package_file),
f'-p{self.extract_password}',
f'-o{self.temp_dir}', '-y'],
capture_output=True,
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
)
if result.returncode != 0:
# 尝试解析错误信息(7za 输出可能是 GBK)
err_msg = ""
for output in [result.stderr, result.stdout]:
if output:
for enc in ['gbk', 'utf-8']:
try:
err_msg += output.decode(enc, errors='replace')
break
except:
continue
ok, err_msg = self._extract_with_7za_progress()
if not ok:
if err_msg.strip():
self.log(f"解压失败: {err_msg.strip()[:300]}", "ERROR")
self.log(f"Package preparation failed: {err_msg.strip()[:300]}", "ERROR")
else:
self.log(f"解压失败 (返回码: {result.returncode}),请检查密码是否正确", "ERROR")
self.log("Package preparation failed", "ERROR")
return False
self.update_progress(1, 1, "资源加载完成")
# 查找app和priv-app目录
self.apps_dir = None
self.priv_apps_dir = None
@@ -895,27 +944,25 @@ class ADKAPKGUI:
self.priv_apps_dir = priv_app_candidates[0]
if not self.apps_dir and not self.priv_apps_dir:
self.log("警告:未找到 app/priv-app 目录", "WARNING")
self.log("Warning: app/priv-app directory not found", "WARNING")
return False
apk_count = len(list(self.apps_dir.glob("*.apk"))) if self.apps_dir else 0
priv_count = len(list(self.priv_apps_dir.glob("*.apk"))) if self.priv_apps_dir else 0
self.log(f"资源准备完成 (app: {apk_count}, priv-app: {priv_count})", "SUCCESS")
self.log("Package prepared", "SUCCESS")
return True
except Exception as e:
if getattr(self, 'debug_mode', False):
self.log(f"资源准备失败: {str(e)}", "ERROR")
self.log(f"Package preparation failed: {str(e)}", "ERROR")
import traceback
self.log(traceback.format_exc(), "ERROR")
else:
self.log("资源准备失败,请检查网络连接后重试", "ERROR")
self.log("Package preparation failed, please check network and retry", "ERROR")
return False
def check_environment(self):
"""检查环境"""
try:
result = subprocess.run(f'{self.adb} version', shell=True, capture_output=True, text=True)
result = subprocess.run(f'{self._adb_cmd()} version', shell=True, capture_output=True, text=True)
if result.returncode == 0:
self.refresh_device_status()
if not self.package_file.exists():
@@ -955,88 +1002,85 @@ class ADKAPKGUI:
# self.log("已复用缓存的资源文件", "INFO")
def refresh_device_status(self):
"""刷新设备状态"""
# 防止并发刷新
"""Refresh device status."""
if self._refreshing:
return
self._refreshing = True
def refresh():
was_connected = self.device_connected
try:
was_connected = self.device_connected
# 检查设备连接
result = subprocess.run(f'{self.adb} -d devices', shell=True, capture_output=True, text=True)
lines = result.stdout.strip().split('\n')
devices = [line for line in lines[1:] if line.strip() and 'device' in line and 'offline' not in line]
result = subprocess.run(f'{self._adb_cmd()} -d devices', shell=True, capture_output=True, text=True)
lines = result.stdout.strip().splitlines()
devices = [line for line in lines[1:] if line.strip() and 'device' in line and 'offline' not in line]
if devices:
# 只在首次连接时打日志
if not was_connected:
self.log("设备已连接", "SUCCESS")
if devices:
if not was_connected:
self.log("Device connected", "SUCCESS")
# 获取VIN — 兼容两种 key,过滤 Android null 返回值
vin = ''
for key in ('ca_vin_info', 'VIN'):
vin_result = subprocess.run(
f'{self.adb} -d shell settings get system {key}',
shell=True, capture_output=True, text=True)
vin = vin_result.stdout.strip()
if vin and vin != 'null':
break
vin = ''
if vin:
self.log(f"当前车辆VIN: {vin}", "INFO")
# 验证授权
authorized = self.check_authorization(vin)
self.update_device_status(True, vin, authorized)
for key in ('ca_vin_info', 'VIN'):
vin_result = subprocess.run(
f'{self._adb_cmd()} -d shell settings get system {key}',
shell=True, capture_output=True, text=True)
vin = vin_result.stdout.strip()
if vin and vin != 'null':
break
vin = ''
if vin:
self.log(f"Current VIN: {vin}", "INFO")
authorized = self.check_authorization(vin)
self.update_device_status(True, vin, authorized)
else:
self.log("Unable to read VIN", "WARNING")
self.update_device_status(True, None, False)
else:
self.log("无法获取VIN", "WARNING")
self.update_device_status(True, None, False)
else:
if was_connected:
self.log("设备未连接", "WARNING")
self.update_device_status(False)
self._refreshing = False
if was_connected:
self.log("Device disconnected", "WARNING")
self.update_device_status(False)
except Exception as e:
self.log(f"Refresh device status failed: {str(e)}", "ERROR")
finally:
self._refreshing = False
threading.Thread(target=refresh, daemon=True).start()
def check_authorization(self, vin):
"""检查授权"""
if self.debug_mode:
self.log("调试模式: 跳过授权验证", "WARNING")
"""Check authorization."""
if getattr(self, 'debug_mode', False):
self.log("Debug mode: skip authorization", "WARNING")
return True
self.log("正在验证授权...", "INFO")
self.log("Checking authorization...", "INFO")
try:
url = f"{self.api_url}?vin={vin}"
url = f"{self.api_url}?{urlencode({'vin': vin})}"
req = Request(url, method='GET', headers={'User-Agent': 'Mozilla/5.0'})
with urlopen(req, timeout=10) as response:
data = json.loads(response.read().decode('utf-8'))
if data.get('authorized') == True:
self.log("✅ 授权验证通过!", "SUCCESS")
self.log("Authorization passed", "SUCCESS")
if 'data' in data and 'vehicleName' in data['data']:
self.log(f"车辆名称: {data['data']['vehicleName']}", "INFO")
self.log(f"Vehicle name: {data['data']['vehicleName']}", "INFO")
return True
else:
self.log(f"❌ 授权验证失败", "ERROR")
self.log("Authorization failed", "ERROR")
return False
except Exception as e:
self.log(f"❌ 授权验证失败", "ERROR")
except Exception:
self.log("Authorization failed", "ERROR")
return False
def fetch_package_password(self):
"""从服务端获取资源包解压密码"""
"""Fetch package password from server."""
if not self.vin:
self.log("请先连接adb", "ERROR")
self.log("Please connect adb first", "ERROR")
return False
try:
pwd_api_url = "https://api.changan.softwindy.cn/api/authorizations/package-key"
url = f"{pwd_api_url}?vin={self.vin}"
url = f"{pwd_api_url}?{urlencode({'vin': self.vin})}"
req = Request(url, method='GET', headers={'User-Agent': 'Mozilla/5.0'})
with urlopen(req, timeout=10) as response:
@@ -1046,16 +1090,16 @@ class ADKAPKGUI:
self.extract_password = data['data']['password']
return True
else:
self.log(f"数据准备失败: {data.get('message', '未知错误')}", "ERROR")
self.log(f"Data preparation failed: {data.get('message', 'unknown error')}", "ERROR")
return False
except Exception as e:
self.log(f"数据准备失败: {str(e)}", "ERROR")
self.log(f"Data preparation failed: {str(e)}", "ERROR")
return False
def run_adb_command(self, command):
"""执行 adb 命令,静默执行,仅返回结果"""
command = command.replace('adb', self.adb, 1)
command = command.replace('adb', self._adb_cmd(), 1)
if self.debug_mode:
self.log(f"CMD: {command}", "CMD")
try:
@@ -1213,30 +1257,33 @@ class ADKAPKGUI:
self.show_progress(True, is_push=True)
total = len(apk_files)
self.log(f"开始批量安装 {total} 个APK...", "INFO")
self.run_adb_command('adb -d shell setprop vecentek.model 1')
success_count = 0
for i, apk_path in enumerate(apk_files, 1):
self.update_progress(i, total, "安装中...", is_push=True)
success, _ = self.run_adb_command(f'adb -d install -r "{apk_path}"')
if success:
success_count += 1
try:
self.run_adb_command('adb -d shell setprop vecentek.model 1')
self.run_adb_command('adb -d shell setprop vecentek.model 0')
self.update_progress(total, total, "安装完成", is_push=True)
self.show_progress(False, is_push=True)
for i, apk_path in enumerate(apk_files, 1):
self.update_progress(i, total, "安装中...", is_push=True)
success, _ = self.run_adb_command(f'adb -d install -r "{apk_path}"')
if success:
success_count += 1
if success_count == total:
self.log(f"安装完成:全部 {total} 个成功", "SUCCESS")
messagebox.showinfo("安装完成", f"成功安装 {total} 个APK")
elif success_count > 0:
self.log(f"安装完成:{success_count}/{total} 成功", "WARNING")
messagebox.showwarning("部分成功", f"成功: {success_count}\n失败: {total - success_count}")
else:
self.log("安装失败", "ERROR")
messagebox.showerror("安装失败", "所有APK安装失败!")
self.show_progress(False, is_push=True)
self.update_progress(total, total, "安装完成", is_push=True)
if success_count == total:
self.log(f"安装完成:全部 {total} 个成功", "SUCCESS")
self.run_on_ui_thread(messagebox.showinfo, "安装完成", f"成功安装 {total} 个APK")
elif success_count > 0:
self.log(f"安装完成:{success_count}/{total} 成功", "WARNING")
self.run_on_ui_thread(messagebox.showwarning, "部分成功", f"成功: {success_count}\n失败: {total - success_count}")
else:
self.log("安装失败", "ERROR")
self.run_on_ui_thread(messagebox.showerror, "安装失败", "所有APK安装失败!")
except Exception as e:
self.log(f"安装过程异常: {str(e)}", "ERROR")
self.run_on_ui_thread(messagebox.showerror, "安装失败", f"安装过程异常:{str(e)}")
finally:
self.run_adb_command('adb -d shell setprop vecentek.model 0')
self.show_progress(False, is_push=True)
threading.Thread(target=install, daemon=True).start()
@@ -1257,15 +1304,19 @@ class ADKAPKGUI:
def install():
self.show_progress(True, is_push=True)
self.update_progress(50, 100, f"安装中", is_push=True)
self.run_adb_command('adb -d shell setprop vecentek.model 1')
success, _ = self.run_adb_command(f'adb -d install -r "{file_path}"')
self.run_adb_command('adb -d shell setprop vecentek.model 0')
self.update_progress(100, 100, f"完成", is_push=True)
if success:
self.log("✓ 安装成功", "SUCCESS")
else:
self.log("✗ 安装失败", "ERROR")
self.show_progress(False, is_push=True)
try:
self.run_adb_command('adb -d shell setprop vecentek.model 1')
success, _ = self.run_adb_command(f'adb -d install -r "{file_path}"')
self.update_progress(100, 100, f"完成", is_push=True)
if success:
self.log("✓ 安装成功", "SUCCESS")
else:
self.log("✗ 安装失败", "ERROR")
except Exception as e:
self.log(f"安装过程异常: {str(e)}", "ERROR")
finally:
self.run_adb_command('adb -d shell setprop vecentek.model 0')
self.show_progress(False, is_push=True)
threading.Thread(target=install, daemon=True).start()
@@ -1373,13 +1424,14 @@ class ADKAPKGUI:
if success:
self.log(f"✓ 语言已设置为 {language_name}", "SUCCESS")
messagebox.showinfo(
self.run_on_ui_thread(
messagebox.showinfo,
"设置成功",
f"系统语言已设置为 {language_name}\n\n⚠️ 请重启设备使其生效。"
)
else:
self.log(f"✗ 语言设置失败: {output}", "ERROR")
messagebox.showerror("设置失败", f"语言设置失败!\n\n{output}")
self.run_on_ui_thread(messagebox.showerror, "设置失败", f"语言设置失败!\n\n{output}")
threading.Thread(target=do_set, daemon=True).start()
@@ -1405,7 +1457,7 @@ class ADKAPKGUI:
if not self.check_device_connection():
return
if messagebox.askyesno("确认重启", "确定要重启设备吗?"):
subprocess.Popen(f'{self.adb} -d shell reboot', shell=True,
subprocess.Popen(f'{self._adb_cmd()} -d shell reboot', shell=True,
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
self.log("设备正在重启...", "INFO")
self.update_device_status(False)
@@ -1435,10 +1487,10 @@ class ADKAPKGUI:
'adb -d shell pm disable-user --user 0 com.incall.apps.softmanager')
if success:
self.log("系统升级已禁用", "SUCCESS")
messagebox.showinfo("成功", "系统升级已成功禁用!")
self.run_on_ui_thread(messagebox.showinfo, "成功", "系统升级已成功禁用!")
else:
self.log("禁用系统升级失败", "ERROR")
messagebox.showerror("错误", f"禁用失败:{output}")
self.run_on_ui_thread(messagebox.showerror, "错误", f"禁用失败:{output}")
self.show_progress(False, is_push=False)
threading.Thread(target=disable, daemon=True).start()
@@ -1452,7 +1504,7 @@ class ADKAPKGUI:
self.refresh_device_status()
return
pwd = tk.simpledialog.askstring("调试模式", "请输入调试密码:", show='*', parent=self.root)
pwd = simpledialog.askstring("调试模式", "请输入调试密码:", show='*', parent=self.root)
if pwd == "zxch5200":
self.debug_mode = True
self.update_device_status(True, "", True)
@@ -1481,35 +1533,71 @@ class ADKAPKGUI:
def install():
self.show_progress(True, is_push=True)
self.log(f"开始安装 {count} 个APK...", "INFO")
self.run_adb_command('adb -d shell setprop vecentek.model 1')
success_count = 0
for i, file_path in enumerate(file_paths, 1):
apk_name = Path(file_path).stem
self.update_progress(i, count, f"安装中 ({apk_name})", is_push=True)
success, _ = self.run_adb_command(f'adb -d install -r "{file_path}"')
if success:
self.log(f"{apk_name}.apk", "SUCCESS")
success_count += 1
try:
self.run_adb_command('adb -d shell setprop vecentek.model 1')
for i, file_path in enumerate(file_paths, 1):
apk_name = Path(file_path).stem
self.update_progress(i, count, f"安装中 ({apk_name})", is_push=True)
success, _ = self.run_adb_command(f'adb -d install -r "{file_path}"')
if success:
self.log(f"{apk_name}.apk", "SUCCESS")
success_count += 1
else:
self.log(f"{apk_name}.apk", "ERROR")
self.update_progress(count, count, "安装完成", is_push=True)
if success_count == count:
self.log(f"安装完成:全部 {count} 个成功", "SUCCESS")
self.run_on_ui_thread(messagebox.showinfo, "安装完成", f"成功安装 {count} 个APK")
elif success_count > 0:
self.log(f"安装完成:{success_count}/{count} 成功", "WARNING")
self.run_on_ui_thread(messagebox.showwarning, "部分成功", f"成功: {success_count}\n失败: {count - success_count}")
else:
self.log(f"{apk_name}.apk", "ERROR")
self.run_adb_command('adb -d shell setprop vecentek.model 0')
self.update_progress(count, count, "安装完成", is_push=True)
self.show_progress(False, is_push=True)
if success_count == count:
self.log(f"安装完成:全部 {count} 个成功", "SUCCESS")
messagebox.showinfo("安装完成", f"成功安装 {count} 个APK")
elif success_count > 0:
self.log(f"安装完成:{success_count}/{count} 成功", "WARNING")
messagebox.showwarning("部分成功", f"成功: {success_count}\n失败: {count - success_count}")
else:
self.log("安装失败", "ERROR")
messagebox.showerror("安装失败", "所有APK安装失败!")
self.log("安装失败", "ERROR")
self.run_on_ui_thread(messagebox.showerror, "安装失败", "所有APK安装失败!")
except Exception as e:
self.log(f"安装过程异常: {str(e)}", "ERROR")
self.run_on_ui_thread(messagebox.showerror, "安装失败", f"安装过程异常:{str(e)}")
finally:
self.run_adb_command('adb -d shell setprop vecentek.model 0')
self.show_progress(False, is_push=True)
threading.Thread(target=install, daemon=True).start()
def _debug_test_extract(self, event=None):
"""Debug-only package extraction test."""
if not getattr(self, 'debug_mode', False):
messagebox.showwarning("Debug mode", "Press Ctrl+Shift+D to enable debug mode first")
return
pwd = simpledialog.askstring("Test extraction", "Enter package.bin password:", show='*', parent=self.root)
if not pwd:
return
def do_extract():
old_password = self.extract_password
self.extract_password = pwd
try:
self.show_progress(True, is_push=False)
if self.extract_package_silent():
self.log("Test extraction succeeded", "SUCCESS")
self.run_on_ui_thread(
messagebox.showinfo,
"Test extraction succeeded",
f"Resources extracted to:\n{self.temp_dir}"
)
else:
self.log("Test extraction failed", "ERROR")
self.run_on_ui_thread(messagebox.showerror, "Test extraction failed", "Check the 7za output in logs")
finally:
self.extract_password = old_password
self.show_progress(False, is_push=False)
threading.Thread(target=do_extract, daemon=True).start()
def run(self):
"""运行程序"""
self.root.mainloop()
@@ -1530,4 +1618,4 @@ def main():
messagebox.showerror("错误", f"程序启动失败: {e}")
if __name__ == "__main__":
main()
main()
+2 -2
View File
@@ -62,7 +62,7 @@ copy ..\7za.exe . >nul
if exist "..\app.ico" copy "..\app.ico" . >nul
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-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
%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-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
@@ -79,7 +79,7 @@ goto :DONE
:NORMAL
cd /d "%~dp0"
echo [INFO] Normal 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;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --collect-all tkinter --uac-admin %SRC%
%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;." --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.
+2 -2
View File
@@ -62,7 +62,7 @@ copy ..\7za.exe . >nul
if exist "..\app.ico" copy "..\app.ico" . >nul
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-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
%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-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
@@ -79,7 +79,7 @@ goto :DONE
:NORMAL
cd /d "%~dp0"
echo [INFO] Normal 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;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --collect-all tkinter --uac-admin %SRC%
%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;." --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.
+2 -2
View File
@@ -62,7 +62,7 @@ copy ..\7za.exe . >nul
if exist "..\app.ico" copy "..\app.ico" . >nul
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-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
%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-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
@@ -79,7 +79,7 @@ goto :DONE
:NORMAL
cd /d "%~dp0"
echo [INFO] Normal 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;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --collect-all tkinter --uac-admin %SRC%
%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;." --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.
+2 -2
View File
@@ -62,7 +62,7 @@ copy ..\7za.exe . >nul
if exist "..\app.ico" copy "..\app.ico" . >nul
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-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
%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-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
@@ -79,7 +79,7 @@ goto :DONE
:NORMAL
cd /d "%~dp0"
echo [INFO] Normal 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;." --hidden-import=queue --hidden-import=threading --hidden-import=tkinter --hidden-import=zipfile --hidden-import=json --hidden-import=urllib --collect-all tkinter --uac-admin %SRC%
%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;." --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.