xml_utils.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697
  1. import os.path
  2. import sys
  3. import xml.etree.ElementTree as ET
  4. from print_log import printlog
  5. namespaces = {'android': 'http://schemas.android.com/apk/res/android'}
  6. encoding = 'UTF-8'
  7. def get_package_name(manifest):
  8. """
  9. 获取包名
  10. """
  11. tree = ET.parse(manifest)
  12. root = tree.getroot()
  13. return root.attrib['package']
  14. def change_package_name(manifest, package_name):
  15. """
  16. 更改包名
  17. """
  18. for key in namespaces:
  19. ET.register_namespace(key, namespaces[key])
  20. tree = ET.parse(manifest)
  21. root = tree.getroot()
  22. root.attrib['package'] = package_name
  23. tree.write(manifest, encoding)
  24. def remove_launcher_activity(manifest):
  25. """
  26. 删除启动的activity
  27. """
  28. for key in namespaces:
  29. ET.register_namespace(key, namespaces[key])
  30. tree = ET.parse(manifest)
  31. root = tree.getroot()
  32. attrName = get_namespaces_format('android:name', namespaces)
  33. for node in root.findall('application/activity'):
  34. if len(list(node)) <= 0:
  35. continue
  36. for sub in list(node):
  37. if sub.tag != 'intent-filter':
  38. continue
  39. for sub2 in sub.getchildren():
  40. if not (sub2.tag == 'category' and sub2.attrib[attrName] == 'android.intent.category.LAUNCHER'):
  41. continue
  42. node.remove(sub)
  43. tree.write(manifest, encoding)
  44. return node.attrib[attrName]
  45. return 0
  46. def remove_launcher_activity_by_name(manifest, name):
  47. """
  48. 删除启动的activity
  49. """
  50. for key in namespaces:
  51. ET.register_namespace(key, namespaces[key])
  52. tree = ET.parse(manifest)
  53. root = tree.getroot()
  54. attrName = get_namespaces_format('android:name', namespaces)
  55. for node in root.findall('application/activity'):
  56. print('node ----> ' + node.attrib[attrName])
  57. if node.attrib[attrName] != name:
  58. continue
  59. for sub in list(node):
  60. if sub.tag != 'intent-filter':
  61. continue
  62. for sub2 in sub.getchildren():
  63. if not (sub2.tag == 'category' and sub2.attrib[attrName] == 'android.intent.category.LAUNCHER'):
  64. continue
  65. node.remove(sub)
  66. tree.write(manifest, encoding)
  67. return node.attrib[attrName]
  68. return 0
  69. def get_screen_orientation(manifest):
  70. """
  71. 获取启动activity的方向
  72. """
  73. for key in namespaces:
  74. ET.register_namespace(key, namespaces[key])
  75. tree = ET.parse(manifest)
  76. root = tree.getroot()
  77. attrName = get_namespaces_format('android:name', namespaces)
  78. attrOrientation = get_namespaces_format('android:screenOrientation', namespaces)
  79. for node in root.findall('application/activity'):
  80. if len(list(node)) <= 0:
  81. continue
  82. for sub in list(node):
  83. if sub.tag != 'intent-filter':
  84. continue
  85. for sub2 in sub.getchildren():
  86. if not (sub2.tag == 'category' and sub2.attrib[attrName] == 'android.intent.category.LAUNCHER'):
  87. continue
  88. return node.attrib[attrOrientation]
  89. return 0
  90. def add_launcher_activity(manifest, screen_orientation, activity):
  91. """
  92. 添加启动的activity
  93. """
  94. for key in namespaces:
  95. ET.register_namespace(key, namespaces[key])
  96. tree = ET.parse(manifest)
  97. root = tree.getroot()
  98. # activity
  99. '''<activity android:name=".LauncherActivity"
  100. android:theme="@style/LauncherStyle">
  101. <intent-filter>
  102. <action android:name="android.intent.action.MAIN" />
  103. <category android:name="android.intent.category.LAUNCHER" />
  104. </intent-filter>
  105. </activity>'''
  106. activity = ET.Element('activity', {'android:name': activity,
  107. 'android:theme': '@android:style/Theme.Holo.Light.NoActionBar.Fullscreen',
  108. 'android:launchMode': 'singleTop',
  109. 'android:configChanges': 'orientation|screenSize|keyboardHidden',
  110. 'android:screenOrientation': screen_orientation})
  111. intent = ET.Element('intent-filter')
  112. action = ET.Element('action', {'android:name': 'android.intent.action.MAIN'})
  113. category = ET.Element('category', {'android:name': 'android.intent.category.LAUNCHER'})
  114. intent.append(action)
  115. intent.append(category)
  116. activity.append(intent)
  117. application = root.find('application')
  118. application.insert(0, activity)
  119. tree.write(manifest, encoding)
  120. def change_app_name(manifest, string_res):
  121. """
  122. 更改app名
  123. """
  124. # 移除主activity的label
  125. remove_launcher_attr(manifest, 'label')
  126. return change_application_attr(manifest, 'label', string_res)
  127. def get_launcher_activity(root):
  128. """
  129. 获取启动的activity
  130. """
  131. attrName = get_namespaces_format('android:name', namespaces)
  132. for node in root.findall('application/activity'):
  133. if sys.version_info < (3, 9):
  134. node_dict = node.getchildren()
  135. else:
  136. node_dict = list(node)
  137. if len(node_dict) <= 0:
  138. continue
  139. for sub in node_dict:
  140. if sys.version_info < (3, 9):
  141. sub_dict = sub.getchildren()
  142. else:
  143. sub_dict = list(sub)
  144. if sub.tag != 'intent-filter':
  145. continue
  146. for sub2 in sub_dict:
  147. if sub2.tag == 'category' and sub2.attrib[attrName] == 'android.intent.category.LAUNCHER':
  148. return node
  149. return None
  150. def get_launcher_activities(manifest):
  151. """
  152. 获取启动的activity
  153. """
  154. attrName = get_namespaces_format('android:name', namespaces)
  155. nodeList = []
  156. tree = ET.parse(manifest)
  157. root = tree.getroot()
  158. for node in root.findall('application/activity'):
  159. if len(list(node)) <= 0:
  160. continue
  161. for sub in list(node):
  162. if sub.tag != 'intent-filter':
  163. continue
  164. for sub2 in sub.getchildren():
  165. if sub2.tag == 'category' and sub2.attrib[attrName] == 'android.intent.category.LAUNCHER':
  166. nodeList.append(node)
  167. return nodeList
  168. def get_launcher_activity_name(manifest):
  169. """
  170. 获取启动的activity
  171. """
  172. for key in namespaces:
  173. ET.register_namespace(key, namespaces[key])
  174. tree = ET.parse(manifest)
  175. root = tree.getroot()
  176. launcherActivity = get_launcher_activity(root)
  177. attrName = get_namespaces_format('android:name', namespaces)
  178. activityName = launcherActivity.attrib[attrName]
  179. return activityName
  180. def add_more_icon(manifest, icon, switch_icon):
  181. """
  182. 添加多图标
  183. """
  184. for key in namespaces:
  185. ET.register_namespace(key, namespaces[key])
  186. tree = ET.parse(manifest)
  187. root = tree.getroot()
  188. launcherActivity = get_launcher_activity(root)
  189. if launcherActivity is None:
  190. return 1
  191. attrName = get_namespaces_format('android:name', namespaces)
  192. activityName = launcherActivity.attrib[attrName]
  193. activityAlias = ET.Element('activity-alias', {'android:name': 'com.jmhy.sdk.icon.normal',
  194. 'android:enabled': 'false',
  195. 'android:icon': icon,
  196. 'android:targetActivity': activityName})
  197. activityAlias2 = ET.Element('activity-alias', {'android:name': 'com.jmhy.sdk.icon.switch',
  198. 'android:enabled': 'false',
  199. 'android:icon': switch_icon,
  200. 'android:targetActivity': activityName})
  201. for item in launcherActivity.getchildren():
  202. activityAlias.append(item)
  203. activityAlias2.append(item)
  204. application = root.find('application')
  205. application.append(activityAlias)
  206. application.append(activityAlias2)
  207. tree.write(manifest, encoding)
  208. return 0
  209. def remove_launcher_attr(manifest, attr_type):
  210. """
  211. 移除启动的activity的label
  212. """
  213. for key in namespaces:
  214. ET.register_namespace(key, namespaces[key])
  215. tree = ET.parse(manifest)
  216. root = tree.getroot()
  217. launcherActivity = get_launcher_activity(root)
  218. if launcherActivity is None:
  219. return 1
  220. attrName = get_namespaces_format('android:%s' % attr_type, namespaces)
  221. if attrName in launcherActivity.attrib:
  222. del launcherActivity.attrib[attrName]
  223. tree.write(manifest, encoding)
  224. return 0
  225. def change_launcher_attr(manifest, attr_type, attr_value):
  226. """
  227. 更改启动的activity的属性
  228. """
  229. for key in namespaces:
  230. ET.register_namespace(key, namespaces[key])
  231. tree = ET.parse(manifest)
  232. root = tree.getroot()
  233. launcherActivity = get_launcher_activity(root)
  234. if launcherActivity is None:
  235. return 1
  236. attrName = get_namespaces_format('android:%s' % attr_type, namespaces)
  237. if attrName in launcherActivity.attrib:
  238. launcherActivity.attrib[attrName] = attr_value
  239. tree.write(manifest, encoding)
  240. return 0
  241. def change_app_icon(manifest, icon_res):
  242. """
  243. 更改app icon
  244. """
  245. # 移除主activity的图标
  246. remove_launcher_attr(manifest, 'icon')
  247. return change_application_attr(manifest, 'icon', icon_res)
  248. def change_application_attr(manifest, attr_type, attr_value):
  249. """
  250. 更改Application的某个属性
  251. """
  252. for key in namespaces:
  253. ET.register_namespace(key, namespaces[key])
  254. tree = ET.parse(manifest)
  255. root = tree.getroot()
  256. application = root.find('application')
  257. # Namespaces
  258. attrName = get_namespaces_format('android:%s' % attr_type, namespaces)
  259. application.attrib[attrName] = attr_value
  260. tree.write(manifest, encoding)
  261. return 0
  262. def add_application_attr(manifest, attr_type, attr_value):
  263. """
  264. 更改Application的某个属性
  265. """
  266. for key in namespaces:
  267. ET.register_namespace(key, namespaces[key])
  268. tree = ET.parse(manifest)
  269. root = tree.getroot()
  270. application = root.find('application')
  271. # Namespaces
  272. attrName = get_namespaces_format('android:%s' % attr_type, namespaces)
  273. application.set(attrName, attr_value)
  274. tree.write(manifest, encoding)
  275. return 0
  276. def get_application_attr(manifest, attr_type):
  277. """
  278. 获取Application的某个属性
  279. """
  280. for key in namespaces:
  281. ET.register_namespace(key, namespaces[key])
  282. tree = ET.parse(manifest)
  283. root = tree.getroot()
  284. application = root.find('application')
  285. attrName = get_namespaces_format('android:%s' % attr_type, namespaces)
  286. if attrName in application.attrib:
  287. return application.attrib[attrName]
  288. return None
  289. def get_namespaces_format(text, space):
  290. """
  291. 格式化带namespaces的属性
  292. """
  293. for key in space:
  294. text = text.replace('%s:' % key, '{%s}' % space[key])
  295. return text
  296. def merge_manifest_res(app_manifest, lib_manifest):
  297. """
  298. 合并主文件
  299. """
  300. if not os.path.exists(lib_manifest):
  301. print('file "%s" not exists' % lib_manifest)
  302. return 1
  303. if not os.path.exists(app_manifest):
  304. print('file "%s" not exists' % app_manifest)
  305. return 1
  306. libInfo = get_lib_manifest_info(lib_manifest)
  307. appPermission = get_manifest_permission(app_manifest)
  308. diffPermission = merge_manifest_permission(appPermission, libInfo['permissionList'])
  309. merge_manifest(app_manifest, diffPermission, libInfo['activityList'])
  310. return 0
  311. def get_lib_manifest_info(lib_manifest):
  312. """
  313. 获取框架的主文件参数
  314. """
  315. for key in namespaces:
  316. ET.register_namespace(key, namespaces[key])
  317. tree = ET.parse(lib_manifest)
  318. root = tree.getroot()
  319. permissionList = root.findall('permissions/*')
  320. activityList = root.findall('application/*')
  321. info = {'permissionList': permissionList, 'activityList': activityList}
  322. return info
  323. def get_manifest_permission(app_manifest):
  324. """
  325. 获取主文件参数
  326. """
  327. for key in namespaces:
  328. ET.register_namespace(key, namespaces[key])
  329. tree = ET.parse(app_manifest)
  330. root = tree.getroot()
  331. return root.findall('uses-permission')
  332. def merge_manifest_permission(app_permission, lib_permission):
  333. """
  334. 合并主文件参数
  335. """
  336. newPermissionList = []
  337. for permission in lib_permission:
  338. if contain(app_permission, permission):
  339. continue
  340. newPermissionList.append(permission)
  341. return newPermissionList
  342. def contain(node_list, item):
  343. """
  344. 是否存在
  345. """
  346. attrName = get_namespaces_format('android:name', namespaces)
  347. for node in node_list:
  348. if node.attrib[attrName] == item.attrib[attrName]:
  349. return True
  350. return False
  351. def merge_manifest(app_manifest, permission_list, activity_list):
  352. """
  353. 将权限和activity加入到主文件
  354. """
  355. for key in namespaces:
  356. ET.register_namespace(key, namespaces[key])
  357. tree = ET.parse(app_manifest)
  358. root = tree.getroot()
  359. # 权限
  360. for item in permission_list:
  361. root.insert(0, item)
  362. # activity
  363. application = root.find('application')
  364. # 删除不支持属性
  365. attrName = get_namespaces_format('android:roundIcon', namespaces)
  366. if attrName in application.attrib:
  367. del application.attrib[attrName]
  368. # 删除不支持属性
  369. attrName = get_namespaces_format('android:testOnly', namespaces)
  370. if attrName in application.attrib:
  371. del application.attrib[attrName]
  372. # 删除不支持属性
  373. attrName = get_namespaces_format('android:debuggable', namespaces)
  374. if attrName in application.attrib:
  375. del application.attrib[attrName]
  376. # 覆盖
  377. # 移除相同的标签
  378. attrName = get_namespaces_format('android:name', namespaces)
  379. if sys.version_info < (3, 9):
  380. children_dict = application.getchildren()
  381. else:
  382. children_dict = list(application)
  383. for item in children_dict:
  384. if contain(activity_list, item):
  385. application.remove(item)
  386. for item in activity_list:
  387. application.insert(1, item)
  388. tree.write(app_manifest, encoding)
  389. def addMetaData(manifest, meta):
  390. """
  391. 添加meta-data
  392. """
  393. for key in namespaces:
  394. ET.register_namespace(key, namespaces[key])
  395. tree = ET.parse(manifest)
  396. root = tree.getroot()
  397. application = root.find('application')
  398. for key in meta:
  399. val = meta[key]
  400. '''if type(val) == str and val.isdigit():
  401. element = ET.Element('meta-data', {'android:name' : key, 'android:value' : '\\ ' + val})
  402. else:
  403. element = ET.Element('meta-data', {'android:name' : key, 'android:value' : val})'''
  404. element = ET.Element('meta-data', {'android:name': key, 'android:value': val})
  405. application.append(element)
  406. tree.write(manifest, encoding)
  407. def read_all_res(res_file, res_list):
  408. """
  409. 读取资源文件
  410. """
  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. printlog('[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) # 对子元素进行递归操作