* implemented feature: 'go to main activity' * fix code formatting (added braces to if statements) * search main activity by original class name, handle exceptions --------- Co-authored-by: Skylot <skylot@gmail.com>
This commit is contained in:
@@ -79,6 +79,7 @@ import ch.qos.logback.classic.Level;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.api.JavaClass;
|
||||
import jadx.api.JavaNode;
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.api.plugins.events.IJadxEvents;
|
||||
@@ -133,6 +134,7 @@ import jadx.gui.ui.treenodes.SummaryNode;
|
||||
import jadx.gui.update.JadxUpdate;
|
||||
import jadx.gui.update.JadxUpdate.IUpdateCallback;
|
||||
import jadx.gui.update.data.Release;
|
||||
import jadx.gui.utils.AndroidManifestParser;
|
||||
import jadx.gui.utils.CacheObject;
|
||||
import jadx.gui.utils.FontUtils;
|
||||
import jadx.gui.utils.ILoadListener;
|
||||
@@ -167,6 +169,7 @@ public class MainWindow extends JFrame {
|
||||
private static final ImageIcon ICON_SEARCH = UiUtils.openSvgIcon("ui/find");
|
||||
private static final ImageIcon ICON_FIND = UiUtils.openSvgIcon("ui/ejbFinderMethod");
|
||||
private static final ImageIcon ICON_COMMENT_SEARCH = UiUtils.openSvgIcon("ui/usagesFinder");
|
||||
private static final ImageIcon ICON_MAIN_ACTIVITY = UiUtils.openSvgIcon("ui/home");
|
||||
private static final ImageIcon ICON_BACK = UiUtils.openSvgIcon("ui/left");
|
||||
private static final ImageIcon ICON_FORWARD = UiUtils.openSvgIcon("ui/right");
|
||||
private static final ImageIcon ICON_QUARK = UiUtils.openSvgIcon("ui/quark");
|
||||
@@ -1027,6 +1030,33 @@ public class MainWindow extends JFrame {
|
||||
commentSearchAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_SEMICOLON,
|
||||
UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK));
|
||||
|
||||
Action gotoMainActivityAction = new AbstractAction(NLS.str("menu.goto_main_activity"), ICON_MAIN_ACTIVITY) {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent ev) {
|
||||
AndroidManifestParser parser = new AndroidManifestParser(getWrapper().getResources());
|
||||
if (!parser.isResourceFound()) {
|
||||
JOptionPane.showMessageDialog(MainWindow.this,
|
||||
NLS.str("error_dialog.not_found", "AndroidManifest.xml"),
|
||||
NLS.str("error_dialog.title"),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
JavaClass mainActivityClass = parser.getMainActivity(getWrapper());
|
||||
tabbedPane.codeJump(getCacheObject().getNodeCache().makeFrom(mainActivityClass));
|
||||
} catch (Exception e) {
|
||||
LOG.error("Main activity not found", e);
|
||||
JOptionPane.showMessageDialog(MainWindow.this,
|
||||
NLS.str("error_dialog.not_found", "Main Activity"),
|
||||
NLS.str("error_dialog.title"),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
};
|
||||
gotoMainActivityAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("menu.goto_main_activity"));
|
||||
gotoMainActivityAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_M,
|
||||
UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK));
|
||||
|
||||
ActionHandler decompileAllAction = new ActionHandler(ev -> requestFullDecompilation());
|
||||
decompileAllAction.setNameAndDesc(NLS.str("menu.decompile_all"));
|
||||
decompileAllAction.setIcon(ICON_DECOMPILE_ALL);
|
||||
@@ -1135,6 +1165,7 @@ public class MainWindow extends JFrame {
|
||||
nav.add(textSearchAction);
|
||||
nav.add(clsSearchAction);
|
||||
nav.add(commentSearchAction);
|
||||
nav.add(gotoMainActivityAction);
|
||||
nav.addSeparator();
|
||||
nav.add(backAction);
|
||||
nav.add(forwardAction);
|
||||
@@ -1199,6 +1230,7 @@ public class MainWindow extends JFrame {
|
||||
toolbar.add(textSearchAction);
|
||||
toolbar.add(clsSearchAction);
|
||||
toolbar.add(commentSearchAction);
|
||||
toolbar.add(gotoMainActivityAction);
|
||||
toolbar.addSeparator();
|
||||
toolbar.add(backAction);
|
||||
toolbar.add(forwardAction);
|
||||
@@ -1220,6 +1252,7 @@ public class MainWindow extends JFrame {
|
||||
textSearchAction.setEnabled(loaded);
|
||||
clsSearchAction.setEnabled(loaded);
|
||||
commentSearchAction.setEnabled(loaded);
|
||||
gotoMainActivityAction.setEnabled(loaded);
|
||||
backAction.setEnabled(loaded);
|
||||
forwardAction.setEnabled(loaded);
|
||||
syncAction.setEnabled(loaded);
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
package jadx.gui.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import io.reactivex.annotations.Nullable;
|
||||
|
||||
import jadx.api.JavaClass;
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.api.ResourceType;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.xmlgen.XmlSecurity;
|
||||
import jadx.gui.JadxWrapper;
|
||||
|
||||
public class AndroidManifestParser {
|
||||
private final Document mXmlDocument;
|
||||
|
||||
public AndroidManifestParser(List<ResourceFile> resourceFiles) {
|
||||
mXmlDocument = parseAndroidManifest(getAndroidManifest(resourceFiles));
|
||||
}
|
||||
|
||||
public boolean isResourceFound() {
|
||||
return mXmlDocument != null;
|
||||
}
|
||||
|
||||
public JavaClass getMainActivity(JadxWrapper decompiler) {
|
||||
final String mainActivityName = getMainActivityName();
|
||||
if (mainActivityName == null) {
|
||||
throw new JadxRuntimeException("Failed to get main activity name from manifest");
|
||||
}
|
||||
JavaClass javaClass = decompiler.searchJavaClassByOrigClassName(mainActivityName);
|
||||
if (javaClass == null) {
|
||||
throw new JadxRuntimeException("Failed to find main activity class: " + mainActivityName);
|
||||
}
|
||||
return javaClass;
|
||||
}
|
||||
|
||||
private String getMainActivityName() {
|
||||
String mainActivityName = getMainActivityNameThroughActivityTag();
|
||||
if (mainActivityName == null) {
|
||||
mainActivityName = getMainActivityNameThroughActivityAliasTag();
|
||||
}
|
||||
return mainActivityName;
|
||||
}
|
||||
|
||||
private String getMainActivityNameThroughActivityAliasTag() {
|
||||
NodeList activityAliasNodes = mXmlDocument.getElementsByTagName("activity-alias");
|
||||
for (int i = 0; i < activityAliasNodes.getLength(); i++) {
|
||||
Element activityElement = (Element) activityAliasNodes.item(i);
|
||||
if (isMainActivityElement(activityElement)) {
|
||||
return activityElement.getAttribute("android:targetActivity");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getMainActivityNameThroughActivityTag() {
|
||||
NodeList activityNodes = mXmlDocument.getElementsByTagName("activity");
|
||||
for (int i = 0; i < activityNodes.getLength(); i++) {
|
||||
Element activityElement = (Element) activityNodes.item(i);
|
||||
if (isMainActivityElement(activityElement)) {
|
||||
return activityElement.getAttribute("android:name");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isMainActivityElement(Element element) {
|
||||
NodeList intentFilterNodes = element.getElementsByTagName("intent-filter");
|
||||
for (int j = 0; j < intentFilterNodes.getLength(); j++) {
|
||||
Element intentFilterElement = (Element) intentFilterNodes.item(j);
|
||||
NodeList actionNodes = intentFilterElement.getElementsByTagName("action");
|
||||
NodeList categoryNodes = intentFilterElement.getElementsByTagName("category");
|
||||
|
||||
boolean isMainAction = false;
|
||||
boolean isLauncherCategory = false;
|
||||
|
||||
for (int k = 0; k < actionNodes.getLength(); k++) {
|
||||
Element actionElement = (Element) actionNodes.item(k);
|
||||
String actionName = actionElement.getAttribute("android:name");
|
||||
if ("android.intent.action.MAIN".equals(actionName)) {
|
||||
isMainAction = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 0; k < categoryNodes.getLength(); k++) {
|
||||
Element categoryElement = (Element) categoryNodes.item(k);
|
||||
String categoryName = categoryElement.getAttribute("android:name");
|
||||
if ("android.intent.category.LAUNCHER".equals(categoryName)) {
|
||||
isLauncherCategory = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isMainAction && isLauncherCategory) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ResourceFile getAndroidManifest(List<ResourceFile> resources) {
|
||||
// TODO: taken from ExportGradleTask.java: also use this function there ?
|
||||
return resources.stream()
|
||||
.filter(resourceFile -> resourceFile.getType() == ResourceType.MANIFEST)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public static Document parseAndroidManifest(ResourceFile androidManifest) {
|
||||
if (androidManifest == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: taken from ExportGradleProject.java: also use this function there ?
|
||||
Document androidManifestDocument;
|
||||
try {
|
||||
String xmlContent = androidManifest.loadContent().getText().getCodeStr();
|
||||
DocumentBuilder builder = XmlSecurity.getSecureDbf().newDocumentBuilder();
|
||||
androidManifestDocument = builder.parse(new InputSource(new StringReader(xmlContent)));
|
||||
androidManifestDocument.getDocumentElement().normalize();
|
||||
} catch (ParserConfigurationException | SAXException | IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
return androidManifestDocument;
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ menu.navigation=Navigation
|
||||
menu.text_search=Textsuche
|
||||
menu.class_search=Klassen-Suche
|
||||
menu.comment_search=Kommentar suchen
|
||||
#menu.goto_main_activity=
|
||||
menu.tools=Tools
|
||||
#menu.plugins=Plugins
|
||||
#menu.decompile_all=Decompile all classes
|
||||
@@ -61,6 +62,7 @@ progress.decompile=Dekompilieren
|
||||
progress.canceling=Breche ab
|
||||
|
||||
error_dialog.title=Fehler
|
||||
#error_dialog.not_found=
|
||||
|
||||
search.previous=Zurück
|
||||
search.next=Weiter
|
||||
|
||||
@@ -14,6 +14,7 @@ menu.navigation=Navigation
|
||||
menu.text_search=Text search
|
||||
menu.class_search=Class search
|
||||
menu.comment_search=Comment search
|
||||
menu.goto_main_activity=Go to main Activity
|
||||
menu.tools=Tools
|
||||
menu.plugins=Plugins
|
||||
menu.decompile_all=Decompile all classes
|
||||
@@ -61,6 +62,7 @@ progress.decompile=Decompiling
|
||||
progress.canceling=Canceling
|
||||
|
||||
error_dialog.title=Error
|
||||
error_dialog.not_found=%s not found
|
||||
|
||||
search.previous=Previous
|
||||
search.next=Next
|
||||
|
||||
@@ -14,6 +14,7 @@ menu.navigation=Navegación
|
||||
menu.text_search=Buscar texto
|
||||
menu.class_search=Buscar clase
|
||||
#menu.comment_search=Comment search
|
||||
#menu.goto_main_activity=
|
||||
menu.tools=Herramientas
|
||||
#menu.plugins=Plugins
|
||||
#menu.decompile_all=Decompile all classes
|
||||
@@ -61,6 +62,7 @@ progress.decompile=Decompiling
|
||||
#progress.canceling=Canceling
|
||||
|
||||
#error_dialog.title=
|
||||
#error_dialog.not_found=
|
||||
|
||||
search.previous=Anterior
|
||||
search.next=Siguiente
|
||||
|
||||
@@ -14,6 +14,7 @@ menu.navigation=네비게이션
|
||||
menu.text_search=텍스트 검색
|
||||
menu.class_search=클래스 검색
|
||||
menu.comment_search=주석 검색
|
||||
#menu.goto_main_activity=
|
||||
menu.tools=도구
|
||||
#menu.plugins=Plugins
|
||||
#menu.decompile_all=Decompile all classes
|
||||
@@ -61,6 +62,7 @@ progress.decompile=디컴파일 중
|
||||
progress.canceling=취소 중
|
||||
|
||||
error_dialog.title=오류
|
||||
#error_dialog.not_found=
|
||||
|
||||
search.previous=이전
|
||||
search.next=다음
|
||||
|
||||
@@ -14,6 +14,7 @@ menu.navigation=Navegação
|
||||
menu.text_search=Buscar por texto
|
||||
menu.class_search=Buscar por classe
|
||||
menu.comment_search=Busca por comentário
|
||||
#menu.goto_main_activity=
|
||||
menu.tools=Ferramentas
|
||||
#menu.plugins=Plugins
|
||||
#menu.decompile_all=Decompile all classes
|
||||
@@ -61,6 +62,7 @@ progress.decompile=Descompilando
|
||||
progress.canceling=Cancelando
|
||||
|
||||
error_dialog.title=Erro
|
||||
#error_dialog.not_found=
|
||||
|
||||
search.previous=Anterior
|
||||
search.next=Próximo
|
||||
|
||||
@@ -14,6 +14,7 @@ menu.navigation=Навигация
|
||||
menu.text_search=Поиск строк
|
||||
menu.class_search=Поиск классов
|
||||
menu.comment_search=Поиск комментариев
|
||||
#menu.goto_main_activity=
|
||||
menu.tools=Инструменты
|
||||
#menu.plugins=Plugins
|
||||
#menu.decompile_all=Decompile all classes
|
||||
@@ -61,6 +62,7 @@ progress.decompile=Декомпиляция
|
||||
progress.canceling=Прерывание
|
||||
|
||||
error_dialog.title=Ошибка
|
||||
#error_dialog.not_found=
|
||||
|
||||
search.previous=Назад
|
||||
search.next=Вперед
|
||||
|
||||
@@ -14,6 +14,7 @@ menu.navigation=导航
|
||||
menu.text_search=文本搜索
|
||||
menu.class_search=类名搜索
|
||||
menu.comment_search=注释搜索
|
||||
#menu.goto_main_activity=
|
||||
menu.tools=工具
|
||||
menu.plugins=插件
|
||||
menu.decompile_all=反编译所有类
|
||||
@@ -61,6 +62,7 @@ progress.decompile=反编译中
|
||||
progress.canceling=正在取消
|
||||
|
||||
error_dialog.title=错误
|
||||
#error_dialog.not_found=
|
||||
|
||||
search.previous=上一个
|
||||
search.next=下一个
|
||||
|
||||
@@ -14,6 +14,7 @@ menu.navigation=瀏覽
|
||||
menu.text_search=文字搜尋
|
||||
menu.class_search=類別搜尋
|
||||
menu.comment_search=註解搜尋
|
||||
#menu.goto_main_activity=
|
||||
menu.tools=工具
|
||||
menu.plugins=外掛程式
|
||||
menu.decompile_all=反編譯所有類別
|
||||
@@ -61,6 +62,7 @@ progress.decompile=正在反編譯
|
||||
progress.canceling=正在取消
|
||||
|
||||
error_dialog.title=錯誤
|
||||
#error_dialog.not_found=
|
||||
|
||||
search.previous=上一個
|
||||
search.next=下一個
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#6E6E6E" width="16" height="16" viewBox="0 0 512 512" id="_x30_1" version="1.1" xml:space="preserve"><path d="M500.284,227.716l-216-216c-15.621-15.621-40.948-15.621-56.568,0h0l-216,216c-15.621,15.621-15.621,40.948,0,56.568 C23.69,296.258,41.363,299.042,56,292.659V452c0,33.137,26.863,60,60,60h280c33.137,0,60-26.863,60-60V292.659 c14.637,6.383,32.31,3.599,44.284-8.375h0C515.905,268.663,515.905,243.337,500.284,227.716z M326,432c0,11.046-8.954,20-20,20H206 c-11.046,0-20-8.954-20-20V312c0-11.046,8.954-20,20-20h100c11.046,0,20,8.954,20,20V432z"/></svg>
|
||||
|
After Width: | Height: | Size: 645 B |
Reference in New Issue
Block a user