Skip to content

Instantly share code, notes, and snippets.

@LeZuse
Created December 1, 2011 04:25
Show Gist options
  • Save LeZuse/1413609 to your computer and use it in GitHub Desktop.
Save LeZuse/1413609 to your computer and use it in GitHub Desktop.
Android resource tracker
#!/usr/bin/env python
'''
Feel free to modify and/or enhance this tool. Also feel free to fix bugs you found.
I would be happy if you could share your modified version on the blog, where this tool
was posted.
Blog-URL: http://www.droidnova.com/android-resource-tracker,723.html
Project Page: http://code.google.com/p/androidresourcetracker
This tool is from a developer for developers, so be like a good colleague and share your
improvements to this tool with the others.
EXPLIZIT: No license... I trust your good will... if you get rich, share with me :)
Created on 24.09.2010
@version: 1.1.0
@author: Martin Breuer (aka WarrenFaith)
'''
import sys
import os
import re
import glob
helpInformation = '''This is a tool that help you to find out, if a resource is used.
Parameter:
-h : shows this help
-i : root directory of your android project
-v : show all matches
--unused-only : shows only unused elements - does not work with --used-only
--used-only : shows on used elements - does not work with --unused-only
--types string:id:drawable : show only the specified types'''
rootProjectDir = None
elementPath = ""
verbose = False
usedOnly = False
unusedOnly = False
resourceArray = {}
sourceArray = {}
xmlFiles = []
resultArray = {}
inXml = 'inXml'
inSrc = 'inSrc'
unsure = 'unsure'
fetchOnly = []
## recursive walk through all directories ##
def findR(subdirectory=''):
filename="R.java"
if subdirectory:
path = subdirectory
else:
path = os.getcwd()
for root, dirs, names in os.walk(path):
if filename in names:
return root
raise 'File not found'
def findXmlFiles(path):
for dirs in os.listdir(path):
if os.path.isdir(os.path.join(path, dirs)):
if dirs != '.svn':
findXmlFiles(os.path.join(path, dirs))
else:
if dirs.endswith(".xml"):
newPath = os.path.basename(path)
xmlFiles.append(os.path.join(newPath, dirs))
return None
def collectSources(path):
for dirs in os.listdir(path):
if os.path.isdir(os.path.join(path, dirs)):
if dirs.startswith('.'):
continue
if not dirs.endswith('.java'):
sourceArray[os.path.join(path, dirs)] = []
collectSources(os.path.join(path, dirs))
return None
def parseR(pathToR):
patternResource = re.compile("\W(.+)\W(.+)=0x")
patternClass = re.compile("class\W(.+)\W{")
file = open(pathToR, "r")
currentClass = "ERROR";
for line in file:
resourceHit = patternResource.search(line)
classHit = patternClass.search(line)
if classHit != None and classHit.group(1) != "R":
currentClass = classHit.group(1)
resourceArray[currentClass] = []
if resourceHit != None:
resourceArray[currentClass] += [resourceHit.group(2)]
return None
def findPossibleMatchInSrc(type, item, searchString):
pattern = re.compile(searchString + item)
for dir in sourceArray:
for files in os.listdir(dir):
if files != ".svn" and files.endswith(".java"):
file = open(os.path.join(dir, files), "r")
lineNumber = 1
for line in file:
if pattern.search(line) != None:
if verbose:
print(files + " - Line: " + str(lineNumber) + " :: " + line.strip())
resultArray[type][item][unsure] = True
lineNumber += 1
return None
def findOccurenceInSrc(type, item, searchString):
pattern = re.compile(searchString + item)
for dir in sourceArray:
for files in os.listdir(dir):
if files != ".svn" and files.endswith(".java"):
file = open(os.path.join(dir, files), "r")
lineNumber = 1
for line in file:
if pattern.search(line) != None:
if verbose:
print(files + " - Line: " + str(lineNumber) + " :: " + line.strip())
resultArray[type][item][inSrc] = True
lineNumber += 1
return None
def findOccurenceInXml(type, item, plainTag, searchString):
if searchString == "@style/":
pattern = re.compile(searchString+"("+item+"|"+item.replace("_",".")+")")
else:
pattern = re.compile(searchString + item)
for files in xmlFiles:
file = open(os.path.join(elementPath, files), "r")
lineNumber = 1
for line in file:
if pattern.search(line) != None:
if verbose:
print(files + " - Line: " + str(lineNumber) + " :: " + line.strip())
resultArray[type][item][inXml] = True
lineNumber += 1
return None
def getLibraryProjectFolders(rootProjectDir):
# read default.properties
file = open(os.path.join(rootProjectDir, "default.properties"), "r")
lineNumber = 1
pattern = re.compile("android.library.reference.")
result = []
for line in file:
if pattern.search(line) != None:
result.append(line[line.rfind("=")+1:len(line)-1])
return result
### Main programm ###
if len(sys.argv) == 1:
print(helpInformation)
sys.exit()
for index,arg in enumerate(sys.argv):
if arg == "-h":
print(helpInformation)
sys.exit()
if arg == "-i":
if len(sys.argv) <= index + 1:
print('''Given path is missing...''')
sys.exit()
rootProjectDir = sys.argv[index + 1]
elementPath = os.path.join(rootProjectDir, "res")
if arg == "-v":
verbose = True
if arg == "--used-only":
usedOnly = True
if arg == "--unused-only":
unusedOnly = True;
if arg == "--types":
if len(sys.argv) <= index + 1:
print("Given element typ or types missing...")
sys.exit()
fetchOnly = sys.argv[index + 1].split(":")
if unusedOnly and usedOnly:
print('''--used-only and --unused-only can't be used together.
Remove one parameter...''')
sys.exit()
# get referenced library projects
#
print("getting library project path...")
librariesPath = getLibraryProjectFolders(rootProjectDir)
print("Processing, fetching, working on library projects...")
for libraryPath in librariesPath:
absoluteLibraryPath = os.path.join(rootProjectDir, libraryPath)
collectSources(os.path.join(absoluteLibraryPath, "src"))
findXmlFiles(os.path.join(absoluteLibraryPath, "res"))
xmlFiles.append(os.path.join(absoluteLibraryPath, "AndroidManifest.xml"))
os.chdir(absoluteLibraryPath)
print("library path being parsed: " + absoluteLibraryPath)
for srcDirs in glob.glob('gen'):
rclass = os.path.join(findR(os.path.join(absoluteLibraryPath, srcDirs)), "R.java")
parseR(rclass)
for entry in resourceArray:
resultArray[entry] = {}
for item in resourceArray[entry]:
resultArray[entry][item] = {}
resultArray[entry][item][inSrc] = False
resultArray[entry][item][inXml] = False
resultArray[entry][item][unsure] = False
findOccurenceInSrc(entry, item, entry + ".")
findOccurenceInXml(entry, item, entry + "." + item, "@" + entry + "/", absoluteLibraryPath + "res/")
findPossibleMatchInSrc(entry, item, 'getIdentifier\(\s*?\"')
print("Processing, fetching, working on main project...")
# collect all src files
collectSources(os.path.join(rootProjectDir, "src"))
# find all xml files
findXmlFiles(os.path.join(rootProjectDir, "res"))
#add manifest
xmlFiles.append(os.path.join(rootProjectDir, "AndroidManifest.xml"))
# read the R class
os.chdir(rootProjectDir)
for srcDirs in glob.glob('gen'):
rclass = os.path.join(findR(os.path.join(rootProjectDir, srcDirs)), "R.java")
parseR(rclass)
for entry in resourceArray:
resultArray[entry] = {}
for item in resourceArray[entry]:
resultArray[entry][item] = {}
resultArray[entry][item][inSrc] = False
resultArray[entry][item][inXml] = False
resultArray[entry][item][unsure] = False
findOccurenceInSrc(entry, item, entry + ".")
findOccurenceInXml(entry, item, entry + "." + item, "@" + entry + "/")
findPossibleMatchInSrc(entry, item, 'getIdentifier\(\s*?\"')
for type in resultArray:
if type in fetchOnly or len(fetchOnly) is 0:
for element,matches in resultArray[type].items():
if matches[inSrc] is False and matches[inXml] is False and matches[unsure] is False:
if usedOnly == False:
print("Unused: " + type + "." + element)
elif matches[inSrc] is True and matches[inXml] is True and matches[unsure] is True:
if unusedOnly == False:
print("Used in src and xml: " + type + "." + element)
elif matches[inSrc] is True or matches[unsure] is True and matches[inXml] is False:
if unusedOnly == False:
print("Used in src: " + type + "." + element)
elif matches[inSrc] is False and matches[unsure] is False and matches[inXml] is True:
if unusedOnly == False:
print("Used in xml: " + type + "." + element)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment