import os.path import sys import xml.etree.ElementTree as ET from print_log import printlog namespaces = {'android': 'http://schemas.android.com/apk/res/android'} encoding = 'UTF-8' def get_package_name(manifest): """ 获取包名 """ tree = ET.parse(manifest) root = tree.getroot() return root.attrib['package'] def change_package_name(manifest, package_name): """ 更改包名 """ for key in namespaces: ET.register_namespace(key, namespaces[key]) tree = ET.parse(manifest) root = tree.getroot() root.attrib['package'] = package_name tree.write(manifest, encoding) def remove_launcher_activity(manifest): """ 删除启动的activity """ for key in namespaces: ET.register_namespace(key, namespaces[key]) tree = ET.parse(manifest) root = tree.getroot() attrName = get_namespaces_format('android:name', namespaces) for node in root.findall('application/activity'): if len(list(node)) <= 0: continue for sub in list(node): if sub.tag != 'intent-filter': continue for sub2 in sub.getchildren(): if not (sub2.tag == 'category' and sub2.attrib[attrName] == 'android.intent.category.LAUNCHER'): continue node.remove(sub) tree.write(manifest, encoding) return node.attrib[attrName] return 0 def remove_launcher_activity_by_name(manifest, name): """ 删除启动的activity """ for key in namespaces: ET.register_namespace(key, namespaces[key]) tree = ET.parse(manifest) root = tree.getroot() attrName = get_namespaces_format('android:name', namespaces) for node in root.findall('application/activity'): print('node ----> ' + node.attrib[attrName]) if node.attrib[attrName] != name: continue for sub in list(node): if sub.tag != 'intent-filter': continue for sub2 in sub.getchildren(): if not (sub2.tag == 'category' and sub2.attrib[attrName] == 'android.intent.category.LAUNCHER'): continue node.remove(sub) tree.write(manifest, encoding) return node.attrib[attrName] return 0 def get_screen_orientation(manifest): """ 获取启动activity的方向 """ for key in namespaces: ET.register_namespace(key, namespaces[key]) tree = ET.parse(manifest) root = tree.getroot() attrName = get_namespaces_format('android:name', namespaces) attrOrientation = get_namespaces_format('android:screenOrientation', namespaces) for node in root.findall('application/activity'): if len(list(node)) <= 0: continue for sub in list(node): if sub.tag != 'intent-filter': continue for sub2 in sub.getchildren(): if not (sub2.tag == 'category' and sub2.attrib[attrName] == 'android.intent.category.LAUNCHER'): continue return node.attrib[attrOrientation] return 0 def add_launcher_activity(manifest, screen_orientation, activity): """ 添加启动的activity """ for key in namespaces: ET.register_namespace(key, namespaces[key]) tree = ET.parse(manifest) root = tree.getroot() # activity ''' ''' activity = ET.Element('activity', {'android:name': activity, 'android:theme': '@android:style/Theme.Holo.Light.NoActionBar.Fullscreen', 'android:launchMode': 'singleTop', 'android:configChanges': 'orientation|screenSize|keyboardHidden', 'android:screenOrientation': screen_orientation}) intent = ET.Element('intent-filter') action = ET.Element('action', {'android:name': 'android.intent.action.MAIN'}) category = ET.Element('category', {'android:name': 'android.intent.category.LAUNCHER'}) intent.append(action) intent.append(category) activity.append(intent) application = root.find('application') application.insert(0, activity) tree.write(manifest, encoding) def change_app_name(manifest, string_res): """ 更改app名 """ # 移除主activity的label remove_launcher_attr(manifest, 'label') return change_application_attr(manifest, 'label', string_res) def get_launcher_activity(root): """ 获取启动的activity """ attrName = get_namespaces_format('android:name', namespaces) for node in root.findall('application/activity'): if sys.version_info < (3, 9): node_dict = node.getchildren() else: node_dict = list(node) if len(node_dict) <= 0: continue for sub in node_dict: if sys.version_info < (3, 9): sub_dict = sub.getchildren() else: sub_dict = list(sub) if sub.tag != 'intent-filter': continue for sub2 in sub_dict: if sub2.tag == 'category' and sub2.attrib[attrName] == 'android.intent.category.LAUNCHER': return node return None def get_launcher_activities(manifest): """ 获取启动的activity """ attrName = get_namespaces_format('android:name', namespaces) nodeList = [] tree = ET.parse(manifest) root = tree.getroot() for node in root.findall('application/activity'): if len(list(node)) <= 0: continue for sub in list(node): if sub.tag != 'intent-filter': continue for sub2 in sub.getchildren(): if sub2.tag == 'category' and sub2.attrib[attrName] == 'android.intent.category.LAUNCHER': nodeList.append(node) return nodeList def get_launcher_activity_name(manifest): """ 获取启动的activity """ for key in namespaces: ET.register_namespace(key, namespaces[key]) tree = ET.parse(manifest) root = tree.getroot() launcherActivity = get_launcher_activity(root) attrName = get_namespaces_format('android:name', namespaces) activityName = launcherActivity.attrib[attrName] return activityName def add_more_icon(manifest, icon, switch_icon): """ 添加多图标 """ for key in namespaces: ET.register_namespace(key, namespaces[key]) tree = ET.parse(manifest) root = tree.getroot() launcherActivity = get_launcher_activity(root) if launcherActivity is None: return 1 attrName = get_namespaces_format('android:name', namespaces) activityName = launcherActivity.attrib[attrName] activityAlias = ET.Element('activity-alias', {'android:name': 'com.jmhy.sdk.icon.normal', 'android:enabled': 'false', 'android:icon': icon, 'android:targetActivity': activityName}) activityAlias2 = ET.Element('activity-alias', {'android:name': 'com.jmhy.sdk.icon.switch', 'android:enabled': 'false', 'android:icon': switch_icon, 'android:targetActivity': activityName}) for item in launcherActivity.getchildren(): activityAlias.append(item) activityAlias2.append(item) application = root.find('application') application.append(activityAlias) application.append(activityAlias2) tree.write(manifest, encoding) return 0 def remove_launcher_attr(manifest, attr_type): """ 移除启动的activity的label """ for key in namespaces: ET.register_namespace(key, namespaces[key]) tree = ET.parse(manifest) root = tree.getroot() launcherActivity = get_launcher_activity(root) if launcherActivity is None: return 1 attrName = get_namespaces_format('android:%s' % attr_type, namespaces) if attrName in launcherActivity.attrib: del launcherActivity.attrib[attrName] tree.write(manifest, encoding) return 0 def change_launcher_attr(manifest, attr_type, attr_value): """ 更改启动的activity的属性 """ for key in namespaces: ET.register_namespace(key, namespaces[key]) tree = ET.parse(manifest) root = tree.getroot() launcherActivity = get_launcher_activity(root) if launcherActivity is None: return 1 attrName = get_namespaces_format('android:%s' % attr_type, namespaces) if attrName in launcherActivity.attrib: launcherActivity.attrib[attrName] = attr_value tree.write(manifest, encoding) return 0 def change_app_icon(manifest, icon_res): """ 更改app icon """ # 移除主activity的图标 remove_launcher_attr(manifest, 'icon') return change_application_attr(manifest, 'icon', icon_res) def change_application_attr(manifest, attr_type, attr_value): """ 更改Application的某个属性 """ for key in namespaces: ET.register_namespace(key, namespaces[key]) tree = ET.parse(manifest) root = tree.getroot() application = root.find('application') # Namespaces attrName = get_namespaces_format('android:%s' % attr_type, namespaces) application.attrib[attrName] = attr_value tree.write(manifest, encoding) return 0 def add_application_attr(manifest, attr_type, attr_value): """ 更改Application的某个属性 """ for key in namespaces: ET.register_namespace(key, namespaces[key]) tree = ET.parse(manifest) root = tree.getroot() application = root.find('application') # Namespaces attrName = get_namespaces_format('android:%s' % attr_type, namespaces) application.set(attrName, attr_value) tree.write(manifest, encoding) return 0 def get_application_attr(manifest, attr_type): """ 获取Application的某个属性 """ for key in namespaces: ET.register_namespace(key, namespaces[key]) tree = ET.parse(manifest) root = tree.getroot() application = root.find('application') attrName = get_namespaces_format('android:%s' % attr_type, namespaces) if attrName in application.attrib: return application.attrib[attrName] return None def get_namespaces_format(text, space): """ 格式化带namespaces的属性 """ for key in space: text = text.replace('%s:' % key, '{%s}' % space[key]) return text def merge_manifest_res(app_manifest, lib_manifest): """ 合并主文件 """ if not os.path.exists(lib_manifest): print('file "%s" not exists' % lib_manifest) return 1 if not os.path.exists(app_manifest): print('file "%s" not exists' % app_manifest) return 1 libInfo = get_lib_manifest_info(lib_manifest) appPermission = get_manifest_permission(app_manifest) diffPermission = merge_manifest_permission(appPermission, libInfo['permissionList']) merge_manifest(app_manifest, diffPermission, libInfo['activityList']) return 0 def get_lib_manifest_info(lib_manifest): """ 获取框架的主文件参数 """ for key in namespaces: ET.register_namespace(key, namespaces[key]) tree = ET.parse(lib_manifest) root = tree.getroot() permissionList = root.findall('permissions/*') activityList = root.findall('application/*') info = {'permissionList': permissionList, 'activityList': activityList} return info def get_manifest_permission(app_manifest): """ 获取主文件参数 """ for key in namespaces: ET.register_namespace(key, namespaces[key]) tree = ET.parse(app_manifest) root = tree.getroot() return root.findall('uses-permission') def merge_manifest_permission(app_permission, lib_permission): """ 合并主文件参数 """ newPermissionList = [] for permission in lib_permission: if contain(app_permission, permission): continue newPermissionList.append(permission) return newPermissionList def contain(node_list, item): """ 是否存在 """ attrName = get_namespaces_format('android:name', namespaces) for node in node_list: if node.attrib[attrName] == item.attrib[attrName]: return True return False def merge_manifest(app_manifest, permission_list, activity_list): """ 将权限和activity加入到主文件 """ for key in namespaces: ET.register_namespace(key, namespaces[key]) tree = ET.parse(app_manifest) root = tree.getroot() # 权限 for item in permission_list: root.insert(0, item) # activity application = root.find('application') # 删除不支持属性 attrName = get_namespaces_format('android:roundIcon', namespaces) if attrName in application.attrib: del application.attrib[attrName] # 删除不支持属性 attrName = get_namespaces_format('android:testOnly', namespaces) if attrName in application.attrib: del application.attrib[attrName] # 删除不支持属性 attrName = get_namespaces_format('android:debuggable', namespaces) if attrName in application.attrib: del application.attrib[attrName] # 覆盖 # 移除相同的标签 attrName = get_namespaces_format('android:name', namespaces) if sys.version_info < (3, 9): children_dict = application.getchildren() else: children_dict = list(application) for item in children_dict: if contain(activity_list, item): application.remove(item) for item in activity_list: application.insert(1, item) tree.write(app_manifest, encoding) def addMetaData(manifest, meta): """ 添加meta-data """ for key in namespaces: ET.register_namespace(key, namespaces[key]) tree = ET.parse(manifest) root = tree.getroot() application = root.find('application') for key in meta: val = meta[key] '''if type(val) == str and val.isdigit(): element = ET.Element('meta-data', {'android:name' : key, 'android:value' : '\\ ' + val}) else: element = ET.Element('meta-data', {'android:name' : key, 'android:value' : val})''' element = ET.Element('meta-data', {'android:name': key, 'android:value': val}) application.append(element) tree.write(manifest, encoding) def read_all_res(res_file, res_list): """ 读取资源文件 """ tree = ET.parse(res_file) root = tree.getroot() if sys.version_info < (3, 9): res_list += root.getchildren() else: res_list += list(root) return res_list def remove_id_from_public(pub_file, remove_list): """ 删除重复的资源 """ tree = ET.parse(pub_file) root = tree.getroot() same = False for node in root.getchildren(): if containPublic(node, remove_list): print('delete public node : type is %s, name is %s' % (node.attrib['type'], node.attrib['name'])) root.remove(node) same = True if same: tree.write(pub_file, encoding) def remove_same_res2(res_file, res_list, remove_list): """ 删除重复的资源 """ tree = ET.parse(res_file) root = tree.getroot() same = False for node in root.getchildren(): if containRes(node, res_list): # print('delete node : tag is %s, name is %s' % (node.tag, node.attrib['name'])) root.remove(node) remove_list.append(node) same = True if same: tree.write(res_file, encoding) return remove_list def remove_same_values_res(res_file, res_list): """ 删除重复的资源 """ tree = ET.parse(res_file) root = tree.getroot() same = False if sys.version_info < (3, 9): children_dict = root.getchildren() else: children_dict = list(root) for node in children_dict: if containRes(node, res_list): printlog('[delete node]: tag is %s, name is %s' % (node.tag, node.attrib['name'])) root.remove(node) same = True if same: tree.write(res_file, encoding) def containRes(node, res_list): """ 是否重复 """ for item in res_list: if item.tag == node.tag and item.attrib['name'] == node.attrib['name']: return True return False def containPublic(node, remove_list): """ 是否重复 """ for item in remove_list: if item.tag == node.attrib['type'] and item.attrib['name'] == node.attrib['name']: return True return False def remove_root_attr(manifest, attr_type): for key in namespaces: ET.register_namespace(key, namespaces[key]) tree = ET.parse(manifest) root = tree.getroot() attrName = get_namespaces_format('android:%s' % attr_type, namespaces) if attrName in root.attrib: del root.attrib[attrName] tree.write(manifest, encoding) return 0 def delete_activity_by_name(manifest, activity_name): """ 删除activity """ attrName = get_namespaces_format('android:name', namespaces) for key in namespaces: ET.register_namespace(key, namespaces[key]) targetTree = ET.parse(manifest) targetRoot = targetTree.getroot() appNode = targetRoot.find('application') activitys = appNode.findall('activity') for activity in activitys: if activity.attrib[attrName] == activity_name: print('delete ------------------> ' + activity_name) appNode.remove(activity) targetTree.write(manifest, encoding) break def get_activity_by_name(root, activity_name): """ 获取activity """ attrName = get_namespaces_format('android:name', namespaces) for node in root.findall('application/activity'): print('activity name = ' + node.attrib[attrName]) if node.attrib[attrName] == activity_name: return node return None def formatXml(manifest): # elemnt为传进来的Elment类,参数indent用于缩进,newline用于换行 indent = '\t' newline = '\n' tree = ET.parse(manifest) root = tree.getroot() # 得到根元素,Element类 prettyXml(root, indent, newline, level=0) # 对子元素进行递归操作 ET.dump(root) return 0 def prettyXml(element, indent, newline, level=0): # elemnt为传进来的Elment类,参数indent用于缩进,newline用于换行 if element: # 判断element是否有子元素 if element.text == None or element.text.isspace(): # 如果element的text没有内容 element.text = newline + indent * (level + 1) else: element.text = newline + indent * (level + 1) + element.text.strip() + newline + indent * (level + 1) # else: # 此处两行如果把注释去掉,Element的text也会另起一行 # element.text = newline + indent * (level + 1) + element.text.strip() + newline + indent * level temp = list(element) # 将elemnt转成list for subelement in temp: if temp.index(subelement) < (len(temp) - 1): # 如果不是list的最后一个元素,说明下一个行是同级别元素的起始,缩进应一致 subelement.tail = newline + indent * (level + 1) else: # 如果是list的最后一个元素, 说明下一行是母元素的结束,缩进应该少一个 subelement.tail = newline + indent * level prettyXml(subelement, indent, newline, level=level + 1) # 对子元素进行递归操作