分享

对于 UEngine 运行器的代码分析

 gfdgd_xi 2023-03-25 发布于广东

新版本Deepin/UOS发布后,可以在应用商店安装部分官方已适配的安卓应用,对爱好者来说,不能自己安装APK软件包始终差点意思,本程序可以为Deepin/UOS上的UEngine安卓运行环境安装自定义APK软件包,并能发送安装的APK包启动菜单到桌面或系统菜单。


而这个程序核心的难点是关于 APK 信息的获取,本人也并没有找到一个比较适合的轮子
(如果不想从主文件 mainwindow.py 看代码,也提供了相应的 API:https:///gfdgd-xi/uengine-runner/tree/main/api)
除了对 UEngine(deepin/UOS 出品的一款 Android 模拟器)的操作,其它大多数还是通用的
获取 APK 信息主要是调用了 aapt,可以通过,从以下源代码即可看出:

[Python] 纯文本查看 复制代码

1
2
3
# 获取 aapt 的所有信息
def GetApkInformation(apkFilePath: "apk 所在路径")->"获取 aapt 的所有信息":
 return GetCommandReturn("aapt dump badging '{}'".format(apkFilePath))


是调用了

[Asm] 纯文本查看 复制代码

1
aapt dump badging apk文件

的命令,返回的为如下内容(接下来均以程序 QQ 为例)

[Bash shell] 纯文本查看 复制代码

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package: name='com.tencent.mobileqq' versionCode='3118' versionName='8.9.3' platformBuildVersionName=''
install-location:'auto'
sdkVersion:'21'
targetSdkVersion:'26'
uses-permission: name='com.android.launcher.permission.INSTALL_SHORTCUT'
uses-permission: name='android.permission.WRITE_EXTERNAL_STORAGE'
uses-permission: name='android.permission.INTERNET'
uses-permission: name='android.permission.VIBRATE'
uses-permission: name='android.permission.ACCESS_NETWORK_STATE'
uses-permission: name='android.permission.CHANGE_CONFIGURATION'
uses-permission: name='android.permission.RECEIVE_BOOT_COMPLETED'
uses-permission: name='android.permission.WAKE_LOCK'
uses-permission: name='android.permission.SYSTEM_ALERT_WINDOW'
uses-permission: name='android.permission.RECORD_AUDIO'
uses-permission: name='com.tencent.msf.permission.account.sync'
uses-permission: name='android.permission.MODIFY_AUDIO_SETTINGS'
uses-permission: name='android.permission.CAMERA'
uses-permission: name='android.permission.CHANGE_WIFI_STATE'
uses-permission: name='android.permission.ACCESS_WIFI_STATE'
uses-permission: name='android.permission.KILL_BACKGROUND_PROCESSES'
uses-permission: name='com.android.launcher.permission.READ_SETTINGS'
uses-permission: name='com.android.launcher.permission.UNINSTALL_SHORTCUT'
uses-permission: name='android.permission.PERSISTENT_ACTIVITY'
uses-permission: name='android.permission.WRITE_SETTINGS'
uses-permission: name='android.permission.GET_TASKS'
uses-permission: name='com.tencent.permission.VIRUS_SCAN'
uses-permission: name='android.permission.READ_LOGS'
uses-permission: name='android.permission.READ_CONTACTS'
uses-permission: name='android.permission.FLASHLIGHT'
uses-permission: name='android.permission.BLUETOOTH'
uses-permission: name='android.permission.BLUETOOTH_ADMIN'
uses-permission: name='android.permission.BROADCAST_STICKY'
uses-permission: name='android.permission.WRITE_CONTACTS'
uses-permission: name='android.permission.WRITE_OWNER_DATA'
uses-permission: name='android.permission.SYSTEM_OVERLAY_WINDOW'
uses-permission: name='android.permission.USE_FINGERPRINT'
uses-permission: name='com.soter.permission.ACCESS_SOTER_KEYSTORE'
uses-permission: name='android.permission.USE_FACERECOGNITION'
uses-permission: name='android.permission.CHANGE_NETWORK_STATE'
uses-permission: name='android.permission.EXPAND_STATUS_BAR'
uses-permission: name='android.permission.INTERACT_ACROSS_USERS'
uses-permission: name='com.android.launcher.permission.WRITE_SETTINGS'
uses-permission: name='com.android.launcher2.permission.READ_SETTINGS'
uses-permission: name='com.android.launcher3.permission.READ_SETTINGS'
uses-permission: name='com.android.launcher3.permission.WRITE_SETTINGS'
uses-permission: name='com.google.android.launcher.permission.READ_SETTINGS'
uses-permission: name='com.bbk.launcher2.permission.READ_SETTINGS'
uses-permission: name='com.huaqin.launcherEx.permission.READ_SETTINGS'
uses-permission: name='com.htc.launcher.settings'
uses-permission: name='com.htc.launcher.permission.READ_SETTINGS'
uses-permission: name='com.htc.launcher.permission.WRITE_SETTINGS'
uses-permission: name='com.huawei.launcher2.permission.READ_SETTINGS'
uses-permission: name='com.huawei.launcher3.permission.READ_SETTINGS'
uses-permission: name='com.huawei.android.launcher.permission.READ_SETTINGS'
uses-permission: name='com.oppo.launcher.permission.READ_SETTINGS'
uses-permission: name='com.android.launcher2.permission.READ_SETTINGS'
uses-permission: name='com.meizu.android.launcher.permission.READ_SETTINGS'
uses-permission: name='com.meizu.launcher2.permission.READ_SETTINGS'
uses-permission: name='com.lenovo.launcher.permission.READ_SETTINGS'
uses-permission: name='com.ebproductions.android.launcher.permission.READ_SETTINGS'
uses-permission: name='com.sec.android.app.twlauncher.settings.READ_SETTINGS'
uses-permission: name='com.fede.launcher.permission.READ_SETTINGS'
uses-permission: name='net.qihoo.launcher.permission.READ_SETTINGS'
uses-permission: name='com.qihoo360.launcher.permission.READ_SETTINGS'
uses-permission: name='com.lge.launcher.permission.READ_SETTINGS'
uses-permission: name='org.adw.launcher.permission.READ_SETTINGS'
uses-permission: name='telecom.mdesk.permission.READ_SETTINGS'
uses-permission: name='com.tencent.mobileqq.permission.Pandora'
uses-permission: name='com.sonyericsson.home.permission.BROADCAST_BADGE'
uses-permission: name='com.sec.android.provider.badge.permission.READ'
uses-permission: name='com.sec.android.provider.badge.permission.WRITE'
uses-permission: name='com.lenovo.launcher.permission.BADGE_READ'
uses-permission: name='com.lenovo.launcher.permission.BADGE_WRITE'
uses-permission: name='com.huawei.android.launcher.permission.CHANGE_BADGE'
uses-permission: name='com.huawei.authentication.HW_ACCESS_AUTH_SERVICE'
uses-permission: name='android.permission.GET_ACCOUNTS'
uses-permission: name='android.permission.MANAGE_ACCOUNTS'
uses-permission: name='android.permission.AUTHENTICATE_ACCOUNTS'
uses-permission: name='android.permission.WRITE_CONTACTS'
uses-permission: name='android.permission.READ_SYNC_SETTINGS'
uses-permission: name='android.permission.WRITE_SYNC_SETTINGS'
uses-permission: name='android.permission.DISABLE_KEYGUARD'
uses-permission: name='android.permission.CHANGE_WIFI_MULTICAST_STATE'
uses-permission: name='com.tencent.mobileqq.permission.MM_MESSAGE'
uses-permission: name='android.permission.WRITE_CALENDAR'
uses-permission: name='android.permission.READ_CALENDAR'
uses-permission: name='android.permission.RESTART_PACKAGES'
uses-permission: name='android.permission.NFC'
uses-permission: name='android.permission.READ_APP_BADGE'
uses-permission: name='android.permission.READ_PACKAGE_BADGE'
uses-permission: name='miui.permission.READ_STEPS'
uses-permission: name='android.permission.REQUEST_INSTALL_PACKAGES'
uses-permission: name='com.google.android.gms.permission.ACTIVITY_RECOGNITION'
uses-permission: name='android.permission.FOREGROUND_SERVICE'
uses-permission: name='com.tencent.mobileqq.permission.TMF_SHARK'
uses-permission: name='com.tencent.photos.permission.DATA'
uses-permission: name='com.tencent.msf.permission.account.sync'
uses-permission: name='com.tencent.music.data.permission2'
uses-permission: name='android.permission.CHANGE_WIFI_STATE'
uses-permission: name='android.permission.INTERNET'
uses-permission: name='android.permission.ACCESS_WIFI_STATE'
uses-permission: name='android.permission.ACCESS_NETWORK_STATE'
uses-permission: name='android.permission.ACCESS_LOCATION'
uses-permission: name='android.permission.ACCESS_FINE_LOCATION'
uses-permission: name='android.permission.ACCESS_COARSE_LOCATION'
uses-permission: name='android.permission.CAMERA'
uses-permission: name='android.permission.WAKE_LOCK'
uses-permission: name='com.android.launcher.permission.INSTALL_SHORTCUT'
uses-permission: name='android.permission.RECEIVE_BOOT_COMPLETED'
uses-permission: name='com.tencent.msg.permission.pushnotify'
uses-permission: name='com.tencent.msf.permission.account.sync'
uses-permission: name='com.tencent.wifisdk.permission.disconnect'
uses-permission: name='com.tencent.qqhead.permission.getheadresp'
uses-permission: name='com.tencent.qzone.permission.notify'
uses-permission: name='com.tencent.mobileqq.permission.MIPUSH_RECEIVE'
uses-permission: name='com.tencent.qav.permission.broadcast'
uses-permission: name='android.permission.ALARM_LOCK'
uses-permission: name='android.permission.REORDER_TASKS'
uses-permission: name='com.tencent.mobileqq.vfs.broadcast'
uses-permission: name='com.tencent.mobileqq.backtrace.warmed_up'
uses-permission: name='com.android.vending.CHECK_LICENSE'
uses-permission: name='com.tencent.mobileqq.permission.PROCESS_PUSH_MSG'
uses-permission: name='com.tencent.mobileqq.permission.PUSH_PROVIDER'
uses-permission: name='com.coloros.mcs.permission.RECIEVE_MCS_MESSAGE'
uses-permission: name='com.vivo.assistant.StepProvider'
uses-permission: name='com.vivo.assistant.permission.access.provider'
uses-permission: name='com.vivo.assistant.permission.sport.broadcast'
uses-permission: name='com.asus.msa.SupplementaryDID.ACCESS'
uses-permission: name='com.oplus.ocs.permission.third'
application-label:'QQ'
application-icon-120:'r/b/icon.png'
application-icon-160:'r/b/icon.png'
application-icon-213:'r/b/icon.png'
application-icon-240:'r/b/icon.png'
application-icon-320:'r/b/icon.png'
application-icon-480:'r/b/icon.png'
application-icon-640:'r/b/icon.png'
application-icon-65534:'r/b/icon.png'
application-icon-65535:'r/b/icon.png'
application: label='QQ' icon='r/b/icon.png'
uses-library-not-required:'com.sec.android.app.multiwindow'
uses-library-not-required:'scamera_sep'
launchable-activity: name='com.tencent.mobileqq.activity.SplashActivity'  label='QQ' icon=''
uses-library-not-required:'com.google.android.media.effects'
uses-library-not-required:'com.motorola.hardware.frontcamera'
uses-library-not-required:'org.apache.http.legacy'
uses-library-not-required:'soterkeystore'
uses-permission: name='android.permission.READ_EXTERNAL_STORAGE'
uses-implied-permission: name='android.permission.READ_EXTERNAL_STORAGE' reason='requested WRITE_EXTERNAL_STORAGE'
feature-group: label=''
  uses-gl-es: '0x20000'
  uses-feature-not-required: name='android.hardware.bluetooth_le'
  uses-feature: name='android.hardware.camera'
  uses-feature-not-required: name='android.hardware.camera.autofocus'
  uses-feature-not-required: name='android.hardware.location'
  uses-feature-not-required: name='android.hardware.location.gps'
  uses-feature-not-required: name='android.hardware.location.network'
  uses-feature-not-required: name='android.hardware.telephony'
  uses-feature: name='android.hardware.bluetooth'
  uses-implied-feature: name='android.hardware.bluetooth' reason='requested android.permission.BLUETOOTH permission, requested android.permission.BLUETOOTH_ADMIN permission, and targetSdkVersion > 4'
  uses-feature: name='android.hardware.faketouch'
  uses-implied-feature: name='android.hardware.faketouch' reason='default feature for all apps'
  uses-feature: name='android.hardware.microphone'
  uses-implied-feature: name='android.hardware.microphone' reason='requested android.permission.RECORD_AUDIO permission'
  uses-feature: name='android.hardware.screen.landscape'
  uses-implied-feature: name='android.hardware.screen.landscape' reason='one or more activities have specified a landscape orientation'
  uses-feature: name='android.hardware.screen.portrait'
  uses-implied-feature: name='android.hardware.screen.portrait' reason='one or more activities have specified a portrait orientation'
  uses-feature: name='android.hardware.wifi'
  uses-implied-feature: name='android.hardware.wifi' reason='requested android.permission.ACCESS_WIFI_STATE permission, requested android.permission.CHANGE_WIFI_MULTICAST_STATE permission, and requested android.permission.CHANGE_WIFI_STATE permission'
provides-component:'wallpaper'
provides-component:'payment'
main
other-activities
other-receivers
other-services
supports-screens: 'small' 'normal' 'large' 'xlarge'
supports-any-density: 'true'
locales: '--_--'
densities: '120' '160' '213' '240' '320' '480' '640' '65534' '65535'
native-code: 'arm64-v8a'


从以上内容可以看到程序申请的权限、图标在包里的路径、程序名、版本、包名、activity等等,可以获取大多数的信息,因此,就可以基于 aapt 获取 APK 信息
首先是包名和版本号,从获取信息的第一行就可以看到了

[Asm] 纯文本查看 复制代码

1
package: name='com.tencent.mobileqq' versionCode='3118' versionName='8.9.3' platformBuildVersionName=''


因此采用遍历读取每一行开头是否含有

[Asm] 纯文本查看 复制代码

1
package:

,定位到所在行,包名排除掉 versionCode 后面和 name 及其前面即可获取包名
版本号排除掉 versionName 及其前面和 platformBuildVersionName 即可

写出的代码如下:

[Python] 纯文本查看 复制代码

01
02
03
04
05
06
07
08
09
10
11
# 获取 apk 包名
def GetApkPackageName(apkFilePath: "apk 所在路径")->"获取 apk 包名":
 info = GetApkInformation(apkFilePath)
 for line in info.split('\n'):
  if "package:" in line:
   line = line[0: line.index("versionCode='")]
   line = line.replace("package:", "")
   line = line.replace("name=", "")
   line = line.replace("'", "")
   line = line.replace(" ", "")
   return line


[Python] 纯文本查看 复制代码

01
02
03
04
05
06
07
08
09
10
11
12
13
14
# 获取 APK 版本号
def GetApkVersion(apkFilePath):
 info = GetApkInformation(apkFilePath)
 for line in info.split('\n'):
 if "package:" in line:
   if "compileSdkVersion='" in line:
 line = line.replace(line[line.index("compileSdkVersion='"): -1], "")
  if "platform" in line:
   line = line.replace(line[line.index("platform"): -1], "")
   line = line.replace(line[0: line.index("versionName='")], "")
   line = line.replace("versionName='", "")
   line = line.replace("'", "")
   line = line.replace(" ", "")
   return line


而打开 Android 应用不是只有包名就可以打开的,还需要程序的activity,同样还是使用 aapt 返回的信息,在 143 行

[Asm] 纯文本查看 复制代码

1
launchable-activity: name='com.tencent.mobileqq.activity.SplashActivity'  label='QQ' icon=''


截取中间的 com.tencent.mobileqq.activity.SplashActivity 即可,实现的代码如下:

[Python] 纯文本查看 复制代码

01
02
03
04
05
06
07
08
09
10
11
12
13
# 获取 apk Activity
def GetApkActivityName(apkFilePath: "apk 所在路径")->"获取 apk Activity":
 info = GetApkInformation(apkFilePath)
 for line in info.split('\n'):
  if "launchable-activity" in line:
   line = line[0: line.index("label='")]
   line = line.replace("launchable-activity: ", "")
   line = line.replace("'", "")
   line = line.replace(" ", "")
   line = line.replace("name=", "")
   line = line.replace("label=", "")
   line = line.replace("icon=", "")
   return line


而获取程序名称也和上面一样了,信息位于第 140 行,截取label=''中间的内容即可

[Asm] 纯文本查看 复制代码

1
application: label='QQ' icon='r/b/icon.png'


[Python] 纯文本查看 复制代码

1
2
3
4
5
6
7
8
# 获取软件的中文名称
def GetApkChineseLabel(apkFilePath)->"获取软件的中文名称":
 info = GetApkInformation(apkFilePath)
 for line in info.split('\n'):
  if "application-label:" in line:
   line = line.replace("application-label:", "")
   line = line.replace("'", "")
   return line



而获取图标是全部信息获取难度最高的,首先从上面的示例文件中可以看到第 140 行就指定了图标在 APK 内的路径,而 APK 本质也是一个压缩包,因此我们拿 zipfile 这个库进行解压

[Asm] 纯文本查看 复制代码

1
application: label='QQ' icon='r/b/icon.png'



下面是 UEngine 运行器 1.2.3 (commit:fde7a0d,文件名:main.py)实现读取图标的代码(1.3.0 就更新了获取方式了)

[Python] 纯文本查看 复制代码

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
# 获取图标在包内的路径
def GetApkIconInApk(apkFilePath)->"获取图标在包内的路径":
 info = GetApkInformation(apkFilePath)
 for line in info.split('\n'):
  if "application:" in line:
   line = line[line.index("icon='"): -1]
   line = line.replace("icon='", "")
   if "'" in line:
 line = line[0: line.index("'")]
   return line
 
# 获取 apk 文件的图标(部分程序不支持)
def SaveApkIcon(apkFilePath, iconSavePath)->"获取 apk 文件的图标(部分程序不支持)":
 zip = zipfile.ZipFile(apkFilePath)
 iconData = zip.read(GetApkIconInApk(apkFilePath))
 with open(iconSavePath, 'w+b') as saveIconFile:
  saveIconFile.write(iconData)


但是如果搞像 B 站这个应用,会发现提取不了图标,你会巧妙的发现,指向的不是 png 而是 xml

[Asm] 纯文本查看 复制代码

1
application: label='哔哩哔哩' icon='res/uFR.xml'


这是目前 1.8.0 的获取代码(因为这一块并不是我贡献的,所以不详细介绍)
(核心)getxmlimg.py:

[Python] 纯文本查看 复制代码

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import PIL.Image as Image
import PIL.ImageDraw as ImageDraw
import zipfile
import subprocess
import re
 
class getsavexml():
 
 def savexml(self,apkFilePath,xmlpath,iconSavePath):
  cmddumpid = "aapt dump xmltree "+ apkFilePath + " " + xmlpath
  print(cmddumpid)
  xmltree =  subprocess.getoutput(cmddumpid)
  xmls = xmltree.splitlines()
  # find strs ,print next line
  def FindStrs(lines,strs):
   i=0
   while i < len(lines):
 if re.search(strs,lines[i]):
  tmpstr = lines[i+1]
  += 1
  Resultstr = tmpstr.split(":")[-1].split("=")[-1].split("0x")[-1]
  return Resultstr
 else:
  += 1
  #从apk的信息中获取前后景图片的ID号
  backimgid =  FindStrs(xmls,"background")
  foreimgid =  FindStrs(xmls,"foreground")
  print(backimgid)
  print(foreimgid)
 
  # 直接从apk resource文件获取前后两层图片路径及ID字符串
  resource =  subprocess.getoutput("aapt dump --values resources " +  apkFilePath + "| grep -iE -A1 " +  "\"" + backimgid + "|" + foreimgid + "\"")
  resourcelines = resource.splitlines()
  print(resourcelines)
 
  # 从过滤出的字符串中获取所有相同ID的图片路径
  def Findpicpath(lines,imgid):
   i=0
   Resultstr = []
   while i < len(lines):
 if re.search(imgid,lines[i]) and re.search("string8",lines[i+1]) :
  print(lines[i+1])
  tmpstr = lines[i+1].replace("\"","")
  += 1
  Resultstr.append(tmpstr.split()[-1])
 else:
  += 1
   return Resultstr
 
  #获取所有带前后图片ID的图片路径(相同背景或者前景的图片ID但分辨率不一样)
  backimgs =  Findpicpath(resourcelines,backimgid)
  foreimgs =  Findpicpath(resourcelines,foreimgid)
  print(backimgs)
  print(foreimgs)
  #获取分辨率最高的图片路径
  def getmaxsize(imgs):
   = 0
   size=(0,0)
   zipapk = zipfile.ZipFile(apkFilePath)
   imgpath = ""
   while j < len(imgs):
 print(imgs[j])
 img = Image.open(zipapk.open(imgs[j]))
 print(imgs[j])
 print(img.size)
 if size < img.size:
  size = img.size
  imgpath = imgs[j]
 += 1
   return imgpath
 
  # 获取到文件列表后,进行比较分辨率,选取分辨率最高的张图片
  iconbackpath = getmaxsize(backimgs)
  iconforepath = getmaxsize(foreimgs)
  print(iconbackpath + " " + iconforepath)
 
  #从APK文件获取最终图片
  zipapk = zipfile.ZipFile(apkFilePath)
  iconback = zipapk.open(iconbackpath)
  iconfore = zipapk.open(iconforepath)
 
 
  # 叠加图片,mask 设置前景为蒙版
  iconbackimg =  Image.open(iconback).convert("RGBA")
  iconforeimg =  Image.open(iconfore).convert("RGBA")
  iconbackimg.paste(iconforeimg,mask=iconforeimg)
 
 
  # 圆角图片函数,网上拷贝的
  def circle_corner(img, radii):  #把原图片变成圆角,这个函数是从网上找的,原址 https://www./p/185266
   """
   圆角处理
   :param img: 源图象。
   :param radii: 半径,如:30。
   :return: 返回一个圆角处理后的图象。
   """
   # 画圆(用于分离4个角)
   circle = Image.new('L', (radii * 2, radii * 2), 0)  # 创建一个黑色背景的画布
   draw = ImageDraw.Draw(circle)
   draw.ellipse((00, radii * 2, radii * 2), fill=255)  # 画白色圆形
   # 原图
   img = img.convert("RGBA")
   w, h = img.size
   # 画4个角(将整圆分离为4个部分)
   alpha = Image.new('L', img.size, 255)
   alpha.paste(circle.crop((00, radii, radii)), (00))  # 左上角
   alpha.paste(circle.crop((radii, 0, radii * 2, radii)), (w - radii, 0))  # 右上角
   alpha.paste(circle.crop((radii, radii, radii * 2, radii * 2)), (w - radii, h - radii))  # 右下角
   alpha.paste(circle.crop((0, radii, radii, radii * 2)), (0, h - radii))  # 左下角
   # alpha.show()
   img.putalpha(alpha)  # 白色区域透明可见,黑色区域不可见
   return img
 
  #  圆角半径1/8边长,保存icon图片
  w,h = iconbackimg.size
  iconimg = circle_corner(iconbackimg,int(w/8))
  iconimg.save(iconSavePath)


mainwindow.py:

[Python] 纯文本查看 复制代码

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 保存apk图标
def SaveApkIcon(apkFilePath, iconSavePath)->"保存 apk 文件的图标":
 try:
  if os.path.exists(iconSavePath):
   os.remove(iconSavePath)
  info = GetApkInformation(apkFilePath)
  for line in info.split('\n'):
   if "application:" in line:
 xmlpath = line.split(":")[-1].split()[-1].split("=")[-1].replace("'","") 
 if xmlpath.endswith('.xml'):
   xmlsave = getsavexml()
   print(xmlpath)
   xmlsave.savexml(apkFilePath,xmlpath,iconSavePath)
   return
 else:
  zip = zipfile.ZipFile(apkFilePath)
  iconData = zip.read(xmlpath)
  with open(iconSavePath, 'w+b') as saveIconFile:
   saveIconFile.write(iconData)
   return
  print("None Icon! Show defult icon")
  shutil.copy(programPath + "/defult.png", iconSavePath)
 except:
  traceback.print_exc()
  print("Error, show defult icon")
  shutil.copy(programPath + "/defult.png", iconSavePath)


其它的例如 root 镜像、键盘映射、添加 adb 运行列表就不详细介绍了


核心的功能就完成了
(到时候审核过/没过后再传代码仓库和写 README)

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多