使用最新版的7z解压

This commit is contained in:
2026-05-20 13:57:41 +08:00
parent a99dad2116
commit cb840992ef
33 changed files with 3252 additions and 189 deletions
+2 -2
View File
@@ -3,13 +3,13 @@ from PyInstaller.utils.hooks import collect_all
datas = [('adb.exe', '.'), ('AdbWinApi.dll', '.'), ('AdbWinUsbApi.dll', '.'), ('7za.exe', '.')]
binaries = []
hiddenimports = ['queue', 'threading', 'tkinter', 'zipfile', 'json', 'urllib']
hiddenimports = ['queue', 'threading', 'tkinter', 'tkinter.simpledialog', 'zipfile', 'json', 'urllib', 'urllib.parse']
tmp_ret = collect_all('tkinter')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
a = Analysis(
['app-yidong.py'],
['S05_fixed.py'],
pathex=[],
binaries=binaries,
datas=datas,
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+85
View File
@@ -0,0 +1,85 @@
.Language=English,English
.PluginContents=7-Zip Plugin
@Contents
$^#7-Zip Plugin 26.01#
$^#Copyright (c) 1999-2026 Igor Pavlov#
This FAR module performs transparent #archive# processing.
Files in the archive are handled in the same manner as if they
were in a folder.
~Extracting from the archive~@Extract@
~Add files to the archive~@Update@
~7-Zip Plugin configuration~@Config@
Web site: #www.7-zip.org#
@Extract
$ #Extracting from the archive#
In this dialog you may enter extracting mode.
Path mode
#Full pathnames# Extract files with full pathnames.
#Current pathnames# Extract files with all relative paths.
#No pathnames# Extract files without folder paths.
Overwrite mode
#Ask before overwrite# Ask before overwriting existing files.
#Overwrite without prompt# Overwrite existing files without prompt.
#Skip existing files# Skip extracting of existing files.
Files
#Selected files# Extract only selected files.
#All files# Extract all files from archive.
@Update
$ #Add files to the archive#
This dialog allows you to specify options for process of updating archive.
Compression method
#Store# Files will be copied to archive without compression.
#Normal# Files will be compressed.
#Maximum# Files will be compressed with method that gives
maximum compression ratio.
Update mode
#Add and replace files# Add all specified files to the archive.
#Update and add files# Update older files in the archive and add
files that are new to the archive.
#Freshen existing files# Update specified files in the archive that
are older than the selected disk files.
#Synchronize files# Replace specified files only if
added files are newer. Always add those
files, which are not present in the
archive. Delete from archive those files,
which are not present on the disk.
@Config
$ #7-Zip Plugin configuration#
In this dialog you may change following parameters:
#Plugin is used by default# Plugin is used by default.
+220
View File
@@ -0,0 +1,220 @@
.Language=English,English
"Ok"
"&Cancel"
"Warning"
"Error"
"Format"
"Properties"
"Yes"
"No"
"Get password"
"Enter password"
"Extract"
"&Extract to"
"Path mode"
"&Full pathnames"
"C&urrent pathnames"
"&No pathnames"
"Overwrite mode"
"As&k before overwrite"
"&Overwrite without prompt"
"Sk&ip existing files"
"A&uto rename"
"A&uto rename existing files"
"Extract"
"&Selected files"
"A&ll files"
"&Password"
"Extr&act"
"&Cancel"
"Can not open output file '%s'."
"Unsupported compression method for '%s'."
"CRC failed in '%s'."
"Data error in '%s'."
"CRC failed in encrypted file '%s'. Wrong password?"
"Data error in encrypted file '%s'. Wrong password?"
"Confirm File Replace"
"Destination folder already contains processed file."
"Would you like to replace the existing file"
"with this one"
"bytes"
"modified on"
"&Yes"
"Yes to &All"
"&No"
"No to A&ll"
"A&uto rename"
"&Cancel"
"Update operations are not supported for this archive."
"Delete from archive"
"Delete \"%.40s\" from the archive"
"Delete selected files from the archive"
"Delete %d files from the archive"
"Delete"
"Cancel"
"Add files to archive"
"Add to %s a&rchive:"
"Compression method"
"&Store"
"Fas&test"
"&Fast"
"&Normal"
"&Maximum"
"&Ultra"
"Update mode"
"A&dd and replace files"
"&Update and add files"
"&Freshen existing files"
"S&ynchronize files"
"&Add"
"Se&lect archiver"
"Select archive format"
"Wait"
"Reading the archive"
"Extracting from the archive"
"Deleting from the archive"
"Updating the archive"
"Move operation is not supported"
"7-Zip"
"7-Zip (add to archive)"
"7-Zip"
"Plugin is used by default"
"0"
"1"
"2"
"Path"
"Name"
"Extension"
"Is Folder"
"Size"
"Packed Size"
"Attributes"
"Created"
"Accessed"
"Modified"
"Solid"
"Commented"
"Encrypted"
"Splited Before"
"Splited After"
"Dictionary Size"
"CRC"
"Type"
"Anti"
"Method"
"Host OS"
"File System"
"User"
"Group"
"Block"
"Comment"
"Position"
"Path Prefix"
"Folders"
"Files"
"Version"
"Volume"
"Multivolume"
"Offset"
"Links"
"Blocks"
"Volumes"
"Time Type"
"64-bit"
"Big-endian"
"CPU"
"Physical Size"
"Headers Size"
"Checksum"
"Characteristics"
"Virtual Address"
"ID"
"Short Name"
"Creator Application"
"Sector Size"
"Mode"
"Symbolic Link"
"Error"
"Total Size"
"Free Space"
"Cluster Size"
"Label"
"Local Name"
"Provider"
"NT Security"
"Alternate Stream"
"Aux"
"Deleted"
"Tree"
"SHA-1"
"SHA-256"
"Error Type"
"Errors"
"Errors"
"Warnings"
"Warning"
"Streams"
"Alternate Streams"
"Alternate Streams Size"
"Virtual Size"
"Unpack Size"
"Total Physical Size"
"Volume Index"
"SubType"
"Short Comment"
"Code Page"
"Is not archive type"
"Physical Size can't be detected"
"Zeros Tail Is Allowed"
"Tail Size"
"Embedded Stub Size"
"Link"
"Hard Link"
"iNode"
"Stream ID"
"Read-only"
"Out Name"
"Copy Link"
"Arc File Name"
"Is Hash"
"Metadata Changed"
"User ID"
"Group ID"
"Device Major"
"Device Minor"
"Dev Major"
"Dev Minor"
BIN
View File
Binary file not shown.
Binary file not shown.
+84
View File
@@ -0,0 +1,84 @@
.Language=Russian,Russian (ãá᪨©)
.PluginContents=« £¨­ 7-Zip
@Contents
$^#7-Zip Plugin 26.01#
$^#Copyright (c) 1999-2026 Igor Pavlov#
â®â ¬®¤ã«ì FAR ¯®§¢®«ï¥â à ¡®â âì á # à娢 ¬¨#. „«ï ¯®«ì§®¢ â¥«ï
ä ©«ë ¢  à娢 å ­¥ ®â«¨ç îâáï ®â ä ©«®¢ ¢ ¯ ¯ª å.
~ á¯ ª®¢ª  ä ©«®¢ ¨§  à娢 ~@Extract@
~„®¡ ¢«¥­¨¥ ä ©«®¢ ª  à娢ã~@Update@
~ à ¬¥âàë à ¡®âë á  à娢 ¬¨~@Config@
Web site: #www.7-zip.org#
@Extract
$ # á¯ ª®¢ª  ä ©«®¢ ¨§  à娢 #
‚ í⮬ ¤¨ «®£¥ ¢ë ¬®¦¥â¥ ¢¢¥á⨠¯ãâì ¤«ï à á¯ ª®¢ª¨ ä ©«®¢ ¨ § ¤ âì
०¨¬ à á¯ ª®¢ª¨.
ãâ¨
#®«­ë¥ ¯ãâ¨#  á¯ ª®¢ âì ä ©«ë á ¯®«­ë¬¨ ¯ãâﬨ.
#Žâ­®á¨â¥«ì­ë¥ ¯ãâ¨#  á¯ ª®¢ âì á ®â­®á¨â¥«ì­ë¬¨ ¯ãâﬨ.
#¥§ ¯ã⥩#  á¯ ª®¢ âì ¡¥§ ¯ã⥩.
¥à¥§ ¯¨áì
#‘¯à è¨¢ âì ¯®¤â¢¥à¦¤¥­¨¥# ‘¯à è¨¢ âì ¯®¤â¢¥à¦¤¥­¨¥ ­ 
¯¥à¥§ ¯¨áì áãé¥áâ¢ãî饣® ä ©« .
#¥§ ¯®¤â¢¥à¦¤¥­¨ï# ‡ ¬¥é âì áãé¥áâ¢ãî騩 ä ©«
¡¥§ ¯®¤â¢¥à¦¤¥­¨ï.
#யã᪠âì# யã᪠âì áãé¥áâ¢ãî騥 ä ©«ë.
 á¯ ª®¢ âì
#‚ë¡à ­­ë¥ ä ©«ë#  á¯ ª®¢ âì ⮫쪮 ¢ë¤¥«¥­­ë¥ ä ©«ë ¨§  à娢 .
#‚ᥠ䠩«ë#  á¯ ª®¢ âì ¢á¥ ä ©«ë ¨§  à娢 .
@Update
$ #„®¡ ¢«¥­¨¥ ä ©«®¢ ª  à娢ã#
‚ í⮬ ¤¨ «®£¥ ¢ë ¬®¦¥â¥ § ¤ âì ०¨¬ 㯠ª®¢ª¨.
Œ¥â®¤ ᦠâ¨ï:
#¥§ ᦠâ¨ï# ” ©«ë ¡ã¤ãâ ᪮¯¨à®¢ ­ë ¡¥§ ᦠâ¨ï.
#®à¬ «ì­®¥ ᦠ⨥# ” ©«ë ¡ã¤ãâ ᦠâë.
#Œ ªá¨¬ «ì­®¥ ᦠ⨥# ” ©«ë ¡ã¤ãâ ᦠâë á ¬ ªá¨¬ «ì­®©
á⥯¥­ìî ᦠâ¨ï.
¥¦¨¬ ¨§¬¥­¥­¨ï:
#„®¡ ¢¨âì ¨ § ¬¥­¨âì# „®¡ ¢¨âì ¢á¥ ¢ë¡à ­­ë¥ ä ©«ë ¢  à娢.
#Ž¡­®¢¨âì ¨ ¤®¡ ¢¨âì# Ž¡­®¢¨âì ãáâ à¥¢è¨¥ ä ©«ë ¢  à娢¥ ¨
¤®¡ ¢¨âì ä ©«ë, ª®â®àëå ­¥â ¢  à娢¥.
#Ž¡­®¢¨âì# Ž¡­®¢¨âì ãáâ à¥¢è¨¥ ä ©«ë ¢  à娢¥.
#‘¨­åà®­¨§¨à®¢ âì# ‘¨­åà®­¨§¨à®¢ âì ᮤ¥à¦¨¬®¥  à娢 
á ¢ë¡à ­­ë¬¨ ä ©« ¬¨.
@Config
$ # à ¬¥âàë à ¡®âë á ¯« £¨­®¬ 7-Zip#
‚ í⮬ ¤¨ «®£¥ ¢ë ¬®¦¥â¥ ¨§¬¥­¨âì á«¥¤ãî騥 ¯ à ¬¥âàë:
#« £¨­ ¨á¯®«ì§ã¥âáï ¯® 㬮«ç ­¨î# « £¨­ ¨á¯®«ì§ã¥âáï ¯® 㬮«ç ­¨î
+220
View File
@@ -0,0 +1,220 @@
.Language=Russian,Russian (ãá᪨©)
"த®«¦¨âì"
"&Žâ¬¥­¨âì"
"।ã¯à¥¦¤¥­¨¥"
"Žè¨¡ª "
"”®à¬ â"
"‘¢®©á⢠"
"„ "
"¥â"
"‚¢®¤ ¯ à®«ï"
"‚¢¥¤¨â¥ ¯ à®«ì"
" á¯ ª®¢ª "
"& á¯ ª®¢ âì ¢"
"ãâ¨"
"®&«­ë¥ ¯ãâ¨"
"Ž&â­®á¨â¥«ì­ë¥ ¯ãâ¨"
"&¥§ ¯ã⥩"
"¥à¥§ ¯¨áì"
"&‘¯à è¨¢ âì ¯®¤â¢¥à¦¤¥­¨¥"
"&¥§ ¯®¤â¢¥à¦¤¥­¨ï"
"ய&ã᪠âì"
"¥à¥¨¬¥­®¢ âì  ¢â®¬."
"¥à¥¨¬.  ¢â®¬. áãé¥áâ¢."
" á¯ ª®¢ âì"
"‚&ë¡à ­­ë¥ ä ©«ë"
"‚ᥠ&ä ©«ë"
"& à®«ì"
"& á¯ ª®¢ âì"
"&Žâ¬¥­¨âì"
"¥¢®§¬®¦­® ®âªàëâì ä ©« '%s'."
"¥¯®¤¤¥à¦¨¢ ¥¬ë© ¬¥â®¤ ᦠâ¨ï ¤«ï ä ©«  '%s'."
"Žè¨¡ª  CRC ¢ '%s'."
"Žè¨¡ª  ¢ ¤ ­­ëå ¢ '%s'."
"Žè¨¡ª  CRC ¤«ï § è¨ä஢ ­­®£® ä ©«  '%s'. ¥¢¥à­ë© ¯ à®«ì?"
"Žè¨¡ª  ¢ ¤ ­­ëå § è¨ä஢ ­­®£® ä ©«  '%s'. ¥¢¥à­ë© ¯ à®«ì?"
"®¤â¢¥à¤¨â¥ § ¬¥­ã ä ©« "
" ¯ª  㦥 ᮤ¥à¦¨â ®¡à ¡ â뢠¥¬ë© ä ©«."
"‡ ¬¥­¨âì áãé¥áâ¢ãî騩 ä ©«"
"á«¥¤ãî騬 ä ©«®¬"
"¡ ©â"
"¨§¬¥­¥­"
"&„ "
"„  ¤«ï &¢á¥å"
"&¥â"
"¥â ¤«ï ¢&á¥å"
"¥à¥¨¬¥­®¢ âì  ¢â®¬ â¨ç¥áª¨"
"&Žâ¬¥­¨âì"
"„«ï í⮣®  à娢  ®¯¥à æ¨¨ ¨§¬¥­¥­¨ï ­¥ ¯®¤¤¥à¦¨¢ îâáï."
"“¤ «¥­¨¥ ¨§  à娢 "
"“¤ «¨âì \"%.40s\" ¨§  à娢 "
"“¤ «¨âì ¢ë¡à ­­ë¥ ä ©«ë ¨§  à娢 "
"“¤ «¨âì %d ä ©«®¢ ¨§  à娢 "
"“¤ «¥­¨¥"
"Žâ¬¥­ "
"„®¡ ¢¨âì ä ©«ë ª  à娢ã"
"„®¡ ¢¨âì ª %s & à娢ã"
"Œ¥â®¤ ᦠâ¨ï"
"¥§ ᦠâ¨ï"
"‘ª®à®áâ­®©"
"ëáâàë©"
"®à¬ «ì­ë©"
"Œ ªá¨¬ «ì­ë©"
"“«ìâà "
"¥¦¨¬ ¨§¬¥­¥­¨ï"
"„®¡ ¢¨âì ¨ § ¬¥­¨âì"
"Ž¡­®¢¨âì ¨ ¤®¡ ¢¨âì"
"Ž¡­®¢¨âì"
"‘¨­åà®­¨§¨à®¢ âì"
"&„®¡ ¢¨âì"
"€&à娢 â®à"
"‚ë¡®à  à娢­®£® ä®à¬ â "
"®¤®¦¤¨â¥"
"—⥭¨¥  à娢 "
" á¯ ª®¢ª "
"“¤ «¥­¨¥"
"ˆ§¬¥­¥­¨¥"
"¥à¥¬¥é¥­¨¥ ä ©«®¢ ­¥ ¯®¤¤¥à¦¨¢ ¥âáï"
"7-Zip"
"7-Zip (¤®¡ ¢¨âì ¢  à娢)"
"7-Zip configuration"
"« £¨­ ¨á¯®«ì§ã¥âáï ¯® 㬮«ç ­¨î"
"0"
"1"
"2"
"ãâì"
"ˆ¬ï"
" áè¨à¥­¨¥"
" ¯ª "
" §¬¥à"
"‘¦ âë©"
"€âਡãâë"
"‘®§¤ ­"
"Žâªàëâ"
"ˆ§¬¥­¥­"
"¥¯à¥à뢭ë©"
"Š®¬¬¥­â à¨©"
"‡ è¨ä஢ ­"
" §¡¨â „®"
" §¡¨â ®á«¥"
"‘«®¢ àì"
"CRC"
"’¨¯"
"€­â¨"
"Œ¥â®¤"
"‘¨á⥬ "
"” ©«®¢ ï ‘¨á⥬ "
"®«ì§®¢ â¥«ì"
"ƒà㯯 "
"«®ª"
"Š®¬¬¥­â à¨©"
"®§¨æ¨ï"
"ãâì"
" ¯®ª"
"” ©«®¢"
"‚¥àá¨ï"
"’®¬"
"Œ­®£®â®¬­ë©"
"‘¬¥é¥­¨¥"
"‘áë«®ª"
"«®ª®¢"
"’®¬®¢"
"Time Type"
"64-bit"
"Big-endian"
"à®æ¥áá®à"
"”¨§¨ç¥áª¨©  §¬¥à"
" §¬¥à ‡ £®«®¢ª®¢"
"Š®­âà. ‘㬬 "
"• à ªâ¥à¨á⨪¨"
"‚¨àâã «ì­ë© €¤à¥á"
"ID"
"Š®à®âª®¥ ¨¬ï"
"ணࠬ¬ "
" §¬¥à ᥪâ®à "
"¥¦¨¬"
"‘¨¬¢®«ì­ ï ‘á뫪 "
"Žè¨¡ª "
"…¬ª®áâì"
"‘¢®¡®¤­®"
" §¬¥à ª« áâ¥à "
"Œ¥âª "
"‹®ª «ì­®¥ ¨¬ï"
"஢ ©¤¥à"
"NT ¥§®¯ á­®áâì"
"€«ìâ¥à­ â¨¢­ë© ®â®ª"
"Aux"
"“¤ «¥­­ë©"
"„¥à¥¢®"
"SHA-1"
"SHA-256"
"’¨¯ Žè¨¡ª¨"
"Žè¨¡ª¨"
"Žè¨¡ª¨"
"।ã¯à¥¦¤¥­¨ï"
"।ã¯à¥¦¤¥­¨¥"
"®â®ª¨"
"€«ìâ¥à­ â¨¢­ë¥ ®â®ª¨"
" §¬¥à €«ìâ¥à­ â¨¢­ëå ¯®â®ª®¢"
"‚¨àâã «ì­ë©  §¬¥à"
" á¯ ª®¢ ­­ë©  §¬¥à"
"ޡ鍩 ”¨§¨ç¥áª¨©  §¬¥à"
"®¬¥à ’®¬ "
"®¤â¨¯"
"Š®à®âª¨© Š®¬¬¥­â à¨©"
"Š®¤®¢ ï ‘âà ­¨æ "
"Is not archive type"
"Physical Size can't be detected"
"Zeros Tail Is Allowed"
" §¬¥à Žáâ âª "
" §¬¥à ‚áâ஥­­®£® «®ª "
"‘á뫪 "
"†¥áâª ï ‘á뫪 "
"iNode"
"ID ®â®ª "
"’®«ìª® ¤«ï ç⥭¨ï"
"Out Name"
"‘á뫪  ª®¯¨à®¢ ­¨ï"
"Arc File Name"
"Is Hash"
"ˆ§¬¥­¥­ë ¬¥â ¤ ­­ë¥"
"User ID"
"Group ID"
"Device Major"
"Device Minor"
"Dev Major"
"Dev Minor"
+67
View File
@@ -0,0 +1,67 @@
; 7z supporting for MutiArc in Far
; Append the following strings to file
; ..\Program Files\Far\Plugins\MultiArc\Formats\Custom.ini
[7z]
TypeName=7z
ID=37 7A BC AF 27 1C
IDPos=
IDOnly=1
Extension=7z
List=7z l -- %%AQ
Start="^-----"
End="^-----"
Format0="yyyy tt dd hh mm ss aaaaa zzzzzzzzzzzz pppppppppppp nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"
Extract=7z x {-p%%P} -r0 -y -scsDOS -i@%%LQMN -- %%A
ExtractWithoutPath=7z e {-p%%P} -r0 -y -scsDOS -i@%%LQMN -- %%A
Test=7z t {-p%%P} -r0 -scsDOS -i@%%LQMN -- %%A
Delete=7z d {-p%%P} -r0 -ms=off -scsDOS -i@%%LQMN -- %%A
Add=7z a {-p%%P} -r0 -t7z {%%S} -scsDOS -i@%%LQMN -- %%A
AddRecurse=7z a {-p%%P} -r0 -t7z {%%S} -scsDOS -i@%%LQMN -- %%A
AllFilesMask="*"
[rpm]
TypeName=rpm
ID=ED AB EE DB
IDPos=
IDOnly=1
Extension=rpm
List=7z l -- %%AQ
Start="^-----"
End="^-----"
Format0="yyyy tt dd hh mm ss aaaaa zzzzzzzzzzzz pppppppppppp nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"
Extract=7z x {-p%%P} -r0 -y -scsDOS -i@%%LQMN -- %%A
ExtractWithoutPath=7z e {-p%%P} -r0 -y -scsDOS -i@%%LQMN -- %%A
Test=7z t {-p%%P} -r0 -scsDOS -i@%%LQMN -- %%A
AllFilesMask="*"
[cpio]
TypeName=cpio
ID=
IDPos=
IDOnly=0
Extension=cpio
List=7z l -- %%AQ
Start="^-----"
End="^-----"
Format0="yyyy tt dd hh mm ss aaaaa zzzzzzzzzzzz pppppppppppp nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"
Extract=7z x {-p%%P} -r0 -y -scsDOS -i@%%LQMN -- %%A
ExtractWithoutPath=7z e {-p%%P} -r0 -y -scsDOS -i@%%LQMN -- %%A
Test=7z t {-p%%P} -r0 -scsDOS -i@%%LQMN -- %%A
AllFilesMask="*"
[deb]
TypeName=deb
ID=
IDPos=
IDOnly=0
Extension=deb
List=7z l -- %%AQ
Start="^-----"
End="^-----"
Format0="yyyy tt dd hh mm ss aaaaa zzzzzzzzzzzz pppppppppppp nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"
Extract=7z x {-p%%P} -r0 -y -scsDOS -i@%%LQMN -- %%A
ExtractWithoutPath=7z e {-p%%P} -r0 -y -scsDOS -i@%%LQMN -- %%A
Test=7z t {-p%%P} -r0 -scsDOS -i@%%LQMN -- %%A
AllFilesMask="*"
+67
View File
@@ -0,0 +1,67 @@
REGEDIT4
[HKEY_LOCAL_MACHINE\SOFTWARE\Far\Plugins\MultiArc\ZIP]
"Extract"="7z x {-p%%P} -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"ExtractWithoutPath"="7z e {-p%%P} -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Test"="7z t {-p%%P} -r0 -scsDOS -i@%%LQMN -- %%A"
"Delete"="7z d {-p%%P} -r0 {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Add"="7z a {-p%%P} -r0 -tzip {-w%%W} {%%S} -scsDOS -i@%%LQMN -- %%A"
"AddRecurse"="7z a {-p%%P} -r0 -tzip {-w%%W} {%%S} -scsDOS -i@%%LQMN -- %%A"
"AllFilesMask"="*"
[HKEY_LOCAL_MACHINE\SOFTWARE\Far\Plugins\MultiArc\TAR]
"Extract"="7z x -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"ExtractWithoutPath"="7z e -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Test"="7z t -r0 -scsDOS -i@%%LQMN -- %%A"
"Delete"="7z d -r0 {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Add"="7z a -r0 -y -ttar {-w%%W} {%%S} -scsDOS -i@%%LQMN -- %%A"
"AddRecurse"="7z a -r0 -y -ttar {-w%%W} {%%S} -scsDOS -i@%%LQMN -- %%A"
"AllFilesMask"="*"
[HKEY_LOCAL_MACHINE\SOFTWARE\Far\Plugins\MultiArc\GZIP]
"Extract"="7z x -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"ExtractWithoutPath"="7z e -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Test"="7z t -r0 -scsDOS -i@%%LQMN -- %%A"
"Delete"="7z d -r0 {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Add"="7z a -r0 -tgzip {-w%%W} {%%S} -scsDOS -i@%%LQMN -- %%A"
"AddRecurse"="7z a -r0 -tgzip {-w%%W} {%%S} -scsDOS -i@%%LQMN -- %%A"
"AllFilesMask"="*"
[HKEY_LOCAL_MACHINE\SOFTWARE\Far\Plugins\MultiArc\BZIP]
"Extract"="7z x -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"ExtractWithoutPath"="7z e -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Test"="7z t -r0 -scsDOS -i@%%LQMN -- %%A"
"Delete"="7z d -r0 {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Add"="7z a -r0 -tbzip2 {-w%%W} {%%S} -scsDOS -i@%%LQMN -- %%A"
"AddRecurse"="7z a -r0 -tbzip2 {-w%%W} {%%S} -scsDOS -i@%%LQMN -- %%A"
"AllFilesMask"="*"
[HKEY_LOCAL_MACHINE\SOFTWARE\Far\Plugins\MultiArc\ARJ]
"Extract"="7z x -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"ExtractWithoutPath"="7z e -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Test"="7z t -r0 -scsDOS -i@%%LQMN -- %%A"
"AllFilesMask"="*"
[HKEY_LOCAL_MACHINE\SOFTWARE\Far\Plugins\MultiArc\CAB]
"Extract"="7z x -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"ExtractWithoutPath"="7z e -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Test"="7z t -r0 -scsDOS -i@%%LQMN -- %%A"
"AllFilesMask"="*"
[HKEY_LOCAL_MACHINE\SOFTWARE\Far\Plugins\MultiArc\LZH]
"Extract"="7z x -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"ExtractWithoutPath"="7z e -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Test"="7z t -r0 -scsDOS -i@%%LQMN -- %%A"
"AllFilesMask"="*"
[HKEY_LOCAL_MACHINE\SOFTWARE\Far\Plugins\MultiArc\RAR]
"Extract"="7z x -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"ExtractWithoutPath"="7z e -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Test"="7z t -r0 -scsDOS -i@%%LQMN -- %%A"
"AllFilesMask"="*"
[HKEY_LOCAL_MACHINE\SOFTWARE\Far\Plugins\MultiArc\Z(Unix)]
"Extract"="7z x -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"ExtractWithoutPath"="7z e -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Test"="7z t -r0 -scsDOS -i@%%LQMN -- %%A"
"AllFilesMask"="*"
+73
View File
@@ -0,0 +1,73 @@
7-Zip Plugin for FAR Manager
----------------------------
FAR Manager is a file manager working in text mode.
You can download "FAR Manager" from site:
http://www.farmanager.com
Files:
far7z.txt - This file
far7z.reg - Regisrty file for MultiArc Plugin
7zToFar.ini - Supporting 7z for MultiArc Plugin
7-ZipFar.dll - 7-Zip Plugin for FAR Manager
7-ZipEng.hlf - Help file in English for FAR Manager
7-ZipRus.hlf - Help file in Russian for FAR Manager
7-ZipEng.lng - Plugin message strings in English for FAR Manager
7-ZipRus.lng - Plugin message strings in Russian for FAR Manager
There are two ways to use 7-Zip with FAR Manager:
1) Via 7-Zip FAR Plugin (it's recommended way).
2) Via standard MultiArc Plugin.
7-Zip FAR Plugin
~~~~~~~~~~~~~~~~
7-Zip FAR Plugin is first level plugin for FAR Manager, like MultiArc plugin.
It very fast extracts and updates files in archive, since it doesn't use
external programs. It supports all formats supported by 7-Zip:
7z, ZIP, RAR, CAB, ARJ, GZIP, BZIP2, Z, TAR, CPIO, RPM and DEB.
To install 7-Zip FAR Plugin:
1) Create "7-Zip" folder in ...\Program Files\Far\Plugins folder.
2) Copy all files from "FAR" folder of this package to created folder.
3) Install 7-Zip, or copy 7z.dll from 7-Zip to Program Files\Far\Plugins\7-Zip\
4) Restart FAR.
Also you must enable "OEM plugin support" option in FAR Manager:
1) F9 / Options / Plugins manager settings / check on "OEM plugin support".
2) F9 / Options / Save setup.
4) Restart FAR
You can open archives with one of the following ways:
* Pressing Enter.
* Pressing Ctrl-PgDown.
* Pressing F11 and selecting 7-Zip item.
You can create new archives with 7-Zip by pressing F11 and
selecting 7-Zip (add to archive) item.
If you think that some operations with archives is better to do with MultiArc Plugin,
you can disable 7-Zip plugin via Options / Pligin configuration / 7-Zip. In such mode
opening archives by pressing Enter and Ctrl-PgDown will start MultiArc Plugin. And
if you want to open archive with 7-Zip, press F11 and select 7-Zip item.
Using command line 7-Zip via MultiArc Plugin
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you want to use 7-Zip via MultiArc Plugin, you must
register file far7z.reg.
If you want to use 7z archives via MultiArc Plugin, you must
append contents of file Far\7zToFar.ini to file
..\Program Files\Far\Plugins\MultiArc\Formats\Custom.ini.
If you want to cancel using 7-Zip by MultiArc, just remove lines that contain
7-Zip (7z) program name from HKEY_LOCAL_MACHINE\SOFTWARE\Far\Plugins\MultiArc\ZIP
registry key.
+123
View File
@@ -0,0 +1,123 @@
7-Zip Extra
~~~~~~~~~~~
License for use and distribution
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Copyright (C) 1999-2026 Igor Pavlov.
The licenses for files are:
- 7za.exe:
- The "GNU LGPL" as main license for most of the code
- The "BSD 3-clause License" for some code
- The "BSD 2-clause License" for some code
- All other files: the "GNU LGPL".
Redistributions in binary form must reproduce related license information from this file.
Note:
You can use 7-Zip Extra on any computer, including a computer in a commercial
organization. You don't need to register or pay for 7-Zip.
It is allowed to digitally sign DLL and EXE files included into this package
with arbitrary signatures of third parties.
GNU LGPL information
--------------------
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You can receive a copy of the GNU Lesser General Public License from
http://www.gnu.org/
BSD 3-clause License in 7-Zip code
----------------------------------
The "BSD 3-clause License" is used for the following code in 7za.exe
- ZSTD data decompression.
that code was developed using original zstd decoder code as reference code.
The original zstd decoder code was developed by Facebook Inc,
that also uses the "BSD 3-clause License".
Copyright (c) Facebook, Inc. All rights reserved.
Copyright (c) 2023-2025 Igor Pavlov.
Text of the "BSD 3-clause License"
----------------------------------
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may
be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---
BSD 2-clause License in 7-Zip code
----------------------------------
The "BSD 2-clause License" is used for the XXH64 code in 7za.exe.
XXH64 code in 7-Zip was derived from the original XXH64 code developed by Yann Collet.
Copyright (c) 2012-2021 Yann Collet.
Copyright (c) 2023-2025 Igor Pavlov.
Text of the "BSD 2-clause License"
----------------------------------
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---
+230 -19
View File
@@ -28,7 +28,7 @@ class ADKAPKGUI:
self.root.resizable(True, True)
# 设置颜色主题
self.colors = {
self.colors_dark = {
'bg_dark': '#1e1e2e',
'bg_light': '#2a2a3e',
'accent': '#6c5ce7',
@@ -41,6 +41,92 @@ class ADKAPKGUI:
'text_secondary': '#b2bec3',
'border': '#3d3d5e'
}
self.colors_light = {
'bg_dark': '#f5f5f5',
'bg_light': '#ffffff',
'accent': '#6c5ce7',
'accent_hover': '#5b4bc4',
'success': '#00b894',
'error': '#d63031',
'warning': '#e17055',
'info': '#0984e3',
'text': '#2d3436',
'text_secondary': '#636e72',
'border': '#dfe6e9'
}
self.colors = dict(self.colors_dark)
self.theme = 'dark'
# 多语言
self.lang = 'zh'
self.T = {
'zh': {
'title': '深蓝S05多语言安装',
'btn_root': '🔓 获取权限',
'btn_push': '📦 刷入语言包',
'btn_install': '📱 安装App',
'btn_language': '🌐 语言设置',
'btn_timezone': '⏰ 时区设置',
'btn_settings': '⚙️ 安卓设置',
'btn_reboot': '🔄 重启设备',
'btn_disable_upgrade': '❌ 禁用升级',
'btn_clear_log': '🗑 清空日志',
'device_label': '设备:',
'vin_label': 'VIN码:',
'auth_label': '授权:',
'log_title': '📋 运行日志',
'status_ready': '就绪',
'status_connected': '已连接',
'status_disconnected': '未连接',
'status_detecting': '未检测',
'vin_none': '未获取',
'auth_none': '未验证',
'auth_yes': '已授权',
'auth_no': '未授权',
'btn_refresh': '🔄 检查',
'hint_factory': '🔧 工厂模式:拨号 *#*#888 ,密码:369875',
'theme_dark': '🌙 暗色',
'theme_light': '☀️ 亮色',
'lang_zh': '',
'lang_en': 'EN',
'switch_lang': '语言 / Language',
'switch_theme': '切换主题',
'about_company': '宜宾科宜科技有限公司 - 出口改装一站式服务',
},
'en': {
'title': 'Shenlan S05 Multi-Language',
'btn_root': '🔓 Get Root',
'btn_push': '📦 Flash Lang Pkg',
'btn_install': '📱 Install App',
'btn_language': '🌐 Language',
'btn_timezone': '⏰ Timezone',
'btn_settings': '⚙️ Settings',
'btn_reboot': '🔄 Reboot',
'btn_disable_upgrade': '❌ Disable OTA',
'btn_clear_log': '🗑 Clear Log',
'device_label': 'Device:',
'vin_label': 'VIN:',
'auth_label': 'Auth:',
'log_title': '📋 Log',
'status_ready': 'Ready',
'status_connected': 'Connected',
'status_disconnected': 'Disconnected',
'status_detecting': 'Detecting',
'vin_none': 'None',
'auth_none': 'Unknown',
'auth_yes': 'Authorized',
'auth_no': 'Unauthorized',
'btn_refresh': '🔄 Check',
'hint_factory': '🔧 Factory Mode: dial *#*#888, password: 369875',
'theme_dark': '🌙 Dark',
'theme_light': '☀️ Light',
'lang_zh': '',
'lang_en': 'EN',
'switch_lang': 'Language',
'switch_theme': 'Theme',
'about_company': 'Yibin Keyi Technology - Export Modification Service',
}
}
# 从 exe/py 所在目录查找资源文件
self.base_dir = Path(sys.executable).parent if getattr(sys, 'frozen', False) else Path(__file__).parent
@@ -393,6 +479,25 @@ class ADKAPKGUI:
bg=self.colors['bg_light'])
self.status_text.pack(side=tk.LEFT, padx=10)
# 主题和语言切换按钮
self.btn_theme_switch = tk.Button(bottom_status, text="🌙 暗色",
command=self.toggle_theme,
font=('Microsoft YaHei', 8),
fg=self.colors['accent'],
bg=self.colors['bg_light'],
relief=tk.FLAT,
cursor='hand2')
self.btn_theme_switch.pack(side=tk.RIGHT, padx=5)
self.btn_lang_switch = tk.Button(bottom_status, text="EN",
command=self.toggle_lang,
font=('Microsoft YaHei', 8, 'bold'),
fg=self.colors['accent'],
bg=self.colors['bg_light'],
relief=tk.FLAT,
cursor='hand2')
self.btn_lang_switch.pack(side=tk.RIGHT, padx=5)
# 调试模式快捷键
self.root.bind('<Control-Shift-D>', self._toggle_debug)
@@ -443,6 +548,81 @@ class ADKAPKGUI:
"""将函数调度到主线程执行,确保线程安全"""
self.root.after(0, func, *args, **kwargs)
def t(self, key):
"""获取翻译文本"""
return self.T.get(self.lang, self.T['zh']).get(key, key)
def toggle_lang(self):
"""切换语言"""
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._refresh_ui_texts()
self.log(f"语言已切换为 {'English' if self.lang == 'en' else '中文'}", "INFO")
def toggle_theme(self):
"""切换主题"""
if self.theme == 'dark':
self.colors = dict(self.colors_light)
self.theme = 'light'
self.btn_theme_switch.config(text=self.t('theme_dark'))
else:
self.colors = dict(self.colors_dark)
self.theme = 'dark'
self.btn_theme_switch.config(text=self.t('theme_light'))
self._apply_theme()
def _apply_theme(self):
"""应用当前主题到所有控件"""
c = self.colors
self.root.configure(bg=c['bg_dark'])
style = ttk.Style()
style.configure('TFrame', background=c['bg_dark'])
style.configure('TLabel', background=c['bg_dark'], foreground=c['text'])
style.configure('TLabelframe', background=c['bg_dark'], foreground=c['text'])
style.configure('TLabelframe.Label', background=c['bg_dark'], foreground=c['accent'])
style.configure('TProgressbar', background=c['accent'], troughcolor=c['bg_light'], borderwidth=0)
self.log_text.tag_config('INFO', foreground='#74b9ff')
self.log_text.tag_config('SUCCESS', foreground='#55efc4')
self.log_text.tag_config('ERROR', foreground='#ff7675')
self.log_text.tag_config('WARNING', foreground='#ffeaa7')
self.log_text.tag_config('CMD', foreground='#a29bfe')
if self.theme == 'light':
self.log_text.configure(bg='#ffffff', fg='#2d3436')
else:
self.log_text.configure(bg='#2d2d3d', fg='#e0e0e0')
def _refresh_ui_texts(self):
"""刷新所有UI文本"""
t = self.t
widgets = [
(getattr(self, 'title_label', None), 'title', None),
(getattr(self, 'subtitle_label', None), 'about_company', None),
(getattr(self, 'btn_root', None), 'btn_root', None),
(getattr(self, 'btn_push', None), 'btn_push', None),
(getattr(self, 'btn_install_all', None), 'btn_install', None),
(getattr(self, 'btn_language', None), 'btn_language', None),
(getattr(self, 'btn_timezone', None), 'btn_timezone', None),
(getattr(self, 'btn_settings', None), 'btn_settings', None),
(getattr(self, 'btn_reboot', None), 'btn_reboot', None),
(getattr(self, 'btn_exit', None), 'btn_disable_upgrade', None),
(getattr(self, 'btn_clear', None), 'btn_clear_log', None),
(getattr(self, 'log_title_label', None), 'log_title', None),
(getattr(self, 'status_text', None), 'status_ready', None),
(getattr(self, 'device_label', None), 'device_label', None),
(getattr(self, 'vin_label_title', None), 'vin_label', None),
(getattr(self, 'auth_label_title', None), 'auth_label', None),
(getattr(self, 'btn_refresh', None), 'btn_refresh', None),
(getattr(self, 'hint_label', None), 'hint_factory', None),
]
for w, key, _ in widgets:
if w:
w.config(text=t(key))
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'))
if self.vin:
self._update_device_status_impl(self.device_connected, self.vin,
getattr(self, '_last_authorized', False))
def _log_impl(self, message, level="INFO"):
"""日志写入的实际实现(必须在主线程调用)"""
timestamp = datetime.now().strftime("%H:%M:%S")
@@ -507,23 +687,25 @@ class ADKAPKGUI:
def _update_device_status_impl(self, connected, vin, authorized):
"""设备状态UI更新的实际实现(必须在主线程调用)"""
self._last_authorized = authorized
t = self.t
if connected:
self.status_indicator.itemconfig(self.status_dot, fill=self.colors['success'])
self.device_status_label.config(text="已连接", fg=self.colors['success'])
self.device_status_label.config(text=t('status_connected'), fg=self.colors['success'])
if vin:
self.vin_label.config(text=vin, fg=self.colors['success'])
if authorized:
self.auth_label.config(text="已授权", fg=self.colors['success'])
self.auth_label.config(text=t('auth_yes'), fg=self.colors['success'])
else:
self.auth_label.config(text="未授权", fg=self.colors['error'])
self.auth_label.config(text=t('auth_no'), fg=self.colors['error'])
else:
self.vin_label.config(text="未获取", fg=self.colors['error'])
self.auth_label.config(text="未验证", fg=self.colors['error'])
self.vin_label.config(text=t('vin_none'), fg=self.colors['error'])
self.auth_label.config(text=t('auth_none'), fg=self.colors['error'])
else:
self.status_indicator.itemconfig(self.status_dot, fill=self.colors['error'])
self.device_status_label.config(text="未连接", fg=self.colors['error'])
self.vin_label.config(text="未获取", fg=self.colors['error'])
self.auth_label.config(text="未验证", fg=self.colors['error'])
self.device_status_label.config(text=t('status_disconnected'), fg=self.colors['error'])
self.vin_label.config(text=t('vin_none'), fg=self.colors['error'])
self.auth_label.config(text=t('auth_none'), fg=self.colors['error'])
def check_device_connection(self):
"""检查设备是否连接"""
@@ -608,7 +790,15 @@ class ADKAPKGUI:
def extract_package_silent(self):
"""静默解压语言包(带进度)"""
if not self.package_file.exists():
self.log(f"未找到资源包 ({self.package_file.name})", "ERROR")
self.log(f"错误:未找到资源包 ({self.package_file})", "ERROR")
return False
if not self.extract_password:
self.log("错误:解压密码未设置", "ERROR")
return False
if not os.path.exists(self.sz):
self.log(f"错误:未找到 7za.exe ({self.sz})", "ERROR")
return False
try:
@@ -631,19 +821,32 @@ class ADKAPKGUI:
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")
# 使用 7za 高速解压
# 使用 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, text=True, creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
[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:
stderr = result.stderr.strip()
if stderr:
self.log(f"解压失败: {stderr[:200]}", "ERROR")
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
if err_msg.strip():
self.log(f"解压失败: {err_msg.strip()[:300]}", "ERROR")
else:
self.log("解压失败,请检查密码是否正确", "ERROR")
self.log(f"解压失败 (返回码: {result.returncode}),请检查密码是否正确", "ERROR")
return False
self.update_progress(1, 1, "资源加载完成")
@@ -660,13 +863,21 @@ 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("警告:未找到 app/priv-app 目录", "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")
return True
except Exception as e:
self.log(f"资源准备失败", "ERROR")
if getattr(self, 'debug_mode', False):
self.log(f"资源准备失败: {str(e)}", "ERROR")
import traceback
self.log(traceback.format_exc(), "ERROR")
else:
self.log("资源准备失败,请检查网络连接后重试", "ERROR")
return False
def check_environment(self):
Binary file not shown.
Binary file not shown.
+225 -21
View File
@@ -28,7 +28,7 @@ class ADKAPKGUI:
self.root.resizable(True, True)
# 设置颜色主题
self.colors = {
self.colors_dark = {
'bg_dark': '#1e1e2e',
'bg_light': '#2a2a3e',
'accent': '#6c5ce7',
@@ -41,6 +41,84 @@ class ADKAPKGUI:
'text_secondary': '#b2bec3',
'border': '#3d3d5e'
}
self.colors_light = {
'bg_dark': '#f5f5f5',
'bg_light': '#ffffff',
'accent': '#6c5ce7',
'accent_hover': '#5b4bc4',
'success': '#00b894',
'error': '#d63031',
'warning': '#e17055',
'info': '#0984e3',
'text': '#2d3436',
'text_secondary': '#636e72',
'border': '#dfe6e9'
}
self.colors = dict(self.colors_dark)
self.theme = 'dark'
# 多语言
self.lang = 'zh'
self.T = {
'zh': {
'title': '适用于X5plus多语言安装',
'btn_push': '📦 刷入语言包',
'btn_install': '📱 安装App',
'btn_language': '🌐 语言设置',
'btn_timezone': '⏰ 时区设置',
'btn_settings': '⚙️ 安卓设置',
'btn_reboot': '🔄 重启设备',
'btn_disable_upgrade': '❌ 禁用升级',
'btn_clear_log': '🗑 清空日志',
'device_label': '设备:',
'vin_label': 'VIN码:',
'auth_label': '授权:',
'log_title': '📋 运行日志',
'status_ready': '就绪',
'status_connected': '已连接',
'status_disconnected': '未连接',
'status_detecting': '未检测',
'vin_none': '未获取',
'auth_none': '未验证',
'auth_yes': '已授权',
'auth_no': '未授权',
'btn_refresh': '🔄 检查',
'theme_dark': '🌙 暗色',
'theme_light': '☀️ 亮色',
'lang_zh': '',
'lang_en': 'EN',
'about_company': '宜宾科宜科技有限公司 - 智能设备管理平台',
},
'en': {
'title': 'X5plus Multi-Language',
'btn_push': '📦 Flash Lang Pkg',
'btn_install': '📱 Install App',
'btn_language': '🌐 Language',
'btn_timezone': '⏰ Timezone',
'btn_settings': '⚙️ Settings',
'btn_reboot': '🔄 Reboot',
'btn_disable_upgrade': '❌ Disable OTA',
'btn_clear_log': '🗑 Clear Log',
'device_label': 'Device:',
'vin_label': 'VIN:',
'auth_label': 'Auth:',
'log_title': '📋 Log',
'status_ready': 'Ready',
'status_connected': 'Connected',
'status_disconnected': 'Disconnected',
'status_detecting': 'Detecting',
'vin_none': 'None',
'auth_none': 'Unknown',
'auth_yes': 'Authorized',
'auth_no': 'Unauthorized',
'btn_refresh': '🔄 Check',
'theme_dark': '🌙 Dark',
'theme_light': '☀️ Light',
'lang_zh': '',
'lang_en': 'EN',
'about_company': 'Yibin Keyi Technology - Smart Device Platform',
}
}
# 从 exe/py 所在目录查找资源文件
self.base_dir = Path(sys.executable).parent if getattr(sys, 'frozen', False) else Path(__file__).parent
@@ -355,6 +433,22 @@ class ADKAPKGUI:
bg=self.colors['bg_light'])
self.status_text.pack(side=tk.LEFT, padx=10)
# 主题和语言切换按钮
self.btn_theme_switch = tk.Button(bottom_status, text="🌙 暗色",
command=self.toggle_theme,
font=('Microsoft YaHei', 8),
fg=self.colors['accent'],
bg=self.colors['bg_light'],
relief=tk.FLAT, cursor='hand2')
self.btn_theme_switch.pack(side=tk.RIGHT, padx=5)
self.btn_lang_switch = tk.Button(bottom_status, text="EN",
command=self.toggle_lang,
font=('Microsoft YaHei', 8, 'bold'),
fg=self.colors['accent'],
bg=self.colors['bg_light'],
relief=tk.FLAT, cursor='hand2')
self.btn_lang_switch.pack(side=tk.RIGHT, padx=5)
# 调试模式快捷键
self.root.bind('<Control-Shift-D>', self._toggle_debug)
@@ -406,7 +500,7 @@ class ADKAPKGUI:
now = datetime.now()
hour = now.hour # 如 14
minute_tens = now.minute // 10 # 如 58 → 5
return f"{minute_tens}0{hour}"
return f"{minute_tens}0{hour:02d}"
def _update_fac_pwd_display(self):
"""更新工程密码显示,每30秒刷新一次"""
@@ -424,6 +518,73 @@ class ADKAPKGUI:
"""将函数调度到主线程执行,确保线程安全"""
self.root.after(0, func, *args, **kwargs)
def t(self, key):
return self.T.get(self.lang, self.T['zh']).get(key, key)
def toggle_lang(self):
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._refresh_ui_texts()
self.log(f"语言已切换为 {'English' if self.lang == 'en' else '中文'}", "INFO")
def toggle_theme(self):
if self.theme == 'dark':
self.colors = dict(self.colors_light)
self.theme = 'light'
self.btn_theme_switch.config(text=self.t('theme_dark'))
else:
self.colors = dict(self.colors_dark)
self.theme = 'dark'
self.btn_theme_switch.config(text=self.t('theme_light'))
self._apply_theme()
def _apply_theme(self):
c = self.colors
self.root.configure(bg=c['bg_dark'])
style = ttk.Style()
style.configure('TFrame', background=c['bg_dark'])
style.configure('TLabel', background=c['bg_dark'], foreground=c['text'])
style.configure('TLabelframe', background=c['bg_dark'], foreground=c['text'])
style.configure('TLabelframe.Label', background=c['bg_dark'], foreground=c['accent'])
style.configure('TProgressbar', background=c['accent'], troughcolor=c['bg_light'], borderwidth=0)
self.log_text.tag_config('INFO', foreground='#74b9ff')
self.log_text.tag_config('SUCCESS', foreground='#55efc4')
self.log_text.tag_config('ERROR', foreground='#ff7675')
self.log_text.tag_config('WARNING', foreground='#ffeaa7')
self.log_text.tag_config('CMD', foreground='#a29bfe')
if self.theme == 'light':
self.log_text.configure(bg='#ffffff', fg='#2d3436')
else:
self.log_text.configure(bg='#2d2d3d', fg='#e0e0e0')
def _refresh_ui_texts(self):
t = self.t
widgets = [
(getattr(self, 'title_label', None), 'title', None),
(getattr(self, 'subtitle_label', None), 'about_company', None),
(getattr(self, 'btn_push', None), 'btn_push', None),
(getattr(self, 'btn_install_all', None), 'btn_install', None),
(getattr(self, 'btn_language', None), 'btn_language', None),
(getattr(self, 'btn_timezone', None), 'btn_timezone', None),
(getattr(self, 'btn_settings', None), 'btn_settings', None),
(getattr(self, 'btn_reboot', None), 'btn_reboot', None),
(getattr(self, 'btn_exit', None), 'btn_disable_upgrade', None),
(getattr(self, 'btn_clear', None), 'btn_clear_log', None),
(getattr(self, 'log_title_label', None), 'log_title', None),
(getattr(self, 'status_text', None), 'status_ready', None),
(getattr(self, 'device_label', None), 'device_label', None),
(getattr(self, 'vin_label_title', None), 'vin_label', None),
(getattr(self, 'auth_label_title', None), 'auth_label', None),
(getattr(self, 'btn_refresh', None), 'btn_refresh', None),
]
for w, key, _ in widgets:
if w: w.config(text=t(key))
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'))
if self.vin:
self._update_device_status_impl(self.device_connected, self.vin,
getattr(self, '_last_authorized', False))
def _log_impl(self, message, level="INFO"):
"""日志写入的实际实现(必须在主线程调用)"""
timestamp = datetime.now().strftime("%H:%M:%S")
@@ -488,23 +649,25 @@ class ADKAPKGUI:
def _update_device_status_impl(self, connected, vin, authorized):
"""设备状态UI更新的实际实现(必须在主线程调用)"""
self._last_authorized = authorized
t = self.t
if connected:
self.status_indicator.itemconfig(self.status_dot, fill=self.colors['success'])
self.device_status_label.config(text="已连接", fg=self.colors['success'])
self.device_status_label.config(text=t('status_connected'), fg=self.colors['success'])
if vin:
self.vin_label.config(text=vin, fg=self.colors['success'])
if authorized:
self.auth_label.config(text="已授权", fg=self.colors['success'])
self.auth_label.config(text=t('auth_yes'), fg=self.colors['success'])
else:
self.auth_label.config(text="未授权", fg=self.colors['error'])
self.auth_label.config(text=t('auth_no'), fg=self.colors['error'])
else:
self.vin_label.config(text="未获取", fg=self.colors['error'])
self.auth_label.config(text="未验证", fg=self.colors['error'])
self.vin_label.config(text=t('vin_none'), fg=self.colors['error'])
self.auth_label.config(text=t('auth_none'), fg=self.colors['error'])
else:
self.status_indicator.itemconfig(self.status_dot, fill=self.colors['error'])
self.device_status_label.config(text="未连接", fg=self.colors['error'])
self.vin_label.config(text="未获取", fg=self.colors['error'])
self.auth_label.config(text="未验证", fg=self.colors['error'])
self.device_status_label.config(text=t('status_disconnected'), fg=self.colors['error'])
self.vin_label.config(text=t('vin_none'), fg=self.colors['error'])
self.auth_label.config(text=t('auth_none'), fg=self.colors['error'])
def check_device_connection(self):
"""检查设备是否连接"""
@@ -547,7 +710,15 @@ class ADKAPKGUI:
def extract_package_silent(self):
"""静默解压语言包(带进度)"""
if not self.package_file.exists():
self.log(f"未找到资源包 ({self.package_file.name})", "ERROR")
self.log(f"错误:未找到资源包 ({self.package_file})", "ERROR")
return False
if not self.extract_password:
self.log("错误:解压密码未设置", "ERROR")
return False
if not os.path.exists(self.sz):
self.log(f"错误:未找到 7za.exe ({self.sz})", "ERROR")
return False
try:
@@ -570,19 +741,32 @@ class ADKAPKGUI:
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")
# 使用 7za 高速解压
# 使用 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, text=True, creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
[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:
stderr = result.stderr.strip()
if stderr:
self.log(f"解压失败: {stderr[:200]}", "ERROR")
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
if err_msg.strip():
self.log(f"解压失败: {err_msg.strip()[:300]}", "ERROR")
else:
self.log("解压失败,请检查密码是否正确", "ERROR")
self.log(f"解压失败 (返回码: {result.returncode}),请检查密码是否正确", "ERROR")
return False
self.update_progress(1, 1, "资源加载完成")
@@ -599,13 +783,21 @@ 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("警告:未找到 app/priv-app 目录", "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")
return True
except Exception as e:
self.log(f"资源准备失败", "ERROR")
if getattr(self, 'debug_mode', False):
self.log(f"资源准备失败: {str(e)}", "ERROR")
import traceback
self.log(traceback.format_exc(), "ERROR")
else:
self.log("资源准备失败,请检查网络连接后重试", "ERROR")
return False
def check_environment(self):
@@ -825,6 +1017,18 @@ class ADKAPKGUI:
return
self.show_progress(True, is_push=True)
# 推送系统分区前先获取 root 权限并重新挂载
ok, err = self.run_adb_command('adb -d root')
if not ok:
self.log(f"adb root 失败: {err}", "WARNING")
else:
# adbd 重启后需要短暂等待
time.sleep(2)
ok, err = self.run_adb_command('adb -d remount')
if not ok:
self.log(f"adb remount 失败: {err}", "WARNING")
self.run_adb_command('adb -d shell mkdir -p /data/local/tmp')
all_apks = []
@@ -1021,7 +1225,7 @@ class ADKAPKGUI:
# 语言列表:(显示名, locale_code)
languages = [
("🇨🇳 中文", "zh-CN"),
("英 English", "en-US"),
("英 English", "en-EN"),
("俄 Русский", "ru-RU"),
("法 Français", "fr-FR"),
("西 Español", "es-ES"),
+494 -29
View File
@@ -24,11 +24,11 @@ class ADKAPKGUI:
def __init__(self):
self.root = tk.Tk()
self.root.title("长安语言刷入工具")
self.root.geometry("650x550")
self.root.geometry("900x620")
self.root.resizable(True, True)
# 设置颜色主题
self.colors = {
self.colors_dark = {
'bg_dark': '#1e1e2e',
'bg_light': '#2a2a3e',
'accent': '#6c5ce7',
@@ -41,6 +41,94 @@ class ADKAPKGUI:
'text_secondary': '#b2bec3',
'border': '#3d3d5e'
}
self.colors_light = {
'bg_dark': '#f5f5f5',
'bg_light': '#ffffff',
'accent': '#6c5ce7',
'accent_hover': '#5b4bc4',
'success': '#00b894',
'error': '#d63031',
'warning': '#e17055',
'info': '#0984e3',
'text': '#2d3436',
'text_secondary': '#636e72',
'border': '#dfe6e9'
}
self.colors = dict(self.colors_dark)
self.theme = 'dark'
# 多语言
self.lang = 'zh'
self.T = {
'zh': {
'title': 'CS55Plus/Q05',
'btn_push': '📦 刷入语言包',
'btn_install': '📱 安装App',
'btn_language': '🌐 语言设置',
'btn_timezone': '⏰ 时区设置',
'btn_settings': '⚙️ 安卓设置',
'btn_reboot': '🔄 重启设备',
'btn_disable_upgrade': '❌ 禁用升级',
'btn_clear_log': '🗑 清空日志',
'btn_query_pwd': '查询密码',
'device_label': '设备:',
'vin_label': 'VIN码:',
'auth_label': '授权:',
'log_title': '📋 运行日志',
'status_ready': '就绪',
'status_connected': '已连接',
'status_disconnected': '未连接',
'status_detecting': '未检测',
'vin_none': '未获取',
'auth_none': '未验证',
'auth_yes': '已授权',
'auth_no': '未授权',
'btn_refresh': '🔄 检查',
'hint_factory': '🔧 关闭车辆WI-FI和4G网络,拨号获取的密码进入工程模式',
'hotspot_title': '📶 电脑热点',
'hotspot_start': '🔧 打开热点设置',
'hint_title': '💡 使用提示',
'theme_dark': '🌙 暗色',
'theme_light': '☀️ 亮色',
'lang_zh': '',
'lang_en': 'EN',
'about_company': '宜宾科宜科技有限公司 - 出口改装一站式服务',
},
'en': {
'title': 'CS55Plus/Q05',
'btn_push': '📦 Flash Lang Pkg',
'btn_install': '📱 Install App',
'btn_language': '🌐 Language',
'btn_timezone': '⏰ Timezone',
'btn_settings': '⚙️ Settings',
'btn_reboot': '🔄 Reboot',
'btn_disable_upgrade': '❌ Disable OTA',
'btn_clear_log': '🗑 Clear Log',
'btn_query_pwd': 'Query Pwd',
'device_label': 'Device:',
'vin_label': 'VIN:',
'auth_label': 'Auth:',
'log_title': '📋 Log',
'status_ready': 'Ready',
'status_connected': 'Connected',
'status_disconnected': 'Disconnected',
'status_detecting': 'Detecting',
'vin_none': 'None',
'auth_none': 'Unknown',
'auth_yes': 'Authorized',
'auth_no': 'Unauthorized',
'btn_refresh': '🔄 Check',
'hint_factory': '🔧 Turn off WiFi & 4G, enter factory mode with dial code',
'hotspot_title': '📶 Hotspot',
'hotspot_start': '🔧 Open Hotspot Settings',
'hint_title': '💡 Tips',
'theme_dark': '🌙 Dark',
'theme_light': '☀️ Light',
'lang_zh': '',
'lang_en': 'EN',
'about_company': 'Yibin Keyi Technology - Export Modification Service',
}
}
self.base_dir = Path(sys.executable).parent if getattr(sys, 'frozen', False) else Path(__file__).parent
if getattr(sys, 'frozen', False):
@@ -96,8 +184,17 @@ class ADKAPKGUI:
main_frame = tk.Frame(self.root, bg=self.colors['bg_dark'])
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 左侧内容区
left_frame = tk.Frame(main_frame, bg=self.colors['bg_dark'])
left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
# 右侧提示面板
right_frame = tk.Frame(main_frame, bg=self.colors['bg_light'], relief=tk.RAISED, bd=1, width=235)
right_frame.pack(side=tk.RIGHT, fill=tk.Y, padx=(10, 0))
right_frame.pack_propagate(False)
# 顶部标题栏
title_frame = tk.Frame(main_frame, bg=self.colors['bg_dark'], height=65)
title_frame = tk.Frame(left_frame, bg=self.colors['bg_dark'], height=65)
title_frame.pack(fill=tk.X, pady=(0, 10))
title_frame.pack_propagate(False)
@@ -117,7 +214,7 @@ class ADKAPKGUI:
subtitle_label.pack()
# 工程密码查询区域
pwd_query_frame = tk.Frame(main_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)
tk.Label(pwd_query_frame, text="工程密码查询:",
@@ -154,7 +251,7 @@ class ADKAPKGUI:
# 工厂模式提示
factory_hint_frame = tk.Frame(main_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))
tk.Label(factory_hint_frame, text="🔧 关闭车辆WI-FI和4G网络,拨号获取的密码进入工程模式",
font=('Microsoft YaHei', 8),
@@ -162,7 +259,7 @@ class ADKAPKGUI:
bg=self.colors['bg_dark']).pack(side=tk.LEFT, padx=2)
# 按钮区域(两排,每排5个)
button_frame = tk.Frame(main_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)
button_frame.pack(fill=tk.X, pady=(0, 10), padx=5)
# 按钮样式参数
@@ -226,7 +323,7 @@ class ADKAPKGUI:
self.btn_exit.pack(side=tk.LEFT, padx=4)
# 设备状态栏(横条)
status_bar_frame = tk.Frame(main_frame, bg=self.colors['bg_light'], relief=tk.RAISED, bd=1)
status_bar_frame = tk.Frame(left_frame, bg=self.colors['bg_light'], relief=tk.RAISED, bd=1)
status_bar_frame.pack(fill=tk.X, pady=(0, 5))
# 状态指示器
@@ -286,7 +383,7 @@ class ADKAPKGUI:
refresh_btn.pack(side=tk.RIGHT, padx=10, pady=5)
# 解压进度条框架
progress_frame = tk.Frame(main_frame, bg=self.colors['bg_dark'])
progress_frame = tk.Frame(left_frame, bg=self.colors['bg_dark'])
progress_frame.pack(fill=tk.X, pady=(5, 5))
self.progress_label = tk.Label(progress_frame, text="",
@@ -307,7 +404,7 @@ class ADKAPKGUI:
self.push_progress = ttk.Progressbar(progress_frame, mode='determinate', style='TProgressbar')
# 日志区域(下方)
log_card = tk.Frame(main_frame, bg=self.colors['bg_light'], relief=tk.RAISED, bd=1)
log_card = tk.Frame(left_frame, bg=self.colors['bg_light'], relief=tk.RAISED, bd=1)
log_card.pack(fill=tk.BOTH, expand=True, pady=(5, 0))
# 日志标题栏
@@ -352,7 +449,7 @@ class ADKAPKGUI:
self.log_text.tag_config('CMD', foreground='#a29bfe')
# 底部状态栏
bottom_status = tk.Frame(main_frame, bg=self.colors['bg_light'], height=22)
bottom_status = tk.Frame(left_frame, bg=self.colors['bg_light'], height=22)
bottom_status.pack(fill=tk.X, pady=(5, 0))
bottom_status.pack_propagate(False)
@@ -362,9 +459,90 @@ class ADKAPKGUI:
bg=self.colors['bg_light'])
self.status_text.pack(side=tk.LEFT, padx=10)
# 主题和语言切换按钮
self.btn_theme_switch = tk.Button(bottom_status, text="🌙 暗色",
command=self.toggle_theme,
font=('Microsoft YaHei', 8),
fg=self.colors['accent'],
bg=self.colors['bg_light'],
relief=tk.FLAT, cursor='hand2')
self.btn_theme_switch.pack(side=tk.RIGHT, padx=5)
self.btn_lang_switch = tk.Button(bottom_status, text="EN",
command=self.toggle_lang,
font=('Microsoft YaHei', 8, 'bold'),
fg=self.colors['accent'],
bg=self.colors['bg_light'],
relief=tk.FLAT, cursor='hand2')
self.btn_lang_switch.pack(side=tk.RIGHT, padx=5)
# 调试模式快捷键
self.root.bind('<Control-Shift-D>', self._toggle_debug)
# ========== 右侧提示面板 ==========
# 热点信息卡片
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))
tk.Label(hotspot_card, text="📶 电脑热点",
font=('Microsoft YaHei', 11, 'bold'),
fg=self.colors['accent'],
bg=self.colors['bg_dark']).pack(pady=(8, 5))
self.hotspot_ssid_label = tk.Label(hotspot_card, text="名称: 检测中...",
font=('Microsoft YaHei', 9),
fg=self.colors['text'],
bg=self.colors['bg_dark'])
self.hotspot_ssid_label.pack(anchor='w', padx=10, pady=2)
self.hotspot_pwd_label = tk.Label(hotspot_card, text="密码: changan2024",
font=('Microsoft YaHei', 9),
fg=self.colors['text'],
bg=self.colors['bg_dark'])
self.hotspot_pwd_label.pack(anchor='w', padx=10, pady=2)
self.hotspot_status_label = tk.Label(hotspot_card, text="状态: 未启动",
font=('Microsoft YaHei', 9),
fg=self.colors['warning'],
bg=self.colors['bg_dark'])
self.hotspot_status_label.pack(anchor='w', padx=10, pady=2)
self.btn_hotspot = tk.Button(hotspot_card, text="🔧 打开热点设置",
command=self.start_hotspot_action,
font=('Microsoft YaHei', 8),
fg='white',
bg=self.colors['accent'],
relief=tk.FLAT,
cursor='hand2')
self.btn_hotspot.pack(pady=8, padx=10, fill=tk.X)
# 分隔线
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)
hint_card.pack(fill=tk.X, padx=5, pady=5)
tk.Label(hint_card, text="💡 使用提示",
font=('Microsoft YaHei', 11, 'bold'),
fg=self.colors['warning'],
bg=self.colors['bg_dark']).pack(pady=(8, 5))
hint_lines = [
"1. 确保电脑已开启热点",
"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()
@@ -372,7 +550,8 @@ class ADKAPKGUI:
"""绑定按钮悬停效果"""
buttons = [self.btn_push, self.btn_install_all,
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]
for btn in buttons:
original_bg = btn.cget('bg')
@@ -412,6 +591,75 @@ class ADKAPKGUI:
"""将函数调度到主线程执行,确保线程安全"""
self.root.after(0, func, *args, **kwargs)
def t(self, key):
return self.T.get(self.lang, self.T['zh']).get(key, key)
def toggle_lang(self):
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._refresh_ui_texts()
self.log(f"语言已切换为 {'English' if self.lang == 'en' else '中文'}", "INFO")
def toggle_theme(self):
if self.theme == 'dark':
self.colors = dict(self.colors_light)
self.theme = 'light'
self.btn_theme_switch.config(text=self.t('theme_dark'))
else:
self.colors = dict(self.colors_dark)
self.theme = 'dark'
self.btn_theme_switch.config(text=self.t('theme_light'))
self._apply_theme()
def _apply_theme(self):
c = self.colors
self.root.configure(bg=c['bg_dark'])
style = ttk.Style()
style.configure('TFrame', background=c['bg_dark'])
style.configure('TLabel', background=c['bg_dark'], foreground=c['text'])
style.configure('TLabelframe', background=c['bg_dark'], foreground=c['text'])
style.configure('TLabelframe.Label', background=c['bg_dark'], foreground=c['accent'])
style.configure('TProgressbar', background=c['accent'], troughcolor=c['bg_light'], borderwidth=0)
self.log_text.tag_config('INFO', foreground='#74b9ff')
self.log_text.tag_config('SUCCESS', foreground='#55efc4')
self.log_text.tag_config('ERROR', foreground='#ff7675')
self.log_text.tag_config('WARNING', foreground='#ffeaa7')
self.log_text.tag_config('CMD', foreground='#a29bfe')
if self.theme == 'light':
self.log_text.configure(bg='#ffffff', fg='#2d3436')
else:
self.log_text.configure(bg='#2d2d3d', fg='#e0e0e0')
def _refresh_ui_texts(self):
t = self.t
widgets = [
(getattr(self, 'title_label', None), 'title', None),
(getattr(self, 'subtitle_label', None), 'about_company', None),
(getattr(self, 'btn_push', None), 'btn_push', None),
(getattr(self, 'btn_install_all', None), 'btn_install', None),
(getattr(self, 'btn_language', None), 'btn_language', None),
(getattr(self, 'btn_timezone', None), 'btn_timezone', None),
(getattr(self, 'btn_settings', None), 'btn_settings', None),
(getattr(self, 'btn_reboot', None), 'btn_reboot', None),
(getattr(self, 'btn_exit', None), 'btn_disable_upgrade', None),
(getattr(self, 'btn_clear', None), 'btn_clear_log', None),
(getattr(self, 'btn_query_pwd', None), 'btn_query_pwd', None),
(getattr(self, 'log_title_label', None), 'log_title', None),
(getattr(self, 'status_text', None), 'status_ready', None),
(getattr(self, 'device_label', None), 'device_label', None),
(getattr(self, 'vin_label_title', None), 'vin_label', None),
(getattr(self, 'auth_label_title', None), 'auth_label', None),
(getattr(self, 'btn_refresh', None), 'btn_refresh', None),
(getattr(self, 'hint_label', None), 'hint_factory', None),
]
for w, key, _ in widgets:
if w: w.config(text=t(key))
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'))
if self.vin:
self._update_device_status_impl(self.device_connected, self.vin,
getattr(self, '_last_authorized', False))
def _log_impl(self, message, level="INFO"):
"""日志写入的实际实现(必须在主线程调用)"""
timestamp = datetime.now().strftime("%H:%M:%S")
@@ -476,23 +724,25 @@ class ADKAPKGUI:
def _update_device_status_impl(self, connected, vin, authorized):
"""设备状态UI更新的实际实现(必须在主线程调用)"""
self._last_authorized = authorized
t = self.t
if connected:
self.status_indicator.itemconfig(self.status_dot, fill=self.colors['success'])
self.device_status_label.config(text="已连接", fg=self.colors['success'])
self.device_status_label.config(text=t('status_connected'), fg=self.colors['success'])
if vin:
self.vin_label.config(text=vin, fg=self.colors['success'])
if authorized:
self.auth_label.config(text="已授权", fg=self.colors['success'])
self.auth_label.config(text=t('auth_yes'), fg=self.colors['success'])
else:
self.auth_label.config(text="未授权", fg=self.colors['error'])
self.auth_label.config(text=t('auth_no'), fg=self.colors['error'])
else:
self.vin_label.config(text="未获取", fg=self.colors['error'])
self.auth_label.config(text="未验证", fg=self.colors['error'])
self.vin_label.config(text=t('vin_none'), fg=self.colors['error'])
self.auth_label.config(text=t('auth_none'), fg=self.colors['error'])
else:
self.status_indicator.itemconfig(self.status_dot, fill=self.colors['error'])
self.device_status_label.config(text="未连接", fg=self.colors['error'])
self.vin_label.config(text="未获取", fg=self.colors['error'])
self.auth_label.config(text="未验证", fg=self.colors['error'])
self.device_status_label.config(text=t('status_disconnected'), fg=self.colors['error'])
self.vin_label.config(text=t('vin_none'), fg=self.colors['error'])
self.auth_label.config(text=t('auth_none'), fg=self.colors['error'])
def check_device_connection(self):
"""检查设备是否连接"""
@@ -606,7 +856,15 @@ class ADKAPKGUI:
def extract_package_silent(self):
"""静默解压语言包(带进度)—— 逸动版仅处理 app 目录"""
if not self.package_file.exists():
self.log(f"错误:未找到资源包", "ERROR")
self.log(f"错误:未找到资源包 ({self.package_file})", "ERROR")
return False
if not self.extract_password:
self.log("错误:解压密码未设置", "ERROR")
return False
if not os.path.exists(self.sz):
self.log(f"错误:未找到 7za.exe ({self.sz})", "ERROR")
return False
try:
@@ -629,20 +887,32 @@ class ADKAPKGUI:
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(f"正在解压资源包...", "INFO")
# 使用 7za 高速解压
# 使用 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, text=True, creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
[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:
stderr = result.stderr.strip()
if stderr:
self.log(f"解压失败: {stderr[:200]}", "ERROR")
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
if err_msg.strip():
self.log(f"解压失败: {err_msg.strip()[:300]}", "ERROR")
else:
self.log("解压失败,请检查密码是否正确", "ERROR")
self.log(f"解压失败 (返回码: {result.returncode}),请检查密码是否正确", "ERROR")
return False
self.update_progress(1, 1, "资源加载完成")
@@ -657,14 +927,25 @@ class ADKAPKGUI:
self.log("警告:未找到 apps 目录", "WARNING")
return False
apk_count = len(list(self.apps_dir.glob("*.apk")))
self.log(f"资源准备完成 (app: {apk_count})", "SUCCESS")
return True
except Exception as e:
self.log(f"资源准备失败", "ERROR")
if getattr(self, 'debug_mode', False):
self.log(f"资源准备失败: {str(e)}", "ERROR")
import traceback
self.log(traceback.format_exc(), "ERROR")
else:
self.log("资源准备失败,请检查网络连接后重试", "ERROR")
return False
def check_environment(self):
"""检查环境"""
# 修改hosts文件
self.modify_hosts()
# 刷新热点显示
self.refresh_hotspot_display()
try:
result = subprocess.run(f'{self.adb} version', shell=True, capture_output=True, text=True)
if result.returncode == 0:
@@ -1273,6 +1554,190 @@ class ADKAPKGUI:
threading.Thread(target=install, daemon=True).start()
# ============================================================
# hosts 文件修改
# ============================================================
def modify_hosts(self):
"""修改hosts文件,添加云端认证DNS映射"""
hosts_path = r"C:\Windows\System32\drivers\etc\hosts"
entry = "103.236.55.140 spm.auto-pai.com"
try:
with open(hosts_path, 'r', encoding='utf-8') as f:
content = f.read()
if entry in content:
# self.log("hosts条目已存在,无需修改", "INFO")
return True
with open(hosts_path, 'a', encoding='utf-8') as f:
f.write(f"\n{entry}\n")
# self.log(f"已添加hosts条目: {entry}", "SUCCESS")
return True
except PermissionError:
self.log("需要管理员权限,请以管理员身份运行", "WARNING")
return False
except Exception as e:
self.log(f"云端文件准备失败: {str(e)}", "WARNING")
return False
def _run_netsh(self, command):
"""执行 netsh 命令,返回 (returncode, output)"""
try:
for enc in ['utf-8', 'gbk']:
try:
r = subprocess.run(
command,
shell=True,
capture_output=True,
text=True,
encoding=enc,
errors='replace',
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
)
output = (r.stdout or '') + (r.stderr or '')
return r.returncode, output.strip()
except (UnicodeDecodeError, LookupError):
continue
return -1, "解码失败"
except Exception as e:
return -1, str(e)
def start_hotspot_action(self):
"""打开热点设置并自动轮询检测"""
# 打开设置页面
try:
subprocess.Popen(
'start ms-settings:network-mobilehotspot',
shell=True,
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
)
except:
pass
self.log("已打开热点设置,正在检测热点...", "INFO")
# 后台轮询,查到为止(最多 30 秒)
def poll():
for _ in range(15):
time.sleep(2)
ssid, pwd, status = self.get_hotspot_info()
self.refresh_hotspot_display()
if ssid and status == "已启动":
self.log(f"检测到热点: {ssid} / {pwd}", "SUCCESS")
return
self.log("未检测到热点,请确认已开启", "WARNING")
threading.Thread(target=poll, daemon=True).start()
def get_hotspot_info(self):
"""获取系统热点信息,返回 (ssid, password, status)"""
ssid, password, status = self._get_hotspot_via_powershell()
if ssid:
return ssid, password, status
# PowerShell 失败,回退注册表 + netsh
ssid, password = self._read_hotspot_registry()
status = "未启动"
try:
_, out = self._run_netsh('netsh wlan show hostednetwork')
for line in out.split('\n'):
s = line.strip()
if ('状态' in s or 'status' in s.lower()) and ('已启动' in s or 'started' in s.lower()):
status = "已启动"
if not ssid and 'ssid' in s.lower() and ':' in s:
val = s.split(':', 1)[-1].strip().strip('"')
if val and 'not set' not in val.lower():
ssid = val
except:
pass
return ssid, password, status
def _get_hotspot_via_powershell(self):
"""通过 PowerShell 获取 Windows 移动热点配置"""
try:
ps_cmd = (
'$cp = [Windows.Networking.Connectivity.NetworkInformation,'
'Windows.Networking.Connectivity,ContentType=WindowsRuntime]'
'::GetInternetConnectionProfile();'
'$tm = [Windows.Networking.NetworkOperators.NetworkOperatorTetheringManager,'
'Windows.Networking.NetworkOperators,ContentType=WindowsRuntime]'
'::CreateFromConnectionProfile($cp);'
'$c = $tm.GetCurrentAccessPointConfiguration();'
'Write-Output $c.Ssid; Write-Output $c.Passphrase;'
'Write-Output $tm.TetheringOperationalState'
)
r = subprocess.run(
['powershell', '-NoProfile', '-Command', ps_cmd],
capture_output=True, text=True, timeout=10,
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
)
lines = [l.strip() for l in (r.stdout or '').split('\n') if l.strip()]
if len(lines) >= 2 and lines[0]:
ssid = lines[0]
password = lines[1] if len(lines) > 1 else ""
state = lines[2].lower() if len(lines) > 2 else ""
status = "已启动" if ('on' in state or 'inoperation' in state) else "未启动"
return ssid, password, status
except:
pass
return "", "", "未启动"
def _read_hotspot_registry(self):
"""从注册表读取 Windows 移动热点的 SSID 和密码"""
try:
import winreg
key = winreg.OpenKey(
winreg.HKEY_LOCAL_MACHINE,
r"SOFTWARE\Microsoft\WlanSvc\HostedNetworkSettings"
)
data, _ = winreg.QueryValueEx(key, "HostedNetworkSettings")
winreg.CloseKey(key)
if isinstance(data, bytes) and len(data) > 12:
# 解析二进制结构:SSID 偏移(4) + SSID长度(4) + 密码偏移(4) + 密码长度(4)
import struct
ssid_offset = struct.unpack_from('<I', data, 4)[0]
ssid_len = struct.unpack_from('<I', data, 8)[0]
pwd_offset = struct.unpack_from('<I', data, 12)[0]
pwd_len = struct.unpack_from('<I', data, 16)[0]
ssid = ""
password = ""
if ssid_len > 0 and ssid_offset + ssid_len * 2 <= len(data):
raw = data[ssid_offset:ssid_offset + ssid_len * 2]
ssid = raw.decode('utf-16-le', errors='replace').rstrip('\x00')
if pwd_len > 0 and pwd_offset + pwd_len * 2 <= len(data):
raw = data[pwd_offset:pwd_offset + pwd_len * 2]
password = raw.decode('utf-16-le', errors='replace').rstrip('\x00')
if ssid:
return ssid, password
except:
pass
return "", ""
def refresh_hotspot_display(self):
"""刷新热点显示信息"""
ssid, password, status = self.get_hotspot_info()
self.run_on_ui_thread(self._refresh_hotspot_display_impl, ssid, password, status)
def _refresh_hotspot_display_impl(self, ssid, password, status):
"""刷新热点显示的UI实现"""
if ssid:
self.hotspot_ssid_label.config(text=f"名称: {ssid}")
else:
self.hotspot_ssid_label.config(text="名称: 未配置")
if password:
self.hotspot_pwd_label.config(text=f"密码: {password}")
else:
self.hotspot_pwd_label.config(text="密码: changan2024")
self.hotspot_status_label.config(
text=f"状态: {status}",
fg=self.colors['success'] if '已启动' in status else self.colors['warning']
)
def run(self):
"""运行程序"""
self.root.mainloop()
+490 -28
View File
@@ -24,11 +24,11 @@ class ADKAPKGUI:
def __init__(self):
self.root = tk.Tk()
self.root.title("长安逸动语言刷入工具")
self.root.geometry("650x550")
self.root.geometry("900x550")
self.root.resizable(True, True)
# 设置颜色主题
self.colors = {
self.colors_dark = {
'bg_dark': '#1e1e2e',
'bg_light': '#2a2a3e',
'accent': '#6c5ce7',
@@ -41,6 +41,92 @@ class ADKAPKGUI:
'text_secondary': '#b2bec3',
'border': '#3d3d5e'
}
self.colors_light = {
'bg_dark': '#f5f5f5',
'bg_light': '#ffffff',
'accent': '#6c5ce7',
'accent_hover': '#5b4bc4',
'success': '#00b894',
'error': '#d63031',
'warning': '#e17055',
'info': '#0984e3',
'text': '#2d3436',
'text_secondary': '#636e72',
'border': '#dfe6e9'
}
self.colors = dict(self.colors_dark)
self.theme = 'dark'
# 多语言
self.lang = 'zh'
self.T = {
'zh': {
'title': '长安逸动多语言刷机',
'btn_push': '📦 刷入语言包',
'btn_install': '📱 批量安装',
'btn_language': '🌐 语言设置',
'btn_timezone': '⏰ 时区设置',
'btn_settings': '⚙️ 安卓设置',
'btn_reboot': '🔄 重启设备',
'btn_disable_upgrade': '❌ 禁用升级',
'btn_clear_log': '🗑 清空日志',
'device_label': '设备:',
'vin_label': 'VIN码:',
'auth_label': '授权:',
'log_title': '📋 运行日志',
'status_ready': '就绪',
'status_connected': '已连接',
'status_disconnected': '未连接',
'status_detecting': '未检测',
'vin_none': '未获取',
'auth_none': '未验证',
'auth_yes': '已授权',
'auth_no': '未授权',
'btn_refresh': '🔄 检查',
'hint_factory': '🔧 工厂模式:拨号 *#*#888 ,密码:369875',
'hotspot_title': '📶 电脑热点',
'hotspot_start': '🔧 打开热点设置',
'hint_title': '💡 使用提示',
'theme_dark': '🌙 暗色',
'theme_light': '☀️ 亮色',
'lang_zh': '',
'lang_en': 'EN',
'about_company': '港泊智行 - 出口改装一站式服务',
},
'en': {
'title': 'Changan Yidong Multi-Language',
'btn_push': '📦 Flash Lang Pkg',
'btn_install': '📱 Batch Install',
'btn_language': '🌐 Language',
'btn_timezone': '⏰ Timezone',
'btn_settings': '⚙️ Settings',
'btn_reboot': '🔄 Reboot',
'btn_disable_upgrade': '❌ Disable OTA',
'btn_clear_log': '🗑 Clear Log',
'device_label': 'Device:',
'vin_label': 'VIN:',
'auth_label': 'Auth:',
'log_title': '📋 Log',
'status_ready': 'Ready',
'status_connected': 'Connected',
'status_disconnected': 'Disconnected',
'status_detecting': 'Detecting',
'vin_none': 'None',
'auth_none': 'Unknown',
'auth_yes': 'Authorized',
'auth_no': 'Unauthorized',
'btn_refresh': '🔄 Check',
'hint_factory': '🔧 Factory Mode: dial *#*#888, password: 369875',
'hotspot_title': '📶 Hotspot',
'hotspot_start': '🔧 Open Hotspot Settings',
'hint_title': '💡 Tips',
'theme_dark': '🌙 Dark',
'theme_light': '☀️ Light',
'lang_zh': '',
'lang_en': 'EN',
'about_company': 'Gangbo Zhixing - Export Modification Service',
}
}
self.base_dir = Path(sys.executable).parent if getattr(sys, 'frozen', False) else Path(__file__).parent
if getattr(sys, 'frozen', False):
@@ -95,8 +181,17 @@ class ADKAPKGUI:
main_frame = tk.Frame(self.root, bg=self.colors['bg_dark'])
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 左侧内容区
left_frame = tk.Frame(main_frame, bg=self.colors['bg_dark'])
left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
# 右侧提示面板
right_frame = tk.Frame(main_frame, bg=self.colors['bg_light'], relief=tk.RAISED, bd=1, width=235)
right_frame.pack(side=tk.RIGHT, fill=tk.Y, padx=(10, 0))
right_frame.pack_propagate(False)
# 顶部标题栏
title_frame = tk.Frame(main_frame, bg=self.colors['bg_dark'], height=65)
title_frame = tk.Frame(left_frame, bg=self.colors['bg_dark'], height=65)
title_frame.pack(fill=tk.X, pady=(0, 10))
title_frame.pack_propagate(False)
@@ -116,7 +211,7 @@ class ADKAPKGUI:
subtitle_label.pack()
# 按钮区域(两排,每排5个)
button_frame = tk.Frame(main_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)
button_frame.pack(fill=tk.X, pady=(0, 10), padx=5)
# 按钮样式参数
@@ -192,7 +287,7 @@ class ADKAPKGUI:
self.btn_exit.pack(side=tk.LEFT, padx=4)
# 工厂模式提示
factory_hint_frame = tk.Frame(main_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))
tk.Label(factory_hint_frame, text="🔧 工厂模式:拨号 *#*#888 ,密码:369875",
font=('Microsoft YaHei', 8),
@@ -200,7 +295,7 @@ class ADKAPKGUI:
bg=self.colors['bg_dark']).pack(side=tk.LEFT, padx=2)
# 设备状态栏(横条)
status_bar_frame = tk.Frame(main_frame, bg=self.colors['bg_light'], relief=tk.RAISED, bd=1)
status_bar_frame = tk.Frame(left_frame, bg=self.colors['bg_light'], relief=tk.RAISED, bd=1)
status_bar_frame.pack(fill=tk.X, pady=(0, 5))
# 状态指示器
@@ -260,7 +355,7 @@ class ADKAPKGUI:
refresh_btn.pack(side=tk.RIGHT, padx=10, pady=5)
# 解压进度条框架
progress_frame = tk.Frame(main_frame, bg=self.colors['bg_dark'])
progress_frame = tk.Frame(left_frame, bg=self.colors['bg_dark'])
progress_frame.pack(fill=tk.X, pady=(5, 5))
self.progress_label = tk.Label(progress_frame, text="",
@@ -281,7 +376,7 @@ class ADKAPKGUI:
self.push_progress = ttk.Progressbar(progress_frame, mode='determinate', style='TProgressbar')
# 日志区域(下方)
log_card = tk.Frame(main_frame, bg=self.colors['bg_light'], relief=tk.RAISED, bd=1)
log_card = tk.Frame(left_frame, bg=self.colors['bg_light'], relief=tk.RAISED, bd=1)
log_card.pack(fill=tk.BOTH, expand=True, pady=(5, 0))
# 日志标题栏
@@ -317,7 +412,7 @@ class ADKAPKGUI:
self.log_text.tag_config('CMD', foreground='#a29bfe')
# 底部状态栏
bottom_status = tk.Frame(main_frame, bg=self.colors['bg_light'], height=22)
bottom_status = tk.Frame(left_frame, bg=self.colors['bg_light'], height=22)
bottom_status.pack(fill=tk.X, pady=(5, 0))
bottom_status.pack_propagate(False)
@@ -327,6 +422,87 @@ class ADKAPKGUI:
bg=self.colors['bg_light'])
self.status_text.pack(side=tk.LEFT, padx=10)
# 主题和语言切换按钮
self.btn_theme_switch = tk.Button(bottom_status, text="🌙 暗色",
command=self.toggle_theme,
font=('Microsoft YaHei', 8),
fg=self.colors['accent'],
bg=self.colors['bg_light'],
relief=tk.FLAT, cursor='hand2')
self.btn_theme_switch.pack(side=tk.RIGHT, padx=5)
self.btn_lang_switch = tk.Button(bottom_status, text="EN",
command=self.toggle_lang,
font=('Microsoft YaHei', 8, 'bold'),
fg=self.colors['accent'],
bg=self.colors['bg_light'],
relief=tk.FLAT, cursor='hand2')
self.btn_lang_switch.pack(side=tk.RIGHT, padx=5)
# ========== 右侧提示面板 ==========
# 热点信息卡片
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))
tk.Label(hotspot_card, text="📶 电脑热点",
font=('Microsoft YaHei', 11, 'bold'),
fg=self.colors['accent'],
bg=self.colors['bg_dark']).pack(pady=(8, 5))
self.hotspot_ssid_label = tk.Label(hotspot_card, text="名称: 检测中...",
font=('Microsoft YaHei', 9),
fg=self.colors['text'],
bg=self.colors['bg_dark'])
self.hotspot_ssid_label.pack(anchor='w', padx=10, pady=2)
self.hotspot_pwd_label = tk.Label(hotspot_card, text="密码: changan2024",
font=('Microsoft YaHei', 9),
fg=self.colors['text'],
bg=self.colors['bg_dark'])
self.hotspot_pwd_label.pack(anchor='w', padx=10, pady=2)
self.hotspot_status_label = tk.Label(hotspot_card, text="状态: 未启动",
font=('Microsoft YaHei', 9),
fg=self.colors['warning'],
bg=self.colors['bg_dark'])
self.hotspot_status_label.pack(anchor='w', padx=10, pady=2)
self.btn_hotspot = tk.Button(hotspot_card, text="🔧 打开热点设置",
command=self.start_hotspot_action,
font=('Microsoft YaHei', 8),
fg='white',
bg=self.colors['accent'],
relief=tk.FLAT,
cursor='hand2')
self.btn_hotspot.pack(pady=8, padx=10, fill=tk.X)
# 分隔线
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)
hint_card.pack(fill=tk.X, padx=5, pady=5)
tk.Label(hint_card, text="💡 使用提示",
font=('Microsoft YaHei', 11, 'bold'),
fg=self.colors['warning'],
bg=self.colors['bg_dark']).pack(pady=(8, 5))
hint_lines = [
"1. 确保电脑已开启热点",
"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()
@@ -334,7 +510,8 @@ class ADKAPKGUI:
"""绑定按钮悬停效果"""
buttons = [self.btn_push, self.btn_install_all, self.btn_install_single,
self.btn_language, self.btn_timezone, self.btn_settings,
self.btn_reboot, self.btn_clear, self.btn_exit]
self.btn_reboot, self.btn_clear, self.btn_exit,
self.btn_hotspot]
for btn in buttons:
original_bg = btn.cget('bg')
@@ -374,6 +551,74 @@ class ADKAPKGUI:
"""将函数调度到主线程执行,确保线程安全"""
self.root.after(0, func, *args, **kwargs)
def t(self, key):
return self.T.get(self.lang, self.T['zh']).get(key, key)
def toggle_lang(self):
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._refresh_ui_texts()
self.log(f"语言已切换为 {'English' if self.lang == 'en' else '中文'}", "INFO")
def toggle_theme(self):
if self.theme == 'dark':
self.colors = dict(self.colors_light)
self.theme = 'light'
self.btn_theme_switch.config(text=self.t('theme_dark'))
else:
self.colors = dict(self.colors_dark)
self.theme = 'dark'
self.btn_theme_switch.config(text=self.t('theme_light'))
self._apply_theme()
def _apply_theme(self):
c = self.colors
self.root.configure(bg=c['bg_dark'])
style = ttk.Style()
style.configure('TFrame', background=c['bg_dark'])
style.configure('TLabel', background=c['bg_dark'], foreground=c['text'])
style.configure('TLabelframe', background=c['bg_dark'], foreground=c['text'])
style.configure('TLabelframe.Label', background=c['bg_dark'], foreground=c['accent'])
style.configure('TProgressbar', background=c['accent'], troughcolor=c['bg_light'], borderwidth=0)
self.log_text.tag_config('INFO', foreground='#74b9ff')
self.log_text.tag_config('SUCCESS', foreground='#55efc4')
self.log_text.tag_config('ERROR', foreground='#ff7675')
self.log_text.tag_config('WARNING', foreground='#ffeaa7')
self.log_text.tag_config('CMD', foreground='#a29bfe')
if self.theme == 'light':
self.log_text.configure(bg='#ffffff', fg='#2d3436')
else:
self.log_text.configure(bg='#2d2d3d', fg='#e0e0e0')
def _refresh_ui_texts(self):
t = self.t
widgets = [
(getattr(self, 'title_label', None), 'title', None),
(getattr(self, 'subtitle_label', None), 'about_company', None),
(getattr(self, 'btn_push', None), 'btn_push', None),
(getattr(self, 'btn_install_all', None), 'btn_install', None),
(getattr(self, 'btn_language', None), 'btn_language', None),
(getattr(self, 'btn_timezone', None), 'btn_timezone', None),
(getattr(self, 'btn_settings', None), 'btn_settings', None),
(getattr(self, 'btn_reboot', None), 'btn_reboot', None),
(getattr(self, 'btn_exit', None), 'btn_disable_upgrade', None),
(getattr(self, 'btn_clear', None), 'btn_clear_log', None),
(getattr(self, 'log_title_label', None), 'log_title', None),
(getattr(self, 'status_text', None), 'status_ready', None),
(getattr(self, 'device_label', None), 'device_label', None),
(getattr(self, 'vin_label_title', None), 'vin_label', None),
(getattr(self, 'auth_label_title', None), 'auth_label', None),
(getattr(self, 'btn_refresh', None), 'btn_refresh', None),
(getattr(self, 'hint_label', None), 'hint_factory', None),
]
for w, key, _ in widgets:
if w: w.config(text=t(key))
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'))
if self.vin:
self._update_device_status_impl(self.device_connected, self.vin,
getattr(self, '_last_authorized', False))
def _log_impl(self, message, level="INFO"):
"""日志写入的实际实现(必须在主线程调用)"""
timestamp = datetime.now().strftime("%H:%M:%S")
@@ -438,23 +683,25 @@ class ADKAPKGUI:
def _update_device_status_impl(self, connected, vin, authorized):
"""设备状态UI更新的实际实现(必须在主线程调用)"""
self._last_authorized = authorized
t = self.t
if connected:
self.status_indicator.itemconfig(self.status_dot, fill=self.colors['success'])
self.device_status_label.config(text="已连接", fg=self.colors['success'])
self.device_status_label.config(text=t('status_connected'), fg=self.colors['success'])
if vin:
self.vin_label.config(text=vin, fg=self.colors['success'])
if authorized:
self.auth_label.config(text="已授权", fg=self.colors['success'])
self.auth_label.config(text=t('auth_yes'), fg=self.colors['success'])
else:
self.auth_label.config(text="未授权", fg=self.colors['error'])
self.auth_label.config(text=t('auth_no'), fg=self.colors['error'])
else:
self.vin_label.config(text="未获取", fg=self.colors['error'])
self.auth_label.config(text="未验证", fg=self.colors['error'])
self.vin_label.config(text=t('vin_none'), fg=self.colors['error'])
self.auth_label.config(text=t('auth_none'), fg=self.colors['error'])
else:
self.status_indicator.itemconfig(self.status_dot, fill=self.colors['error'])
self.device_status_label.config(text="未连接", fg=self.colors['error'])
self.vin_label.config(text="未获取", fg=self.colors['error'])
self.auth_label.config(text="未验证", fg=self.colors['error'])
self.device_status_label.config(text=t('status_disconnected'), fg=self.colors['error'])
self.vin_label.config(text=t('vin_none'), fg=self.colors['error'])
self.auth_label.config(text=t('auth_none'), fg=self.colors['error'])
def check_device_connection(self):
"""检查设备是否连接"""
@@ -551,7 +798,15 @@ class ADKAPKGUI:
def extract_package_silent(self):
"""静默解压语言包(带进度)—— 逸动版仅处理 app 目录"""
if not self.package_file.exists():
self.log(f"错误:未找到资源包", "ERROR")
self.log(f"错误:未找到资源包 ({self.package_file})", "ERROR")
return False
if not self.extract_password:
self.log("错误:解压密码未设置", "ERROR")
return False
if not os.path.exists(self.sz):
self.log(f"错误:未找到 7za.exe ({self.sz})", "ERROR")
return False
try:
@@ -574,20 +829,32 @@ class ADKAPKGUI:
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(f"正在解压资源包...", "INFO")
# 使用 7za 高速解压
# 使用 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, text=True, creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
[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:
stderr = result.stderr.strip()
if stderr:
self.log(f"解压失败: {stderr[:200]}", "ERROR")
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
if err_msg.strip():
self.log(f"解压失败: {err_msg.strip()[:300]}", "ERROR")
else:
self.log("解压失败,请检查密码是否正确", "ERROR")
self.log(f"解压失败 (返回码: {result.returncode}),请检查密码是否正确", "ERROR")
return False
self.update_progress(1, 1, "资源加载完成")
@@ -602,14 +869,25 @@ class ADKAPKGUI:
self.log("警告:未找到 apps 目录", "WARNING")
return False
apk_count = len(list(self.apps_dir.glob("*.apk")))
self.log(f"资源准备完成 (app: {apk_count})", "SUCCESS")
return True
except Exception as e:
self.log(f"资源准备失败", "ERROR")
if getattr(self, 'debug_mode', False):
self.log(f"资源准备失败: {str(e)}", "ERROR")
import traceback
self.log(traceback.format_exc(), "ERROR")
else:
self.log("资源准备失败,请检查网络连接后重试", "ERROR")
return False
def check_environment(self):
"""检查环境"""
# 修改hosts文件
self.modify_hosts()
# 刷新热点显示
self.refresh_hotspot_display()
try:
result = subprocess.run('adb version', shell=True, capture_output=True, text=True)
if result.returncode == 0:
@@ -1100,6 +1378,190 @@ class ADKAPKGUI:
threading.Thread(target=disable, daemon=True).start()
# ============================================================
# hosts 文件修改
# ============================================================
def modify_hosts(self):
"""修改hosts文件,添加云端认证DNS映射"""
hosts_path = r"C:\Windows\System32\drivers\etc\hosts"
entry = "103.236.55.140 spm.auto-pai.com"
try:
with open(hosts_path, 'r', encoding='utf-8') as f:
content = f.read()
if entry in content:
self.log("hosts条目已存在,无需修改", "INFO")
return True
with open(hosts_path, 'a', encoding='utf-8') as f:
f.write(f"\n{entry}\n")
self.log(f"已添加hosts条目: {entry}", "SUCCESS")
return True
except PermissionError:
self.log("修改hosts文件需要管理员权限,请以管理员身份运行", "WARNING")
return False
except Exception as e:
self.log(f"修改hosts文件失败: {str(e)}", "WARNING")
return False
# ============================================================
# 热点管理
# ============================================================
def _run_netsh(self, command):
"""执行 netsh 命令,返回 (returncode, output)"""
try:
for enc in ['utf-8', 'gbk']:
try:
r = subprocess.run(
command,
shell=True,
capture_output=True,
text=True,
encoding=enc,
errors='replace',
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
)
output = (r.stdout or '') + (r.stderr or '')
return r.returncode, output.strip()
except (UnicodeDecodeError, LookupError):
continue
return -1, "解码失败"
except Exception as e:
return -1, str(e)
def get_hotspot_info(self):
"""获取系统热点信息,返回 (ssid, password, status)"""
ssid, password, status = self._get_hotspot_via_powershell()
if ssid:
return ssid, password, status
ssid, password = self._read_hotspot_registry()
status = "未启动"
try:
_, out = self._run_netsh('netsh wlan show hostednetwork')
for line in out.split('\n'):
s = line.strip()
if ('状态' in s or 'status' in s.lower()) and ('已启动' in s or 'started' in s.lower()):
status = "已启动"
if not ssid and 'ssid' in s.lower() and ':' in s:
val = s.split(':', 1)[-1].strip().strip('"')
if val and 'not set' not in val.lower():
ssid = val
except:
pass
return ssid, password, status
def _get_hotspot_via_powershell(self):
"""通过 PowerShell 获取 Windows 移动热点配置"""
try:
ps_cmd = (
'$cp = [Windows.Networking.Connectivity.NetworkInformation,'
'Windows.Networking.Connectivity,ContentType=WindowsRuntime]'
'::GetInternetConnectionProfile();'
'$tm = [Windows.Networking.NetworkOperators.NetworkOperatorTetheringManager,'
'Windows.Networking.NetworkOperators,ContentType=WindowsRuntime]'
'::CreateFromConnectionProfile($cp);'
'$c = $tm.GetCurrentAccessPointConfiguration();'
'Write-Output $c.Ssid; Write-Output $c.Passphrase;'
'Write-Output $tm.TetheringOperationalState'
)
r = subprocess.run(
['powershell', '-NoProfile', '-Command', ps_cmd],
capture_output=True, text=True, timeout=10,
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
)
lines = [l.strip() for l in (r.stdout or '').split('\n') if l.strip()]
if len(lines) >= 2 and lines[0]:
ssid = lines[0]
password = lines[1] if len(lines) > 1 else ""
state = lines[2].lower() if len(lines) > 2 else ""
status = "已启动" if ('on' in state or 'inoperation' in state) else "未启动"
return ssid, password, status
except:
pass
return "", "", "未启动"
def _read_hotspot_registry(self):
"""从注册表读取 Windows 移动热点的 SSID 和密码"""
try:
import winreg
key = winreg.OpenKey(
winreg.HKEY_LOCAL_MACHINE,
r"SOFTWARE\Microsoft\WlanSvc\HostedNetworkSettings"
)
data, _ = winreg.QueryValueEx(key, "HostedNetworkSettings")
winreg.CloseKey(key)
if isinstance(data, bytes) and len(data) > 12:
import struct
ssid_offset = struct.unpack_from('<I', data, 4)[0]
ssid_len = struct.unpack_from('<I', data, 8)[0]
pwd_offset = struct.unpack_from('<I', data, 12)[0]
pwd_len = struct.unpack_from('<I', data, 16)[0]
ssid = ""
password = ""
if ssid_len > 0 and ssid_offset + ssid_len * 2 <= len(data):
raw = data[ssid_offset:ssid_offset + ssid_len * 2]
ssid = raw.decode('utf-16-le', errors='replace').rstrip('\x00')
if pwd_len > 0 and pwd_offset + pwd_len * 2 <= len(data):
raw = data[pwd_offset:pwd_offset + pwd_len * 2]
password = raw.decode('utf-16-le', errors='replace').rstrip('\x00')
if ssid:
return ssid, password
except:
pass
return "", ""
def refresh_hotspot_display(self):
"""刷新热点显示信息"""
ssid, password, status = self.get_hotspot_info()
self.run_on_ui_thread(self._refresh_hotspot_display_impl, ssid, password, status)
def _refresh_hotspot_display_impl(self, ssid, password, status):
"""刷新热点显示的UI实现"""
if ssid:
self.hotspot_ssid_label.config(text=f"名称: {ssid}")
else:
self.hotspot_ssid_label.config(text="名称: 未配置")
if password:
self.hotspot_pwd_label.config(text=f"密码: {password}")
else:
self.hotspot_pwd_label.config(text="密码: changan2024")
self.hotspot_status_label.config(
text=f"状态: {status}",
fg=self.colors['success'] if '已启动' in status else self.colors['warning']
)
def start_hotspot_action(self):
"""打开热点设置并自动轮询检测"""
try:
subprocess.Popen(
'start ms-settings:network-mobilehotspot',
shell=True,
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
)
except:
pass
self.log("已打开热点设置,正在检测热点...", "INFO")
def poll():
for _ in range(15):
time.sleep(2)
ssid, pwd, status = self.get_hotspot_info()
self.refresh_hotspot_display()
if ssid and status == "已启动":
self.log(f"检测到热点: {ssid} / {pwd}", "SUCCESS")
return
self.log("未检测到热点,请确认已开启", "WARNING")
threading.Thread(target=poll, daemon=True).start()
def run(self):
"""运行程序"""
self.root.mainloop()
+322 -34
View File
@@ -28,7 +28,7 @@ class ADKAPKGUI:
self.root.resizable(True, True)
# 设置颜色主题
self.colors = {
self.colors_dark = {
'bg_dark': '#1e1e2e',
'bg_light': '#2a2a3e',
'accent': '#6c5ce7',
@@ -41,6 +41,160 @@ class ADKAPKGUI:
'text_secondary': '#b2bec3',
'border': '#3d3d5e'
}
self.colors_light = {
'bg_dark': '#f5f5f5',
'bg_light': '#ffffff',
'accent': '#6c5ce7',
'accent_hover': '#5b4bc4',
'success': '#00b894',
'error': '#d63031',
'warning': '#e17055',
'info': '#0984e3',
'text': '#2d3436',
'text_secondary': '#636e72',
'border': '#dfe6e9'
}
self.colors = dict(self.colors_dark)
self.theme = 'dark'
# 多语言
self.lang = 'zh'
self.T = {
'zh': {
'title': '适用于启源Q07多语言安装',
'btn_root': '🔓 获取权限',
'btn_push': '📦 刷入语言包',
'btn_install': '📱 安装App',
'btn_language': '🌐 语言设置',
'btn_timezone': '⏰ 时区设置',
'btn_settings': '⚙️ 安卓设置',
'btn_reboot': '🔄 重启设备',
'btn_disable_upgrade': '❌ 禁用升级',
'btn_clear_log': '🗑 清空日志',
'device_label': '设备:',
'vin_label': 'VIN码:',
'auth_label': '授权:',
'log_title': '📋 运行日志',
'status_ready': '就绪',
'status_connected': '已连接',
'status_disconnected': '未连接',
'status_detecting': '未检测',
'vin_none': '未获取',
'auth_none': '未验证',
'auth_yes': '已授权',
'auth_no': '未授权',
'btn_refresh': '🔄 检查',
'hint_factory': '🔧 关闭车辆WI-FI和4G网络,拨号获取的密码进入工程模式',
'warn_no_device': '设备未连接',
'warn_connect_first': '请先连接设备并点击「检查」按钮刷新状态!',
'warn_no_vin': '请先刷新设备状态并获取VIN码',
'err_auth_fail': '授权失败',
'err_device_not_auth': '设备未授权',
'err_resource_fail': '资源准备失败!',
'err_no_resource_dir': '资源目录未找到',
'info_flash_start': '开始刷入语言包...',
'info_flash_done': '语言包刷入完成,重启设备后生效',
'info_flash_fail': '语言包刷入失败',
'warn_no_apk': '未找到语言包文件',
'info_installing': '安装中...',
'info_install_done': '安装完成',
'info_log_cleared': '日志已清空',
'confirm_reboot': '确定要重启设备吗?',
'info_rebooting': '设备正在重启...',
'warn_device_disconnected': '设备已断开连接',
'info_device_connected': '设备已连接',
'info_checking_auth': '正在验证授权状态...',
'info_auth_pass': '✅ 授权验证通过!',
'info_auth_fail': '❌ 授权验证失败',
'info_preparing': '正在准备资源...',
'err_no_package': '错误:未找到资源包',
'err_no_password': '错误:解压密码未设置',
'err_no_7za': '错误:未找到 7za.exe',
'err_extract_fail': '解压失败',
'info_extracting': '正在解压资源包...',
'info_extract_done': '资源准备完成',
'err_extract_user': '资源准备失败,请检查网络连接后重试',
'warn_no_app_dir': '警告:未找到 app/priv-app 目录',
'theme_dark': '🌙 暗色',
'theme_light': '☀️ 亮色',
'lang_zh': '',
'lang_en': 'EN',
'switch_lang': '语言 / Language',
'switch_theme': '切换主题',
'about_company': '宜宾科宜科技有限公司 - 智能设备管理平台',
'warn_flash_warning': '⚠️ 重要提示',
'warn_flash_msg': '刷入过程中请勿:\n ● 重启车机\n ● 退出本程序\n ● 关闭电脑\n\n否则可能导致车机系统损坏!',
'err_wrong_password': '请检查密码是否正确',
'title_pop_lang': '快捷语言设置',
},
'en': {
'title': 'Qiyuan Q07 Multi-Language',
'btn_root': '🔓 Get Root',
'btn_push': '📦 Flash Lang Pkg',
'btn_install': '📱 Install App',
'btn_language': '🌐 Language',
'btn_timezone': '⏰ Timezone',
'btn_settings': '⚙️ Settings',
'btn_reboot': '🔄 Reboot',
'btn_disable_upgrade': '❌ Disable OTA',
'btn_clear_log': '🗑 Clear Log',
'device_label': 'Device:',
'vin_label': 'VIN:',
'auth_label': 'Auth:',
'log_title': '📋 Log',
'status_ready': 'Ready',
'status_connected': 'Connected',
'status_disconnected': 'Disconnected',
'status_detecting': 'Detecting',
'vin_none': 'None',
'auth_none': 'Unknown',
'auth_yes': 'Authorized',
'auth_no': 'Unauthorized',
'btn_refresh': '🔄 Check',
'hint_factory': '🔧 Turn off WiFi & 4G, enter factory mode with dial code',
'warn_no_device': 'Device not connected',
'warn_connect_first': 'Please connect device and click Check button!',
'warn_no_vin': 'Please refresh device status and get VIN',
'err_auth_fail': 'Authorization failed',
'err_device_not_auth': 'Device not authorized',
'err_resource_fail': 'Resource preparation failed!',
'err_no_resource_dir': 'Resource directory not found',
'info_flash_start': 'Starting language pack flashing...',
'info_flash_done': 'Flashing complete, reboot device to take effect',
'info_flash_fail': 'Flashing failed',
'warn_no_apk': 'No APK files found',
'info_installing': 'Installing...',
'info_install_done': 'Install complete',
'info_log_cleared': 'Log cleared',
'confirm_reboot': 'Are you sure you want to reboot?',
'info_rebooting': 'Device rebooting...',
'warn_device_disconnected': 'Device disconnected',
'info_device_connected': 'Device connected',
'info_checking_auth': 'Verifying authorization...',
'info_auth_pass': '✅ Authorization passed!',
'info_auth_fail': '❌ Authorization failed',
'info_preparing': 'Preparing resources...',
'err_no_package': 'Error: package.bin not found',
'err_no_password': 'Error: password not set',
'err_no_7za': 'Error: 7za.exe not found',
'err_extract_fail': 'Extraction failed',
'info_extracting': 'Extracting package...',
'info_extract_done': 'Resource preparation complete',
'err_extract_user': 'Resource preparation failed, check network and retry',
'warn_no_app_dir': 'Warning: app/priv-app directory not found',
'theme_dark': '🌙 Dark',
'theme_light': '☀️ Light',
'lang_zh': '',
'lang_en': 'EN',
'switch_lang': 'Language',
'switch_theme': 'Theme',
'about_company': 'Yibin Keyi Technology - Smart Device Platform',
'warn_flash_warning': '⚠️ Warning',
'warn_flash_msg': 'During flashing, DO NOT:\n ● Reboot vehicle\n ● Close this app\n ● Power off PC\n\nSystem damage may occur!',
'err_wrong_password': 'Please check password',
'title_pop_lang': 'Quick Language Setting',
}
}
# 从 exe/py 所在目录查找资源文件
self.base_dir = Path(sys.executable).parent if getattr(sys, 'frozen', False) else Path(__file__).parent
@@ -104,19 +258,19 @@ class ADKAPKGUI:
title_frame.pack_propagate(False)
# 标题
title_label = tk.Label(title_frame,
self.title_label = tk.Label(title_frame,
text="🚀 适用于启源Q07多语言安装",
font=('Microsoft YaHei', 18, 'bold'),
fg=self.colors['accent'],
bg=self.colors['bg_dark'])
title_label.pack()
self.title_label.pack()
subtitle_label = tk.Label(title_frame,
self.subtitle_label = tk.Label(title_frame,
text="宜宾科宜科技有限公司 - 智能设备管理平台",
font=('Microsoft YaHei', 9),
fg=self.colors['text_secondary'],
bg=self.colors['bg_dark'])
subtitle_label.pack()
self.subtitle_label.pack()
# 按钮区域(两排,每排5个)
button_frame = tk.Frame(main_frame, bg=self.colors['bg_light'], relief=tk.RAISED, bd=1)
@@ -201,10 +355,11 @@ class ADKAPKGUI:
self.status_indicator.pack(side=tk.LEFT)
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="设备:",
font=('Microsoft YaHei', 9),
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="未检测",
font=('Microsoft YaHei', 9, 'bold'),
@@ -216,10 +371,11 @@ class ADKAPKGUI:
# VIN信息
vin_frame = tk.Frame(status_bar_frame, bg=self.colors['bg_light'])
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="VIN码:",
font=('Microsoft YaHei', 9),
fg=self.colors['text'],
bg=self.colors['bg_light']).pack(side=tk.LEFT)
bg=self.colors['bg_light'])
self.vin_label_title.pack(side=tk.LEFT)
self.vin_label = tk.Label(vin_frame, text="未获取",
font=('Microsoft YaHei', 9, 'bold'),
fg='#636e72',
@@ -230,10 +386,11 @@ class ADKAPKGUI:
# 授权状态
auth_frame = tk.Frame(status_bar_frame, bg=self.colors['bg_light'])
auth_frame.pack(side=tk.LEFT, padx=20, pady=5)
tk.Label(auth_frame, text="授权:",
self.auth_label_title = tk.Label(auth_frame, text="授权:",
font=('Microsoft YaHei', 9),
fg=self.colors['text'],
bg=self.colors['bg_light']).pack(side=tk.LEFT)
bg=self.colors['bg_light'])
self.auth_label_title.pack(side=tk.LEFT)
self.auth_label = tk.Label(auth_frame, text="未验证",
font=('Microsoft YaHei', 9, 'bold'),
fg='#636e72',
@@ -242,14 +399,14 @@ class ADKAPKGUI:
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="🔄 检查",
command=self.refresh_device_status,
font=('Microsoft YaHei', 8),
fg=self.colors['accent'],
bg=self.colors['bg_light'],
relief=tk.FLAT,
cursor='hand2')
refresh_btn.pack(side=tk.RIGHT, padx=10, pady=5)
self.btn_refresh.pack(side=tk.RIGHT, padx=10, pady=5)
# 提示信息区域(设备状态下方)
tips_frame = tk.Frame(main_frame, bg=self.colors['bg_light'], relief=tk.RAISED, bd=1)
@@ -301,10 +458,11 @@ class ADKAPKGUI:
log_title_frame.pack(fill=tk.X)
log_title_frame.pack_propagate(False)
tk.Label(log_title_frame, text="📋 运行日志",
self.log_title_label = tk.Label(log_title_frame, text="📋 运行日志",
font=('Microsoft YaHei', 10, 'bold'),
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="🗑 清空日志",
command=self.clear_log,
@@ -348,6 +506,25 @@ class ADKAPKGUI:
bg=self.colors['bg_light'])
self.status_text.pack(side=tk.LEFT, padx=10)
# 主题和语言切换按钮
self.btn_theme_switch = tk.Button(bottom_status, text="🌙 暗色",
command=self.toggle_theme,
font=('Microsoft YaHei', 8),
fg=self.colors['accent'],
bg=self.colors['bg_light'],
relief=tk.FLAT,
cursor='hand2')
self.btn_theme_switch.pack(side=tk.RIGHT, padx=5)
self.btn_lang_switch = tk.Button(bottom_status, text="EN",
command=self.toggle_lang,
font=('Microsoft YaHei', 8, 'bold'),
fg=self.colors['accent'],
bg=self.colors['bg_light'],
relief=tk.FLAT,
cursor='hand2')
self.btn_lang_switch.pack(side=tk.RIGHT, padx=5)
# 调试模式快捷键
self.root.bind('<Control-Shift-D>', self._toggle_debug)
@@ -398,6 +575,84 @@ class ADKAPKGUI:
"""将函数调度到主线程执行,确保线程安全"""
self.root.after(0, func, *args, **kwargs)
def t(self, key):
"""获取翻译文本"""
return self.T.get(self.lang, self.T['zh']).get(key, key)
def toggle_lang(self):
"""切换语言"""
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._refresh_ui_texts()
self.log(f"语言已切换为 {'English' if self.lang == 'en' else '中文'}", "INFO")
def toggle_theme(self):
"""切换主题"""
if self.theme == 'dark':
self.colors = dict(self.colors_light)
self.theme = 'light'
self.btn_theme_switch.config(text=self.t('theme_dark'))
else:
self.colors = dict(self.colors_dark)
self.theme = 'dark'
self.btn_theme_switch.config(text=self.t('theme_light'))
self._apply_theme()
def _apply_theme(self):
"""应用当前主题到所有控件"""
c = self.colors
self.root.configure(bg=c['bg_dark'])
# 更新 ttk 样式
style = ttk.Style()
style.configure('TFrame', background=c['bg_dark'])
style.configure('TLabel', background=c['bg_dark'], foreground=c['text'])
style.configure('TLabelframe', background=c['bg_dark'], foreground=c['text'])
style.configure('TLabelframe.Label', background=c['bg_dark'], foreground=c['accent'])
style.configure('TProgressbar', background=c['accent'], troughcolor=c['bg_light'], borderwidth=0)
# 更新日志颜色
self.log_text.tag_config('INFO', foreground='#74b9ff')
self.log_text.tag_config('SUCCESS', foreground='#55efc4')
self.log_text.tag_config('ERROR', foreground='#ff7675')
self.log_text.tag_config('WARNING', foreground='#ffeaa7')
self.log_text.tag_config('CMD', foreground='#a29bfe')
if self.theme == 'light':
self.log_text.configure(bg='#ffffff', fg='#2d3436')
else:
self.log_text.configure(bg='#2d2d3d', fg='#e0e0e0')
def _refresh_ui_texts(self):
"""刷新所有UI文本"""
t = self.t
widgets = [
(getattr(self, 'title_label', None), 'title', None),
(getattr(self, 'subtitle_label', None), 'about_company', None),
(getattr(self, 'btn_root', None), 'btn_root', None),
(getattr(self, 'btn_push', None), 'btn_push', None),
(getattr(self, 'btn_install_all', None), 'btn_install', None),
(getattr(self, 'btn_language', None), 'btn_language', None),
(getattr(self, 'btn_timezone', None), 'btn_timezone', None),
(getattr(self, 'btn_settings', None), 'btn_settings', None),
(getattr(self, 'btn_reboot', None), 'btn_reboot', None),
(getattr(self, 'btn_exit', None), 'btn_disable_upgrade', None),
(getattr(self, 'btn_clear', None), 'btn_clear_log', None),
(getattr(self, 'log_title_label', None), 'log_title', None),
(getattr(self, 'status_text', None), 'status_ready', None),
(getattr(self, 'device_label', None), 'device_label', None),
(getattr(self, 'vin_label_title', None), 'vin_label', None),
(getattr(self, 'auth_label_title', None), 'auth_label', None),
(getattr(self, 'btn_refresh', None), 'btn_refresh', None),
(getattr(self, 'hint_label', None), 'hint_factory', None),
]
for w, key, _ in widgets:
if w:
w.config(text=t(key))
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'))
# 刷新设备状态显示
if self.vin:
self._update_device_status_impl(self.device_connected, self.vin,
getattr(self, '_last_authorized', False))
def _log_impl(self, message, level="INFO"):
"""日志写入的实际实现(必须在主线程调用)"""
timestamp = datetime.now().strftime("%H:%M:%S")
@@ -462,23 +717,25 @@ class ADKAPKGUI:
def _update_device_status_impl(self, connected, vin, authorized):
"""设备状态UI更新的实际实现(必须在主线程调用)"""
self._last_authorized = authorized
t = self.t
if connected:
self.status_indicator.itemconfig(self.status_dot, fill=self.colors['success'])
self.device_status_label.config(text="已连接", fg=self.colors['success'])
self.device_status_label.config(text=t('status_connected'), fg=self.colors['success'])
if vin:
self.vin_label.config(text=vin, fg=self.colors['success'])
if authorized:
self.auth_label.config(text="已授权", fg=self.colors['success'])
self.auth_label.config(text=t('auth_yes'), fg=self.colors['success'])
else:
self.auth_label.config(text="未授权", fg=self.colors['error'])
self.auth_label.config(text=t('auth_no'), fg=self.colors['error'])
else:
self.vin_label.config(text="未获取", fg=self.colors['error'])
self.auth_label.config(text="未验证", fg=self.colors['error'])
self.vin_label.config(text=t('vin_none'), fg=self.colors['error'])
self.auth_label.config(text=t('auth_none'), fg=self.colors['error'])
else:
self.status_indicator.itemconfig(self.status_dot, fill=self.colors['error'])
self.device_status_label.config(text="未连接", fg=self.colors['error'])
self.vin_label.config(text="未获取", fg=self.colors['error'])
self.auth_label.config(text="未验证", fg=self.colors['error'])
self.device_status_label.config(text=t('status_disconnected'), fg=self.colors['error'])
self.vin_label.config(text=t('vin_none'), fg=self.colors['error'])
self.auth_label.config(text=t('auth_none'), fg=self.colors['error'])
def check_device_connection(self):
"""检查设备是否连接"""
@@ -563,7 +820,16 @@ class ADKAPKGUI:
def extract_package_silent(self):
"""静默解压语言包(带进度)"""
if not self.package_file.exists():
self.log(f"未找到资源包 ({self.package_file.name})", "ERROR")
self.log(f"错误:未找到资源包 ({self.package_file})", "ERROR")
return False
if not self.extract_password:
self.log("错误:解压密码未设置", "ERROR")
return False
# 检查 7za 是否存在
if not os.path.exists(self.sz):
self.log(f"错误:未找到 7za.exe ({self.sz})", "ERROR")
return False
try:
@@ -586,19 +852,33 @@ class ADKAPKGUI:
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")
# 使用 7za 高速解压
# 使用 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, text=True, creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0
[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:
stderr = result.stderr.strip()
if stderr:
self.log(f"解压失败: {stderr[:200]}", "ERROR")
# 尝试解析错误信息(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
if err_msg.strip():
self.log(f"解压失败: {err_msg.strip()[:300]}", "ERROR")
else:
self.log("解压失败,请检查密码是否正确", "ERROR")
self.log(f"解压失败 (返回码: {result.returncode}),请检查密码是否正确", "ERROR")
return False
self.update_progress(1, 1, "资源加载完成")
@@ -615,13 +895,21 @@ 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("警告:未找到 app/priv-app 目录", "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")
return True
except Exception as e:
self.log(f"资源准备失败", "ERROR")
if getattr(self, 'debug_mode', False):
self.log(f"资源准备失败: {str(e)}", "ERROR")
import traceback
self.log(traceback.format_exc(), "ERROR")
else:
self.log("资源准备失败,请检查网络连接后重试", "ERROR")
return False
def check_environment(self):
@@ -1160,7 +1448,7 @@ class ADKAPKGUI:
if self.debug_mode:
self.debug_mode = False
self.log("调试模式已关闭", "WARNING")
self.status_text.config(text="就绪")
self.status_text.config(text=self.t('status_ready'))
self.refresh_device_status()
return
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+368
View File
@@ -0,0 +1,368 @@
7-Zip Extra history
-------------------
This file contains only information about changes related to that package exclusively.
The full history of changes is listed in history.txt in main 7-Zip program.
26.01 2026-04-27
-------------------------
- new -spo[d|c|r] switch specifies the path generation mode for the output directory
for archive extraction. The output directory path is generated from the path specified
in the -o{dir_path} switch and the name of the archive being unpacked.
-spod : for Linux/Posix/macOS: -o{dir_path} specifies the direct path to the output directory.
The asterisk (*) character in {dir_path} will not be replaced by the archive name.
-spoc : 7-Zip will concatenate the path specified in -o{dir_path} with the archive name
to form the final path to the output directory.
-spor : 7-Zip will replace asterisk (*) character in the path specified in the -o{dir_path}
with the archive name. This is the default option.
- some bugs were fixed.
26.00 2026-02-12
-------------------------
- improved code for ZIP support.
- the bug was fixed: 7-Zip could not correctly extract TAR archives containing sparse files.
- some bugs were fixed.
25.01 2025-08-03
-------------------------
- The code for handling symbolic links has been changed
to provide greater security when extracting files from archives.
Command line switch -snld20 can be used to bypass default security
checks when creating symbolic links.
25.00 2025-07-05
-------------------------
- 7-Zip for Windows can now use more than 64 CPU threads for compression
to zip/7z/xz archives and for the 7-Zip benchmark.
If there are more than one processor group in Windows (on systems with more than
64 cpu threads), 7-Zip distributes running CPU threads across different processor groups.
- bzip2 compression speed was increased by 15-40%.
- deflate (zip/gz) compression speed was increased by 1-3%.
- improved support for zip, cpio and fat archives.
- fixed some bugs and vulnerabilities.
24.09 2024-11-29
-------------------------
- The default dictionary size values for LZMA/LZMA2 compression methods were increased:
dictionary size compression level
v24.08 v24.09 v24.09
32-bit 64-bit
8 MB 16 MB 16 MB -mx4
16 MB 32 MB 32 MB -mx5 : Normal
32 MB 64 MB 64 MB -mx6
32 MB 64 MB 128 MB -mx7 : Maximum
64 MB 64 MB 256 MB -mx8
64 MB 64 MB 256 MB -mx9 : Ultra
The default dictionary size values for 32-bit versions of LZMA/LZMA2 don't exceed 64 MB.
- If an archive update operation uses a temporary archive folder and
the archive is moved to the destination folder, 7-Zip shows the progress of moving
the archive file, as this operation can take a long time if the archive is large.
- Some bugs were fixed.
24.08 2024-08-11
-------------------------
- The bug in 7-Zip 24.00-24.07 was fixed:
For creating a zip archive: 7-Zip could write extra zero bytes after the end of the archive,
if a file included to archive cannot be compressed to a size smaller than original.
The created zip archive is correct except for the useless zero bytes after the end of the archive.
When unpacking such a zip archive, 7-Zip displays a warning:
"WARNING: There are data after the end of archive".
24.07 2024-06-19
-------------------------
- The bug was fixed: 7-Zip could crash for some incorrect ZSTD archives.
24.06 2024-05-26
-------------------------
- The bug was fixed: 7-Zip could not unpack some ZSTD archives.
24.05 2024-05-14
-------------------------
- New switch -myv={MMNN} to set decoder compatibility version for 7z archive creating.
{MMNN} is 4-digit number that represents the version of 7-Zip without a dot.
If -myv={MMNN} switch is specified, 7-Zip will only use compression methods that can
be decoded by the specified version {MMNN} of 7-Zip and newer versions.
If -myv={MMNN} switch is not specified, -myv=2300 is used, and 7-Zip will only
use compression methods that can be decoded by 7-Zip 23.00 and newer versions.
- New switch -myfa={FilterID} to allow 7-Zip to use the specified filter method for 7z archive creating.
- New switch -myfd={FilterID} to disallow 7-Zip to use the specified filter method for 7z archive creating.
24.03 2024-03-23
-------------------------
- 7-Zip now can use new RISCV filter for compression to 7z and xz archives.
RISCV filter can increase compression ratio for data containing executable
files compiled for RISC-V architecture.
- The speed for LZMA and LZMA2 decompression in ARM64 version for Windows
was increased by 20%-60%.
- new switch -smemx{size}g : to set allowed memory usage limit for RAR archive unpacking.
RAR archives can use dictionary up 64 GB. Default allowed limit for RAR unpacking is 4 GB.
- -slmu switch : to show timestamps as UTC instead of LOCAL TIME.
- -slsl switch : in console 7-Zip for Windows : to show file paths with
linux path separator slash '/' instead of backslash separator '\'.
- 7-Zip supports .sha256 files that use backslash path separator '\'.
- Some bugs were fixed.
24.01 2024-01-31
-------------------------
- 7-Zip now can unpack ZSTD archives (.zst filename extension).
- 7-Zip now can unpack ZIP archives that use ZSTD compression method.
- 7-Zip now supports fast hash algorithm XXH64 that is used in ZSTD.
- Speed optimizations for archive unpacking: zip, gz, cab.
- Speed optimizations for hash caclulation: CRC-32, CRC-64.
- arm64 binaries are included to 7-Zip Extra package.
- The bug was fixed: 7-Zip for Linux could fail for multivolume creation in some cases.
- Some bugs were fixed.
23.01 2023-06-20
-------------------------
- Some bugs were fixed.
23.00 2023-05-07
-------------------------
- 7-Zip now can use new ARM64 filter for compression to 7z and xz archives.
ARM64 filter can increase compression ratio for data containing executable
files compiled for ARM64 (AArch64) architecture.
Also 7-Zip now parses executable files (that have exe and dll filename extensions)
before compressing, and it selects appropriate filter for each parsed file:
- BCJ or BCJ2 filter for x86 executable files,
- ARM64 filter for ARM64 executable files.
Previous versions by default used x86 filter BCJ or BCJ2 for all exe/dll files.
- Default section size for BCJ2 filter was changed from 64 MiB to 240 MiB.
It can increase compression ratio for executable files larger than 64 MiB.
- When new 7-Zip creates multivolume archive, 7-Zip keeps in open state
only volumes that still can be changed. Previous versions kept all volumes
in open state until the end of the archive creation.
- 7-Zip for Linux and macOS now can reduce the number of simultaneously open files,
when 7-Zip opens, extracts or creates multivolume archive. It allows to avoid
the failures for cases with big number of volumes, bacause there is a limitation
for number of open files allowed for a single program in Linux and macOS.
- The bugs were fixed:
- ZIP archives: if multithreaded zip compression was performed with more than one
file to stdout stream (-so switch), 7-zip didn't write "data descriptor" for some files.
- Some another bugs were fixed.
22.00 2022-06-16
-------------------------
- 7-Zip now can create TAR archives in POSIX (pax) tar format with the switches
-ttar -mm=pax or -ttar -mm=posix
- 7-Zip now can store additional file timestamps with high precision (1 ns in Linux)
in tar/pax archives with the following switches:
-ttar -mm=pax -mtp=3 -mtc -mta
21.07 2021-12-26
-------------------------
- New switches: -spm and -im!{file_path} to exclude directories from processing
for specified paths that don't contain path separator character at the end of path.
- The sorting order of files in archives was slightly changed to be more consistent
for cases where the name of some directory is the same as the prefix part of the name
of another directory or file.
- TAR archives created by 7-Zip now are more consistent with archives created by GNU TAR program.
21.06 2021-11-24
-------------------------
- New switch -mmemuse={N}g / -mmemuse=p{N} to set a limit on memory usage (RAM)
for compressing and decompressing.
- Bug in versions 21.00-21.05 was fixed:
7-Zip didn't set attributes of directories during archive extracting.
- Some bugs were fixed.
21.04 beta 2021-11-02
-------------------------
- 7-Zip now reduces the number of working CPU threads for compression,
if RAM size is not enough for compression with big LZMA2 dictionary.
- 7-Zip now can create and check "file.sha256" text files that contain the list
of file names and SHA-256 checksums in format compatible with sha256sum program.
7-Zip can work with such checksum files as with archives,
but these files don't contain real file data.
The context menu commands for command line version::
7z a -thash file.sha256 *.txt
7z t -thash file.sha256
7z t -thash -shd. file.sha256
New -shd{dir_path} switch to set the directory that is used to check files
referenced by "file.sha256" file for "Test" operation.
If -shd{dir_path} is not specified, 7-Zip uses the directory where "file.sha256" is stored.
- New -xtd switch to exclude directory metadata records from processing.
21.03 beta 2021-07-20
-------------------------
- The maximum dictionary size for LZMA/LZMA2 compressing was increased to 4 GB (3840 MiB).
- Minor speed optimizations in LZMA/LZMA2 compressing.
21.02 alpha 2021-05-06
-------------------------
- 7-Zip now writes additional field for filename in UTF-8 encoding to zip archives.
It allows to extract correct file name from zip archives on different systems.
- Some changes and improvements in ZIP and TAR code.
21.01 alpha 2021-03-09
-------------------------
- The improvements for speed of ARM64 version using hardware CPU instructions
for AES, CRC-32, SHA-1 and SHA-256.
- The bug in versions 18.02 - 21.00 was fixed:
7-Zip could not correctly extract some ZIP archives created with xz compression method.
- Some bugs were fixed.
20.02 alpha 2020-08-08
-------------------------
- The default number of LZMA2 chunks per solid block in 7z archive was increased to 64.
It allows to increase the compression speed for big 7z archives, if there is a big number
of CPU cores and threads.
- The speed of PPMd compressing/decompressing was increased for 7z/ZIP archives.
- The new -ssp switch. If the switch -ssp is specified, 7-Zip doesn't allow the system
to modify "Last Access Time" property of source files for archiving and hashing operations.
- Some bugs were fixed.
20.00 alpha 2020-02-06
-------------------------
- 7-Zip now supports new optional match finders for LZMA/LZMA2 compression: bt5 and hc5,
that can work faster than bt4 and hc4 match finders for the data with big redundancy.
- The compression ratio was improved for Fast and Fastest compression levels with the
following default settings:
- Fastest level (-mx1) : hc5 match finder with 256 KB dictionary.
- Fast level (-mx3) : hc5 match finder with 4 MB dictionary.
- Minor speed optimizations in multithreaded LZMA/LZMA2 compression for Normal/Maximum/Ultra
compression levels.
- bzip2 decoding code was updated to support bzip2 archives, created by lbzip2 program.
19.02 alpha 2019-09-05
-------------------------
- 7-Zip now can use new x86/x64 hardware instructions for SHA-1 and SHA-256, supported
by AMD Ryzen and latest Intel CPUs: Ice Lake and Goldmont.
It increases
- the speed of SHA-1/SHA-256 hash value calculation,
- the speed of encryption/decryption in zip AES,
- the speed of key derivation for encryption/decryption in 7z/zip/rar archives.
- The speed of zip AES encryption and 7z/zip/rar AES decryption was increased with
the following improvements:
- 7-Zip now can use new x86/x64 VAES (AVX Vector AES) instructions, supported by
Intel Ice Lake CPU.
- The existing code of x86/x64 AES-NI was improved also.
- Some bugs were fixed.
19.00 2019-02-21
-------------------------
- Encryption strength for 7z archives was increased:
the size of random initialization vector was increased from 64-bit to 128-bit,
and the pseudo-random number generator was improved.
- Some bugs were fixed.
18.06 2018-12-30
-------------------------
- The speed for LZMA/LZMA2 compressing was increased by 3-10%,
and there are minor changes in compression ratio.
- Some bugs were fixed.
18.05 2018-04-30
-------------------------
- The speed for LZMA/LZMA2 compressing was increased
by 8% for fastest/fast compression levels and
by 3% for normal/maximum compression levels.
18.03 beta 2018-03-04
-------------------------
- The speed for single-thread LZMA/LZMA2 decoding
was increased by 30% in x64 version and by 3% in x86 version.
- 7-Zip now can use multi-threading for 7z/LZMA2 decoding,
if there are multiple independent data chunks in LZMA2 stream.
9.35 beta 2014-12-07
------------------------------
- SFX modules were moved to LZMA SDK package.
9.34 alpha 2014-06-22
------------------------------
- Minimum supported system now is Windows 2000 for EXE and DLL files.
- all EXE and DLL files use msvcrt.dll.
- 7zr.exe now support AES encryption.
9.18 2010-11-02
------------------------------
- New small SFX module for installers.
9.17 2010-10-04
------------------------------
- New 7-Zip plugin for FAR Manager x64.
9.10 2009-12-30
------------------------------
- 7-Zip for installers now supports LZMA2.
9.09 2009-12-12
------------------------------
- LZMA2 compression method support.
- Some bugs were fixed.
4.65 2009-02-03
------------------------------
- Some bugs were fixed.
4.38 beta 2006-04-13
------------------------------
- SFX for installers now supports new properties in config file:
Progress, Directory, ExecuteFile, ExecuteParameters.
4.34 beta 2006-02-27
------------------------------
- ISetProperties::SetProperties:
it's possible to specify desirable number of CPU threads:
PROPVARIANT: name=L"mt", vt = VT_UI4, ulVal = NumberOfThreads
If "mt" is not defined, 7za.dll will check number of processors in system to set
number of desirable threads.
Now 7za.dll can use:
2 threads for LZMA compressing
N threads for BZip2 compressing
4 threads for BZip2 decompressing
Other codecs use only one thread.
Note: 7za.dll can use additional "small" threads with low CPU load.
- It's possible to call ISetProperties::SetProperties to specify "mt" property for decoder.
4.33 beta 2006-02-05
------------------------------
- Compressing speed and Memory requirements were increased.
Default dictionary size was increased: Fastest: 64 KB, Fast: 1 MB,
Normal: 4 MB, Max: 16 MB, Ultra: 64 MB.
- 7z/LZMA now can use only these match finders: HC4, BT2, BT3, BT4
4.27 2005-09-21
------------------------------
- Some GUIDs/interfaces were changed.
IStream.h:
ISequentialInStream::Read now works as old ReadPart
ISequentialOutStream::Write now works as old WritePart
+126
View File
@@ -0,0 +1,126 @@
7-Zip Extra 26.01
-----------------
7-Zip Extra is package of extra modules of 7-Zip.
7-Zip Copyright (C) 1999-2026 Igor Pavlov.
7-Zip is free software. Read License.txt for more information about license.
Source code of binaries can be found at:
http://www.7-zip.org/
This package contains the following files:
7za.exe - standalone console version of 7-Zip with reduced formats support.
7za.dll - library for working with 7z archives
7zxa.dll - library for extracting from 7z archives
License.txt - license information
readme.txt - this file
7-zip.chm - Help file
Far\ - plugin for Far Manager
x64\ - binaries for x64
arm64\ - binaries for arm64
All 32-bit binaries can work in:
Windows 2000 / 2003 / 2008 / XP / Vista / 7 / 8 / 10
and in any Windows x64 version with WoW64 support.
All x64 binaries can work in any Windows x64 version.
All x86 and x64 binaries use msvcrt.dll.
7za.exe
-------
7za.exe - is a standalone console version of 7-Zip with reduced formats support.
Extra: 7za.exe : support for only some formats of 7-Zip.
7-Zip: 7z.exe with 7z.dll : support for all formats of 7-Zip.
7za.exe and 7z.exe from 7-Zip have same command line interface.
7za.exe doesn't use external DLL files.
You can read Help File (7-zip.chm) from 7-Zip package for description
of all commands and switches for 7za.exe and 7z.exe.
7za.exe features:
- High compression ratio in 7z format
- Supported formats:
- Packing / unpacking: 7z, xz, ZIP, GZIP, BZIP2 and TAR
- Unpacking only: lzma, CAB, ZSTD.
- Highest compression ratio for ZIP and GZIP formats.
- Fast compression and decompression
- Strong AES-256 encryption in 7z and ZIP formats.
Note: another package "LZMA SDK" contains 7zr.exe - that is reduced version of 7za.exe.
But you can use 7zr.exe as "public domain" code.
DLL files
---------
7za.dll and 7zxa.dll are reduced versions of 7z.dll from 7-Zip.
7za.dll and 7zxa.dll support only 7z format.
Note: 7z.dll is main DLL file that works with all archive types in 7-Zip.
7za.dll and 7zxa.dll support the following decoding methods:
- LZMA, LZMA2, PPMD, BCJ, BCJ2, COPY, 7zAES, BZip2, Deflate.
7za.dll also supports 7z encoding with the following encoding methods:
- LZMA, LZMA2, PPMD, BCJ, BCJ2, COPY, 7zAES.
7za.dll and 7zxa.dll work via COM interfaces.
But these DLLs don't use standard COM interfaces for objects creating.
Look also example code that calls DLL functions (in source code of 7-Zip):
7zip\UI\Client7z
Another example of binary that uses these interface is 7-Zip itself.
The following binaries from 7-Zip use 7z.dll:
- 7z.exe (console version)
- 7zG.exe (GUI version)
- 7zFM.exe (7-Zip File Manager)
Note: The source code of LZMA SDK also contains the code for similar DLLs
(DLLs without BZip2, Deflate support). And these files from LZMA SDK can be
used as "public domain" code. If you use LZMA SDK files, you don't need to
follow GNU LGPL rules, if you want to change the code.
License FAQ
-----------
Can I use the EXE or DLL files from 7-Zip in a commercial application?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Yes, but you are required to specify in documentation for your application:
(1) that you used parts of the 7-Zip program,
(2) that 7-Zip is licensed under the GNU LGPL license and
(3) you must give a link to www.7-zip.org, where the source code can be found.
Can I use the source code of 7-Zip in a commercial application?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Since 7-Zip is licensed under the GNU LGPL you must follow the rules of that license.
In brief, it means that any LGPL'ed code must remain licensed under the LGPL.
For instance, you can change the code from 7-Zip or write a wrapper for some
code from 7-Zip and compile it into a DLL; but, the source code of that DLL
(including your modifications / additions / wrapper) must be licensed under
the LGPL or GPL.
Any other code in your application can be licensed as you wish. This scheme allows
users and developers to change LGPL'ed code and recompile that DLL. That is the
idea of free software. Read more here: http://www.gnu.org/.
Note: You can look also LZMA SDK, which is available under a more liberal license.
---
End of document
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.