xml_utils.py 19 KB

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