1#!/usr/bin/env python3 2# Copyright (C) 2021 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16import os 17import re 18import sys 19try: 20 import lxml.etree as etree 21except ImportError: 22 print("Please install 'lxml' python package and retry. \n" 23 + "E.g., you can use 'sudo apt-get install python3-lxml'.") 24 sys.exit(1) 25 26class ResourceLocation: 27 def __init__(self, file, line=None): 28 self.file = file 29 self.line = line 30 def __str__(self): 31 if self.line is not None: 32 return self.file + ':' + str(self.line) 33 else: 34 return self.file 35 36class Resource: 37 def __init__(self, name, type, location=None): 38 self.name = name 39 self.type = type 40 self.locations = [] 41 if location is not None: 42 self.locations.append(location) 43 def __eq__(self, other): 44 if isinstance(other, _Grab): 45 return other == self 46 return self.name == other.name and self.type == other.type 47 def __ne__(self, other): 48 if isinstance(other, _Grab): 49 return other != self 50 return self.name != other.name or self.type != other.type 51 def __hash__(self): 52 return hash((self.name, self.type)) 53 def __str__(self): 54 result = '' 55 for location in self.locations: 56 result += str(location) + ': ' 57 result += '<'+self.type+' name="'+self.name+'"' 58 return result + '>' 59 def __repr__(self): 60 return str(self) 61 62def get_all_resources(resDir, excluded_resource_files=[]): 63 excluded_resource_files = [os.path.abspath(file) for file in excluded_resource_files] 64 allResDirs = [f for f in os.listdir(resDir) if os.path.isdir(os.path.join(resDir, f))] 65 valuesDirs = [f for f in allResDirs if f.startswith('values')] 66 fileDirs = [f for f in allResDirs if not f.startswith('values')] 67 resources = set() 68 # Get the filenames of the all the files in all the fileDirs 69 for dir in fileDirs: 70 type = dir.split('-')[0] 71 for file in os.listdir(os.path.join(resDir, dir)): 72 filePath = os.path.abspath(os.path.join(resDir, dir, file)) 73 if file.endswith('.xml') and filePath not in excluded_resource_files: 74 add_resource_to_set(resources, 75 Resource(file[:-4], type, 76 ResourceLocation(os.path.join(resDir, dir, file)))) 77 if dir.startswith("layout"): 78 for resource in get_ids_from_layout_file(os.path.join(resDir, dir, file)): 79 add_resource_to_set(resources, resource) 80 for dir in valuesDirs: 81 for file in os.listdir(os.path.join(resDir, dir)): 82 filePath = os.path.abspath(os.path.join(resDir, dir, file)) 83 if file.endswith('.xml') and filePath not in excluded_resource_files: 84 for resource in get_resources_from_single_file(os.path.join(resDir, dir, file), 85 dir != "values"): 86 add_resource_to_set(resources, resource) 87 return resources 88 89def get_ids_from_layout_file(filename): 90 result = set() 91 with open(filename, 'r') as file: 92 r = re.compile("@\+id/([a-zA-Z0-9_]+)") 93 for i in r.findall(file.read()): 94 add_resource_to_set(result, Resource(i, 'id', ResourceLocation(filename))) 95 return result 96 97def get_resources_from_single_file(filename, ignore_strings=False): 98 doc = etree.parse(filename) 99 root = doc.getroot() 100 result = set() 101 for resource in root: 102 if resource.tag is etree.Comment: 103 continue 104 if resource.tag == 'declare-styleable': 105 for attr in resource: 106 resName = attr.get('name') 107 # Skip resources beginning with 'android:' as they are part of the framework 108 # resources. This script finds only the app's resources. 109 if resName is None or resName.startswith('android:'): 110 continue 111 resType = "attr" 112 add_resource_to_set(result, Resource(resName, resType, ResourceLocation(filename, attr.sourceline))) 113 continue 114 resName = resource.get('name') 115 resType = resource.tag 116 if resType == "string-array" or resType == "integer-array": 117 resType = "array" 118 if resource.tag == 'item' or resource.tag == 'public': 119 resType = resource.get('type') 120 if (resType == 'string' or resType == 'plurals') and ignore_strings: 121 continue 122 if resType == 'overlayable': 123 for policy in resource: 124 for overlayable in policy: 125 resName = overlayable.get('name') 126 resType = overlayable.get('type') 127 add_resource_to_set(result, Resource(resName, resType, 128 ResourceLocation(filename, resource.sourceline))) 129 else: 130 add_resource_to_set(result, Resource(resName, resType, 131 ResourceLocation(filename, resource.sourceline))) 132 return result 133 134# Used to get objects out of sets 135class _Grab: 136 def __init__(self, value): 137 self.search_value = value 138 def __hash__(self): 139 return hash(self.search_value) 140 def __eq__(self, other): 141 if self.search_value == other: 142 self.actual_value = other 143 return True 144 return False 145 146def add_resource_to_set(resourceset, resource): 147 if (resource.name == None): 148 return 149 grabber = _Grab(resource) 150 if grabber in resourceset: 151 grabber.actual_value.locations.extend(resource.locations) 152 else: 153 resourceset.update([resource]) 154 155def merge_resources(set1, set2): 156 for resource in set2: 157 add_resource_to_set(set1, resource) 158 159def get_androidx_resources(): 160 # source: https://android.googlesource.com/platform/frameworks/opt/sherpa/+/studio-3.0/constraintlayout/src/main/res/values/attrs.xml 161 resources = set() 162 add_resource_to_set(resources, Resource('layout_optimizationLevel', 'attr')) 163 add_resource_to_set(resources, Resource('constraintSet', 'attr')) 164 add_resource_to_set(resources, Resource('barrierDirection', 'attr')) 165 add_resource_to_set(resources, Resource('constraint_referenced_ids', 'attr')) 166 add_resource_to_set(resources, Resource('chainUseRtl', 'attr')) 167 add_resource_to_set(resources, Resource('title', 'attr')) 168 add_resource_to_set(resources, Resource('layout_constraintGuide_begin', 'attr')) 169 add_resource_to_set(resources, Resource('layout_constraintGuide_end', 'attr')) 170 add_resource_to_set(resources, Resource('layout_constraintGuide_percent', 'attr')) 171 add_resource_to_set(resources, Resource('layout_constraintLeft_toLeftOf', 'attr')) 172 add_resource_to_set(resources, Resource('layout_constraintLeft_toRightOf', 'attr')) 173 add_resource_to_set(resources, Resource('layout_constraintRight_toLeftOf', 'attr')) 174 add_resource_to_set(resources, Resource('layout_constraintRight_toRightOf', 'attr')) 175 add_resource_to_set(resources, Resource('layout_constraintTop_toTopOf', 'attr')) 176 add_resource_to_set(resources, Resource('layout_constraintTop_toBottomOf', 'attr')) 177 add_resource_to_set(resources, Resource('layout_constraintBottom_toTopOf', 'attr')) 178 add_resource_to_set(resources, Resource('layout_constraintBottom_toBottomOf', 'attr')) 179 add_resource_to_set(resources, Resource('layout_constraintBaseline_toBaselineOf', 'attr')) 180 add_resource_to_set(resources, Resource('layout_constraintStart_toEndOf', 'attr')) 181 add_resource_to_set(resources, Resource('layout_constraintStart_toStartOf', 'attr')) 182 add_resource_to_set(resources, Resource('layout_constraintEnd_toStartOf', 'attr')) 183 add_resource_to_set(resources, Resource('layout_constraintEnd_toEndOf', 'attr')) 184 add_resource_to_set(resources, Resource('layout_goneMarginLeft', 'attr')) 185 add_resource_to_set(resources, Resource('layout_goneMarginTop', 'attr')) 186 add_resource_to_set(resources, Resource('layout_goneMarginRight', 'attr')) 187 add_resource_to_set(resources, Resource('layout_goneMarginBottom', 'attr')) 188 add_resource_to_set(resources, Resource('layout_goneMarginStart', 'attr')) 189 add_resource_to_set(resources, Resource('layout_goneMarginEnd', 'attr')) 190 add_resource_to_set(resources, Resource('layout_constraintHorizontal_bias', 'attr')) 191 add_resource_to_set(resources, Resource('layout_constraintVertical_bias', 'attr')) 192 add_resource_to_set(resources, Resource('layout_constraintWidth_default', 'attr')) 193 add_resource_to_set(resources, Resource('layout_constraintHeight_default', 'attr')) 194 add_resource_to_set(resources, Resource('layout_constraintWidth_min', 'attr')) 195 add_resource_to_set(resources, Resource('layout_constraintWidth_max', 'attr')) 196 add_resource_to_set(resources, Resource('layout_constraintWidth_percent', 'attr')) 197 add_resource_to_set(resources, Resource('layout_constraintHeight_min', 'attr')) 198 add_resource_to_set(resources, Resource('layout_constraintHeight_max', 'attr')) 199 add_resource_to_set(resources, Resource('layout_constraintHeight_percent', 'attr')) 200 add_resource_to_set(resources, Resource('layout_constraintLeft_creator', 'attr')) 201 add_resource_to_set(resources, Resource('layout_constraintTop_creator', 'attr')) 202 add_resource_to_set(resources, Resource('layout_constraintRight_creator', 'attr')) 203 add_resource_to_set(resources, Resource('layout_constraintBottom_creator', 'attr')) 204 add_resource_to_set(resources, Resource('layout_constraintBaseline_creator', 'attr')) 205 add_resource_to_set(resources, Resource('layout_constraintDimensionRatio', 'attr')) 206 add_resource_to_set(resources, Resource('layout_constraintHorizontal_weight', 'attr')) 207 add_resource_to_set(resources, Resource('layout_constraintVertical_weight', 'attr')) 208 add_resource_to_set(resources, Resource('layout_constraintHorizontal_chainStyle', 'attr')) 209 add_resource_to_set(resources, Resource('layout_constraintVertical_chainStyle', 'attr')) 210 add_resource_to_set(resources, Resource('layout_editor_absoluteX', 'attr')) 211 add_resource_to_set(resources, Resource('layout_editor_absoluteY', 'attr')) 212 return resources 213