xml_utils.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. import os.path
  2. import sys
  3. import xml.etree.ElementTree as ET
  4. namespaces = {'android': 'http://schemas.android.com/apk/res/android'}
  5. encoding = 'UTF-8'
  6. def get_package_name(manifest):
  7. """
  8. 获取包名
  9. """
  10. tree = ET.parse(manifest)
  11. root = tree.getroot()
  12. return root.attrib['package']
  13. def change_package_name(manifest, package_name):
  14. """
  15. 更改包名
  16. """
  17. for key in namespaces:
  18. ET.register_namespace(key, namespaces[key])
  19. tree = ET.parse(manifest)
  20. root = tree.getroot()
  21. root.attrib['package'] = package_name
  22. tree.write(manifest, encoding)
  23. def remove_launcher_activity(manifest):
  24. """
  25. 删除启动的activity
  26. """
  27. for key in namespaces:
  28. ET.register_namespace(key, namespaces[key])
  29. tree = ET.parse(manifest)
  30. root = tree.getroot()
  31. attrName = get_namespaces_format('android:name', namespaces)
  32. for node in root.findall('application/activity'):
  33. if len(node.getchildren()) <= 0:
  34. continue
  35. for sub in node.getchildren():
  36. if sub.tag != 'intent-filter':
  37. continue
  38. for sub2 in sub.getchildren():
  39. if not (sub2.tag == 'category' and sub2.attrib[attrName] == 'android.intent.category.LAUNCHER'):
  40. continue
  41. node.remove(sub)
  42. tree.write(manifest, encoding)
  43. return node.attrib[attrName]
  44. return 0
  45. def remove_launcher_activity_by_name(manifest, name):
  46. """
  47. 删除启动的activity
  48. """
  49. for key in namespaces:
  50. ET.register_namespace(key, namespaces[key])
  51. tree = ET.parse(manifest)
  52. root = tree.getroot()
  53. attrName = get_namespaces_format('android:name', namespaces)
  54. for node in root.findall('application/activity'):
  55. print('node ----> ' + node.attrib[attrName])
  56. if node.attrib[attrName] != name:
  57. continue
  58. for sub in node.getchildren():
  59. if sub.tag != 'intent-filter':
  60. continue
  61. for sub2 in sub.getchildren():
  62. if not (sub2.tag == 'category' and sub2.attrib[attrName] == 'android.intent.category.LAUNCHER'):
  63. continue
  64. node.remove(sub)
  65. tree.write(manifest, encoding)
  66. return node.attrib[attrName]
  67. return 0
  68. def get_screen_orientation(manifest):
  69. """
  70. 获取启动activity的方向
  71. """
  72. for key in namespaces:
  73. ET.register_namespace(key, namespaces[key])
  74. tree = ET.parse(manifest)
  75. root = tree.getroot()
  76. attrName = get_namespaces_format('android:name', namespaces)
  77. attrOrientation = get_namespaces_format('android:screenOrientation', namespaces)
  78. for node in root.findall('application/activity'):
  79. if len(node.getchildren()) <= 0:
  80. continue
  81. for sub in node.getchildren():
  82. if sub.tag != 'intent-filter':
  83. continue
  84. for sub2 in sub.getchildren():
  85. if not (sub2.tag == 'category' and sub2.attrib[attrName] == 'android.intent.category.LAUNCHER'):
  86. continue
  87. return node.attrib[attrOrientation]
  88. return 0
  89. def add_launcher_activity(manifest, screen_orientation, activity):
  90. """
  91. 添加启动的activity
  92. """
  93. for key in namespaces:
  94. ET.register_namespace(key, namespaces[key])
  95. tree = ET.parse(manifest)
  96. root = tree.getroot()
  97. # activity
  98. '''<activity android:name=".LauncherActivity"
  99. android:theme="@style/LauncherStyle">
  100. <intent-filter>
  101. <action android:name="android.intent.action.MAIN" />
  102. <category android:name="android.intent.category.LAUNCHER" />
  103. </intent-filter>
  104. </activity>'''
  105. activity = ET.Element('activity', {'android:name': activity,
  106. 'android:theme': '@android:style/Theme.Holo.Light.NoActionBar.Fullscreen',
  107. 'android:launchMode': 'singleTop',
  108. 'android:configChanges': 'orientation|screenSize|keyboardHidden',
  109. 'android:screenOrientation': screen_orientation})
  110. intent = ET.Element('intent-filter')
  111. action = ET.Element('action', {'android:name': 'android.intent.action.MAIN'})
  112. category = ET.Element('category', {'android:name': 'android.intent.category.LAUNCHER'})
  113. intent.append(action)
  114. intent.append(category)
  115. activity.append(intent)
  116. application = root.find('application')
  117. application.insert(0, activity)
  118. tree.write(manifest, encoding)
  119. def change_app_name(manifest, string_res):
  120. """
  121. 更改app名
  122. """
  123. # 移除主activity的label
  124. remove_launcher_attr(manifest, 'label')
  125. return change_application_attr(manifest, 'label', string_res)
  126. def get_launcher_activity(root):
  127. """
  128. 获取启动的activity
  129. """
  130. attrName = get_namespaces_format('android:name', namespaces)
  131. for node in root.findall('application/activity'):
  132. if sys.version_info < (3, 9):
  133. node_dict = node.getchildren()
  134. else:
  135. node_dict = list(node)
  136. if len(node_dict) <= 0:
  137. continue
  138. for sub in node_dict:
  139. if sys.version_info < (3, 9):
  140. sub_dict = sub.getchildren()
  141. else:
  142. sub_dict = list(sub)
  143. if sub.tag != 'intent-filter':
  144. continue
  145. for sub2 in sub_dict:
  146. if sub2.tag == 'category' and sub2.attrib[attrName] == 'android.intent.category.LAUNCHER':
  147. return node
  148. return None
  149. def get_launcher_activities(manifest):
  150. """
  151. 获取启动的activity
  152. """
  153. attrName = get_namespaces_format('android:name', namespaces)
  154. nodeList = []
  155. tree = ET.parse(manifest)
  156. root = tree.getroot()
  157. for node in root.findall('application/activity'):
  158. if len(node.getchildren()) <= 0:
  159. continue
  160. for sub in node.getchildren():
  161. if sub.tag != 'intent-filter':
  162. continue
  163. for sub2 in sub.getchildren():
  164. if sub2.tag == 'category' and sub2.attrib[attrName] == 'android.intent.category.LAUNCHER':
  165. nodeList.append(node)
  166. return nodeList
  167. def get_launcher_activity_name(manifest):
  168. """
  169. 获取启动的activity
  170. """
  171. for key in namespaces:
  172. ET.register_namespace(key, namespaces[key])
  173. tree = ET.parse(manifest)
  174. root = tree.getroot()
  175. launcherActivity = get_launcher_activity(root)
  176. attrName = get_namespaces_format('android:name', namespaces)
  177. activityName = launcherActivity.attrib[attrName]
  178. return activityName
  179. def add_more_icon(manifest, icon, switch_icon):
  180. """
  181. 添加多图标
  182. """
  183. for key in namespaces:
  184. ET.register_namespace(key, namespaces[key])
  185. tree = ET.parse(manifest)
  186. root = tree.getroot()
  187. launcherActivity = get_launcher_activity(root)
  188. if launcherActivity is None:
  189. return 1
  190. attrName = get_namespaces_format('android:name', namespaces)
  191. activityName = launcherActivity.attrib[attrName]
  192. activityAlias = ET.Element('activity-alias', {'android:name': 'com.jmhy.sdk.icon.normal',
  193. 'android:enabled': 'false',
  194. 'android:icon': icon,
  195. 'android:targetActivity': activityName})
  196. activityAlias2 = ET.Element('activity-alias', {'android:name': 'com.jmhy.sdk.icon.switch',
  197. 'android:enabled': 'false',
  198. 'android:icon': switch_icon,
  199. 'android:targetActivity': activityName})
  200. for item in launcherActivity.getchildren():
  201. activityAlias.append(item)
  202. activityAlias2.append(item)
  203. application = root.find('application')
  204. application.append(activityAlias)
  205. application.append(activityAlias2)
  206. tree.write(manifest, encoding)
  207. return 0
  208. def remove_launcher_attr(manifest, attr_type):
  209. """
  210. 移除启动的activity的label
  211. """
  212. for key in namespaces:
  213. ET.register_namespace(key, namespaces[key])
  214. tree = ET.parse(manifest)
  215. root = tree.getroot()
  216. launcherActivity = get_launcher_activity(root)
  217. if launcherActivity is None:
  218. return 1
  219. attrName = get_namespaces_format('android:%s' % attr_type, namespaces)
  220. if attrName in launcherActivity.attrib:
  221. del launcherActivity.attrib[attrName]
  222. tree.write(manifest, encoding)
  223. return 0
  224. def change_launcher_attr(manifest, attr_type, attr_value):
  225. """
  226. 更改启动的activity的属性
  227. """
  228. for key in namespaces:
  229. ET.register_namespace(key, namespaces[key])
  230. tree = ET.parse(manifest)
  231. root = tree.getroot()
  232. launcherActivity = get_launcher_activity(root)
  233. if launcherActivity is None:
  234. return 1
  235. attrName = get_namespaces_format('android:%s' % attr_type, namespaces)
  236. if attrName in launcherActivity.attrib:
  237. launcherActivity.attrib[attrName] = attr_value
  238. tree.write(manifest, encoding)
  239. return 0
  240. def change_app_icon(manifest, icon_res):
  241. """
  242. 更改app icon
  243. """
  244. # 移除主activity的图标
  245. remove_launcher_attr(manifest, 'icon')
  246. return change_application_attr(manifest, 'icon', icon_res)
  247. def change_application_attr(manifest, attr_type, attr_value):
  248. """
  249. 更改Application的某个属性
  250. """
  251. for key in namespaces:
  252. ET.register_namespace(key, namespaces[key])
  253. tree = ET.parse(manifest)
  254. root = tree.getroot()
  255. application = root.find('application')
  256. # Namespaces
  257. attrName = get_namespaces_format('android:%s' % attr_type, namespaces)
  258. application.attrib[attrName] = attr_value
  259. tree.write(manifest, encoding)
  260. return 0
  261. def add_application_attr(manifest, attr_type, attr_value):
  262. """
  263. 更改Application的某个属性
  264. """
  265. for key in namespaces:
  266. ET.register_namespace(key, namespaces[key])
  267. tree = ET.parse(manifest)
  268. root = tree.getroot()
  269. application = root.find('application')
  270. # Namespaces
  271. attrName = get_namespaces_format('android:%s' % attr_type, namespaces)
  272. application.set(attrName, attr_value)
  273. tree.write(manifest, encoding)
  274. return 0
  275. def get_application_attr(manifest, attr_type):
  276. """
  277. 获取Application的某个属性
  278. """
  279. for key in namespaces:
  280. ET.register_namespace(key, namespaces[key])
  281. tree = ET.parse(manifest)
  282. root = tree.getroot()
  283. application = root.find('application')
  284. attrName = get_namespaces_format('android:%s' % attr_type, namespaces)
  285. if attrName in application.attrib:
  286. return application.attrib[attrName]
  287. return None
  288. def get_namespaces_format(text, space):
  289. """
  290. 格式化带namespaces的属性
  291. """
  292. for key in space:
  293. text = text.replace('%s:' % key, '{%s}' % space[key])
  294. return text
  295. def merge_manifest_res(app_manifest, lib_manifest):
  296. """
  297. 合并主文件
  298. """
  299. if not os.path.exists(lib_manifest):
  300. print('file "%s" not exists' % lib_manifest)
  301. return 1
  302. if not os.path.exists(app_manifest):
  303. print('file "%s" not exists' % app_manifest)
  304. return 1
  305. libInfo = get_lib_manifest_info(lib_manifest)
  306. appPermission = get_manifest_permission(app_manifest)
  307. diffPermission = merge_manifest_permission(appPermission, libInfo['permissionList'])
  308. merge_manifest(app_manifest, diffPermission, libInfo['activityList'])
  309. return 0
  310. def get_lib_manifest_info(lib_manifest):
  311. """
  312. 获取框架的主文件参数
  313. """
  314. for key in namespaces:
  315. ET.register_namespace(key, namespaces[key])
  316. tree = ET.parse(lib_manifest)
  317. root = tree.getroot()
  318. permissionList = root.findall('permissions/*')
  319. activityList = root.findall('application/*')
  320. info = {'permissionList': permissionList, 'activityList': activityList}
  321. return info
  322. def get_manifest_permission(app_manifest):
  323. """
  324. 获取主文件参数
  325. """
  326. for key in namespaces:
  327. ET.register_namespace(key, namespaces[key])
  328. tree = ET.parse(app_manifest)
  329. root = tree.getroot()
  330. return root.findall('uses-permission')
  331. def merge_manifest_permission(app_permission, lib_permission):
  332. """
  333. 合并主文件参数
  334. """
  335. newPermissionList = []
  336. for permission in lib_permission:
  337. if contain(app_permission, permission):
  338. continue
  339. newPermissionList.append(permission)
  340. return newPermissionList
  341. def contain(node_list, item):
  342. """
  343. 是否存在
  344. """
  345. attrName = get_namespaces_format('android:name', namespaces)
  346. for node in node_list:
  347. if node.attrib[attrName] == item.attrib[attrName]:
  348. return True
  349. return False
  350. def merge_manifest(app_manifest, permission_list, activity_list):
  351. """
  352. 将权限和activity加入到主文件
  353. """
  354. for key in namespaces:
  355. ET.register_namespace(key, namespaces[key])
  356. tree = ET.parse(app_manifest)
  357. root = tree.getroot()
  358. # 权限
  359. for item in permission_list:
  360. root.insert(0, item)
  361. # activity
  362. application = root.find('application')
  363. # 删除不支持属性
  364. attrName = get_namespaces_format('android:roundIcon', namespaces)
  365. if attrName in application.attrib:
  366. del application.attrib[attrName]
  367. # 删除不支持属性
  368. attrName = get_namespaces_format('android:testOnly', namespaces)
  369. if attrName in application.attrib:
  370. del application.attrib[attrName]
  371. # 删除不支持属性
  372. attrName = get_namespaces_format('android:debuggable', namespaces)
  373. if attrName in application.attrib:
  374. del application.attrib[attrName]
  375. # 覆盖
  376. # 移除相同的标签
  377. attrName = get_namespaces_format('android:name', namespaces)
  378. if sys.version_info < (3, 9):
  379. children_dict = application.getchildren()
  380. else:
  381. children_dict = list(application)
  382. for item in children_dict:
  383. if contain(activity_list, item):
  384. application.remove(item)
  385. for item in activity_list:
  386. application.insert(1, item)
  387. tree.write(app_manifest, encoding)
  388. def addMetaData(manifest, meta):
  389. """
  390. 添加meta-data
  391. """
  392. for key in namespaces:
  393. ET.register_namespace(key, namespaces[key])
  394. tree = ET.parse(manifest)
  395. root = tree.getroot()
  396. application = root.find('application')
  397. for key in meta:
  398. val = meta[key]
  399. '''if type(val) == str and val.isdigit():
  400. element = ET.Element('meta-data', {'android:name' : key, 'android:value' : '\\ ' + val})
  401. else:
  402. element = ET.Element('meta-data', {'android:name' : key, 'android:value' : val})'''
  403. element = ET.Element('meta-data', {'android:name': key, 'android:value': val})
  404. application.append(element)
  405. tree.write(manifest, encoding)
  406. def read_all_res(res_file, res_list):
  407. """
  408. 读取资源文件
  409. """
  410. print('res file: %s' % res_file)
  411. tree = ET.parse(res_file)
  412. root = tree.getroot()
  413. if sys.version_info < (3, 9):
  414. res_list += root.getchildren()
  415. else:
  416. res_list += list(root)
  417. return res_list
  418. def remove_id_from_public(pub_file, remove_list):
  419. """
  420. 删除重复的资源
  421. """
  422. tree = ET.parse(pub_file)
  423. root = tree.getroot()
  424. same = False
  425. for node in root.getchildren():
  426. if containPublic(node, remove_list):
  427. print('delete public node : type is %s, name is %s' % (node.attrib['type'], node.attrib['name']))
  428. root.remove(node)
  429. same = True
  430. if same:
  431. tree.write(pub_file, encoding)
  432. def remove_same_res2(res_file, res_list, remove_list):
  433. """
  434. 删除重复的资源
  435. """
  436. tree = ET.parse(res_file)
  437. root = tree.getroot()
  438. same = False
  439. for node in root.getchildren():
  440. if containRes(node, res_list):
  441. # print('delete node : tag is %s, name is %s' % (node.tag, node.attrib['name']))
  442. root.remove(node)
  443. remove_list.append(node)
  444. same = True
  445. if same:
  446. tree.write(res_file, encoding)
  447. return remove_list
  448. def remove_same_values_res(res_file, res_list):
  449. """
  450. 删除重复的资源
  451. """
  452. tree = ET.parse(res_file)
  453. root = tree.getroot()
  454. same = False
  455. if sys.version_info < (3, 9):
  456. children_dict = root.getchildren()
  457. else:
  458. children_dict = list(root)
  459. for node in children_dict:
  460. if containRes(node, res_list):
  461. # print('delete node : tag is %s, name is %s' % (node.tag, node.attrib['name']))
  462. root.remove(node)
  463. same = True
  464. if same:
  465. tree.write(res_file, encoding)
  466. def containRes(node, res_list):
  467. """
  468. 是否重复
  469. """
  470. for item in res_list:
  471. if item.tag == node.tag and item.attrib['name'] == node.attrib['name']:
  472. return True
  473. return False
  474. def containPublic(node, remove_list):
  475. """
  476. 是否重复
  477. """
  478. for item in remove_list:
  479. if item.tag == node.attrib['type'] and item.attrib['name'] == node.attrib['name']:
  480. return True
  481. return False
  482. def remove_root_attr(manifest, attr_type):
  483. for key in namespaces:
  484. ET.register_namespace(key, namespaces[key])
  485. tree = ET.parse(manifest)
  486. root = tree.getroot()
  487. attrName = get_namespaces_format('android:%s' % attr_type, namespaces)
  488. if attrName in root.attrib:
  489. del root.attrib[attrName]
  490. tree.write(manifest, encoding)
  491. return 0
  492. def delete_activity_by_name(manifest, activity_name):
  493. """
  494. 删除activity
  495. """
  496. attrName = get_namespaces_format('android:name', namespaces)
  497. for key in namespaces:
  498. ET.register_namespace(key, namespaces[key])
  499. targetTree = ET.parse(manifest)
  500. targetRoot = targetTree.getroot()
  501. appNode = targetRoot.find('application')
  502. activitys = appNode.findall('activity')
  503. for activity in activitys:
  504. if activity.attrib[attrName] == activity_name:
  505. print('delete ------------------> ' + activity_name)
  506. appNode.remove(activity)
  507. targetTree.write(manifest, encoding)
  508. break
  509. def get_activity_by_name(root, activity_name):
  510. """
  511. 获取activity
  512. """
  513. attrName = get_namespaces_format('android:name', namespaces)
  514. for node in root.findall('application/activity'):
  515. print('activity name = ' + node.attrib[attrName])
  516. if node.attrib[attrName] == activity_name:
  517. return node
  518. return None
  519. def formatXml(manifest): # elemnt为传进来的Elment类,参数indent用于缩进,newline用于换行
  520. indent = '\t'
  521. newline = '\n'
  522. tree = ET.parse(manifest)
  523. root = tree.getroot() # 得到根元素,Element类
  524. prettyXml(root, indent, newline, level=0) # 对子元素进行递归操作
  525. ET.dump(root)
  526. return 0
  527. def prettyXml(element, indent, newline, level=0): # elemnt为传进来的Elment类,参数indent用于缩进,newline用于换行
  528. if element: # 判断element是否有子元素
  529. if element.text == None or element.text.isspace(): # 如果element的text没有内容
  530. element.text = newline + indent * (level + 1)
  531. else:
  532. element.text = newline + indent * (level + 1) + element.text.strip() + newline + indent * (level + 1)
  533. # else: # 此处两行如果把注释去掉,Element的text也会另起一行
  534. # element.text = newline + indent * (level + 1) + element.text.strip() + newline + indent * level
  535. temp = list(element) # 将elemnt转成list
  536. for subelement in temp:
  537. if temp.index(subelement) < (len(temp) - 1): # 如果不是list的最后一个元素,说明下一个行是同级别元素的起始,缩进应一致
  538. subelement.tail = newline + indent * (level + 1)
  539. else: # 如果是list的最后一个元素, 说明下一行是母元素的结束,缩进应该少一个
  540. subelement.tail = newline + indent * level
  541. prettyXml(subelement, indent, newline, level=level + 1) # 对子元素进行递归操作