Skip to content

Instantly share code, notes, and snippets.

@AesSedai101
Last active October 19, 2016 09:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AesSedai101/4bf30a1ee2306ee3e195009363579846 to your computer and use it in GitHub Desktop.
Save AesSedai101/4bf30a1ee2306ee3e195009363579846 to your computer and use it in GitHub Desktop.
Find unused Activity classes in an IntelliJ Android project

Find unused Android activities in IntelliJ IDEA / Android Studio

This script will look for unused android.app.Activity classes in an IntelliJ project. To use, open the IDE scripting console, paste in this script and then press Ctrl+A and then Ctrl+Enter.

This is a work in progress

  • The script does not yet cater for all usage types. Unknown types are tracked in the unknowns list and is printed just before potentially unused activities.
  • The script assumes that utlity intent creation methods is named getStartIntent
import com.intellij.openapi.module.*
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManager
import com.intellij.psi.*
import com.intellij.psi.search.SearchScope
import com.intellij.psi.search.searches.*
import com.intellij.util.Query
import static com.intellij.openapi.actionSystem.LangDataKeys.*
import static com.intellij.psi.codeStyle.NameUtil.*
// get the project
Project project = ProjectManager.getInstance().getOpenProjects()[0] // If your project isn't the first open one, change this
IDE.print(project)
// get the correct module
Module module = ModuleManager.getInstance(project).getModules()[0] // If you don't want to use the first module, change this
IDE.print(module)
// search for 'android.app.Activity'
SearchScope searchScope = module.getModuleRuntimeScope(false)
Query<PsiClass> result = AllClassesSearch.search(searchScope, project)
def androidActivity
result.each { r ->
if (r.getQualifiedName() == 'android.app.Activity') {
androidActivity = r
}
}
// search for all classes extending 'Activity'
SearchScope moduleScope = module.getModuleScope();
Query<PsiClass> inheritingClasses = ClassInheritorsSearch.search(androidActivity, moduleScope, true);
IDE.print("Found " + inheritingClasses.size() + " inheriting from " + androidActivity.getQualifiedName())
// and process them
def potentiallyUnused = []
def unknowns = []
inheritingClasses.each { clazz ->
Query<PsiReference> refs = ReferencesSearch.search(clazz, moduleScope)
IDE.print("Examining references to " + clazz + " - " + refs.size() + " references found")
int thisCount = 0;
int importCount = 0;
int startIntentCount = 0;
int staticCount = 0;
int classCount = 0;
int localVariableCount = 0;
int parameterCount = 0;
int genericUsages = 0;
int otherReferences = 0;
int xmlCount = 0;
int nestedAccess = 0;
int extendCount = 0;
int castCount = 0;
int instanceOfCount = 0;
int docCount = 0;
int intentCount = 0; ;
refs.each { ref ->
PsiElement refElement = ref.getElement();
PsiElement parent = refElement == null ? null : refElement.getParent();
if (parent instanceof PsiThisExpression) {
thisCount++
} else if (parent instanceof PsiImportStatement) {
importCount++
} else if (parent instanceof PsiReferenceExpression) {
if (parent.getQualifiedName().contains("getStartIntent")) {
startIntentCount++
} else {
staticCount++
}
} else if (parent instanceof PsiTypeElement) {
PsiElement oneUp = parent.getParent()
if (oneUp instanceof PsiClassObjectAccessExpression) {
if (oneUp.getParent() instanceof PsiExpressionList && oneUp.getParent().getExpressions().size() ==
2 &&
(oneUp.getParent().getExpressions()[0].toString().contains("this") || oneUp.getParent().
getExpressions()[0].
toString().
contains("Context") ||
oneUp.getParent().getExpressions()[0].toString().contains("context") ||
oneUp.getParent().getExpressions()[0].toString().contains("Activity")) &&
oneUp.getParent().getExpressions()[1].toString().contains("class")) {
intentCount++
} else {
classCount++
}
} else if (oneUp instanceof PsiLocalVariable) {
localVariableCount++;
} else if (oneUp instanceof PsiParameter) {
parameterCount++;
} else if (oneUp instanceof PsiReferenceParameterList) {
genericUsages++;
} else if (oneUp instanceof PsiTypeCastExpression) {
castCount++
} else if (oneUp instanceof PsiInstanceOfExpression) {
instanceOfCount++
} else {
otherReferences++
unknowns.push("[" + clazz.toString() + "] " + oneUp.toString() + " -(" + parent.toString() + ")")
IDE.print("Unknown " + oneUp.toString() + " from " + parent.toString())
}
} else if (parent instanceof XmlAttribute) {
xmlCount++;
} else if (parent instanceof PsiJavaCodeReferenceElement) {
nestedAccess++
} else if (parent instanceof PsiReferenceList && parent.getRole() == PsiReferenceList.Role.EXTENDS_LIST) {
extendCount++;
} else if (parent instanceof LazyParseablePsiElement) {
docCount++
} else {
otherReferences++
unknowns.push("[" + clazz.toString() + "] " + parent.toString() + " - " + parent.getClass())
IDE.print("Unknown " + parent + " - " + parent.getClass())
}
}
IDE.print("this refs: " + thisCount)
IDE.print("import refs: " + importCount)
IDE.print("getStartIntent refs: " + startIntentCount)
IDE.print("static refs: " + staticCount)
IDE.print(".class refs: " + classCount)
IDE.print("local variable refs: " + localVariableCount)
IDE.print("parameter refs: " + parameterCount)
IDE.print("<> refs: " + genericUsages)
IDE.print("xml refs: " + xmlCount)
IDE.print("nested refs: " + nestedAccess)
IDE.print("extend refs: " + extendCount)
IDE.print("cast refs: " + castCount)
IDE.print("instanceof refs: " + instanceOfCount)
IDE.print("doc refs: " + docCount)
IDE.print("intent refs: " + intentCount)
IDE.print("other refs: " + otherReferences)
if (extendCount == 0 && startIntentCount == 0 && intentCount == 0) {
// no extensions, no utility method and no calls to new Intent() implies no startActivity()
potentiallyUnused.push(clazz.getQualifiedName() + " [" + otherReferences + " unknown refs]")
}
}
IDE.print("")
IDE.print("Done!")
IDE.print("Unknown elements: " + unknowns.unique().size())
unknowns.unique().forEach { u -> IDE.print(u) }
IDE.print("")
IDE.print("Potential unused Activity classes: " + potentiallyUnused.size())
potentiallyUnused.each { clazz -> IDE.print(clazz)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment