fix(gui): split loading and UI update for code area (#2682)

This commit is contained in:
Skylot
2025-11-03 21:00:50 +00:00
parent d191f62b8d
commit bd75baef56
26 changed files with 283 additions and 88 deletions
@@ -514,9 +514,9 @@ public class Utils {
}
private static final class SimpleThreadFactory implements ThreadFactory {
private static final Logger LOG = LoggerFactory.getLogger(Utils.class);
private static final AtomicInteger POOL = new AtomicInteger(0);
private static final Thread.UncaughtExceptionHandler EXC_HANDLER = new SimpleUncaughtExceptionHandler();
private final AtomicInteger number = new AtomicInteger(0);
private final String name;
@@ -529,15 +529,22 @@ public class Utils {
Thread thread = new Thread(r, "jadx-" + name
+ '-' + POOL.incrementAndGet()
+ '-' + number.incrementAndGet());
thread.setUncaughtExceptionHandler((t, e) -> {
thread.setUncaughtExceptionHandler(EXC_HANDLER);
return thread;
}
private static class SimpleUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
private static final Logger LOG = LoggerFactory.getLogger(SimpleUncaughtExceptionHandler.class);
@Override
public void uncaughtException(Thread thread, Throwable e) {
if (e instanceof OutOfMemoryError) {
thread.interrupt();
LOG.error("OutOfMemoryError in thread: {}, forcing interrupt", t.getName());
LOG.error("OutOfMemoryError in thread: {}, forcing interrupt", thread.getName());
} else {
LOG.error("Uncaught thread exception, thread: {}", t.getName(), e);
LOG.error("Uncaught thread exception, thread: {}", thread.getName(), e);
}
});
return thread;
}
}
}
@@ -182,7 +182,7 @@ public class BackgroundExecutor {
jobsCount = taskExecutor.getTasksCount();
LOG.debug("Starting background task '{}', jobs count: {}, time limit: {} ms, memory check: {}",
task.getTitle(), jobsCount, task.timeLimit(), task.checkMemoryUsage());
if (jobsCount != 1) {
if (jobsCount != 1 && !task.isSilent()) {
progressPane.changeVisibility(this, true);
}
status = TaskStatus.STARTED;
@@ -202,6 +202,10 @@ public class BackgroundExecutor {
if (!taskExecutor.isRunning()) {
return TaskStatus.COMPLETE;
}
if (task.isSilent()) {
Thread.sleep(50);
continue;
}
TaskStatus cancelStatus = cancelCheck.get();
if (cancelStatus != null) {
performCancel();
@@ -55,4 +55,11 @@ public interface IBackgroundTask extends Cancelable {
default @Nullable Consumer<ITaskProgress> getProgressListener() {
return null;
}
/**
* Silent task: don't show progress
*/
default boolean isSilent() {
return false;
}
}
@@ -0,0 +1,47 @@
package jadx.gui.jobs;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Supplier;
import jadx.api.utils.tasks.ITaskExecutor;
import jadx.core.utils.tasks.TaskExecutor;
import jadx.gui.utils.NLS;
/**
* Load task: prepare data in background task and use that data in UI task
*/
public class LoadTask<T> extends CancelableBackgroundTask {
private final String title;
private final AtomicReference<T> taskData;
private final Runnable bgTask;
private final Runnable uiTask;
public LoadTask(Supplier<T> loadBgTask, Consumer<T> uiTask) {
this(NLS.str("progress.load"), loadBgTask, uiTask);
}
public LoadTask(String title, Supplier<T> loadBgTask, Consumer<T> uiTask) {
this.title = title;
this.taskData = new AtomicReference<>();
this.bgTask = () -> taskData.set(loadBgTask.get());
this.uiTask = () -> uiTask.accept(taskData.get());
}
@Override
public String getTitle() {
return title;
}
@Override
public ITaskExecutor scheduleTasks() {
TaskExecutor executor = new TaskExecutor();
executor.addSequentialTask(bgTask);
return executor;
}
@Override
public void onFinish(ITaskInfo taskInfo) {
uiTask.run();
}
}
@@ -0,0 +1,32 @@
package jadx.gui.jobs;
import jadx.api.utils.tasks.ITaskExecutor;
import jadx.core.utils.tasks.TaskExecutor;
/**
* Simple and short task, will not show progress
*/
public class SilentTask extends CancelableBackgroundTask {
private final Runnable task;
public SilentTask(Runnable task) {
this.task = task;
}
@Override
public boolean isSilent() {
return true;
}
@Override
public String getTitle() {
return "<silent>";
}
@Override
public ITaskExecutor scheduleTasks() {
TaskExecutor executor = new TaskExecutor();
executor.addSequentialTask(task);
return executor;
}
}
@@ -1,5 +1,6 @@
package jadx.gui.jobs;
import java.util.Objects;
import java.util.function.Consumer;
import org.jetbrains.annotations.Nullable;
@@ -13,15 +14,15 @@ public class TaskWithExtraOnFinish implements IBackgroundTask {
private final IBackgroundTask task;
private final Consumer<TaskStatus> extraOnFinish;
public TaskWithExtraOnFinish(IBackgroundTask task, Consumer<TaskStatus> extraOnFinish) {
this.task = task;
this.extraOnFinish = extraOnFinish;
}
public TaskWithExtraOnFinish(IBackgroundTask task, Runnable extraOnFinish) {
this(task, s -> extraOnFinish.run());
}
public TaskWithExtraOnFinish(IBackgroundTask task, Consumer<TaskStatus> extraOnFinish) {
this.task = Objects.requireNonNull(task);
this.extraOnFinish = Objects.requireNonNull(extraOnFinish);
}
@Override
public void onFinish(ITaskInfo taskInfo) {
task.onFinish(taskInfo);
@@ -38,6 +38,11 @@ public class JInputMapping extends JEditableNode {
this.name = mappingPath.getFileName().toString();
}
@Override
public boolean hasContent() {
return true;
}
@Override
public ContentPanel getContentPanel(TabbedPane tabbedPane) {
return new CodeContentPanel(tabbedPane, this);
@@ -53,6 +53,11 @@ public class QuarkReportNode extends JNode {
return "Quark analysis report";
}
@Override
public boolean hasContent() {
return true;
}
@Override
public ContentPanel getContentPanel(TabbedPane tabbedPane) {
try {
@@ -4,6 +4,8 @@ import org.fife.ui.autocomplete.AutoCompletion;
import org.jetbrains.annotations.NotNull;
import jadx.api.ICodeInfo;
import jadx.gui.jobs.IBackgroundTask;
import jadx.gui.jobs.LoadTask;
import jadx.gui.settings.JadxSettings;
import jadx.gui.treemodel.JInputScript;
import jadx.gui.ui.action.JadxAutoCompletion;
@@ -48,12 +50,14 @@ public class ScriptCodeArea extends AbstractCodeArea {
}
@Override
public void load() {
if (getText().isEmpty()) {
setText(getCodeInfo().getCodeStr());
setCaretPosition(0);
setLoaded();
}
public IBackgroundTask getLoadTask() {
return new LoadTask<>(
() -> node.getCodeInfo().getCodeStr(),
code -> {
setText(code);
setCaretPosition(0);
setLoaded();
});
}
@Override
@@ -83,6 +83,11 @@ public class ApkSignatureNode extends JNode {
return "APK signature";
}
@Override
public boolean hasContent() {
return true;
}
@Override
public ContentPanel getContentPanel(TabbedPane tabbedPane) {
ApkSignatureNode.tabbedPane = tabbedPane;
@@ -135,6 +135,11 @@ public class JClass extends JLoadableNode implements JRenameNode {
return cls.getCodeInfo();
}
@Override
public boolean hasContent() {
return true;
}
@Override
public ContentPanel getContentPanel(TabbedPane tabbedPane) {
return new ClassCodeContentPanel(tabbedPane, this);
@@ -36,6 +36,11 @@ public class JInputScript extends JEditableNode {
this.name = scriptPath.getFileName().toString().replace(".jadx.kts", "");
}
@Override
public boolean hasContent() {
return true;
}
@Override
public ContentPanel getContentPanel(TabbedPane tabbedPane) {
return new ScriptContentPanel(tabbedPane, this);
@@ -35,6 +35,11 @@ public class JInputSmaliFile extends JEditableNode {
return JInputFile.buildInputFilePopupMenu(mainWindow, filePath);
}
@Override
public boolean hasContent() {
return true;
}
@Override
public @Nullable ContentPanel getContentPanel(TabbedPane tabbedPane) {
return new CodeContentPanel(tabbedPane, this);
@@ -45,6 +45,10 @@ public abstract class JNode extends DefaultMutableTreeNode implements ITreeNode,
return null;
}
public boolean hasContent() {
return false;
}
public @Nullable ContentPanel getContentPanel(TabbedPane tabbedPane) {
return null;
}
@@ -152,6 +152,11 @@ public class JResource extends JLoadableNode {
}
}
@Override
public boolean hasContent() {
return resFile != null;
}
@Override
public @Nullable ContentPanel getContentPanel(TabbedPane tabbedPane) {
if (resFile == null) {
@@ -120,15 +120,11 @@ import jadx.gui.settings.JadxSettings;
import jadx.gui.settings.ui.JadxSettingsWindow;
import jadx.gui.tree.TreeExpansionService;
import jadx.gui.treemodel.ApkSignatureNode;
import jadx.gui.treemodel.JInputFiles;
import jadx.gui.treemodel.JInputScripts;
import jadx.gui.treemodel.JInputs;
import jadx.gui.treemodel.JLoadableNode;
import jadx.gui.treemodel.JNode;
import jadx.gui.treemodel.JPackage;
import jadx.gui.treemodel.JResource;
import jadx.gui.treemodel.JRoot;
import jadx.gui.treemodel.JSources;
import jadx.gui.ui.action.ActionModel;
import jadx.gui.ui.action.JadxGuiAction;
import jadx.gui.ui.codearea.AbstractCodeArea;
@@ -920,11 +916,7 @@ public class MainWindow extends JFrame {
}
} else if (obj instanceof JNode) {
JNode treeNode = (JNode) obj;
if (!(treeNode instanceof JPackage)
&& !(treeNode instanceof JSources)
&& !(treeNode instanceof JInputs)
&& !(treeNode instanceof JInputFiles)
&& !(treeNode instanceof JInputScripts)) {
if (treeNode.hasContent() || treeNode.getJParent() != null) {
tabsController.codeJump(treeNode, true);
return true;
}
@@ -13,6 +13,7 @@ import java.awt.event.KeyEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.AbstractAction;
import javax.swing.Action;
@@ -45,6 +46,7 @@ import org.slf4j.LoggerFactory;
import jadx.api.ICodeInfo;
import jadx.core.utils.StringUtils;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.gui.jobs.IBackgroundTask;
import jadx.gui.settings.JadxSettings;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JEditableNode;
@@ -83,7 +85,7 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
protected ContentPanel contentPanel;
protected JNode node;
protected volatile boolean loaded = false;
private final AtomicBoolean loaded = new AtomicBoolean(false);
public AbstractCodeArea(ContentPanel contentPanel, JNode node) {
this.contentPanel = contentPanel;
@@ -254,7 +256,7 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
private void addChangeUpdates(JEditableNode editableNode) {
getDocument().addDocumentListener(new DocumentUpdateListener(ev -> {
if (loaded) {
if (loaded.get()) {
editableNode.setChanged(true);
}
}));
@@ -349,17 +351,33 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
public abstract ICodeInfo getCodeInfo();
public void load() {
if (isLoaded()) {
return;
}
IBackgroundTask loadTask = getLoadTask();
contentPanel.getMainWindow().getBackgroundExecutor().execute(loadTask);
}
/**
* Implement in this method the code that loads and sets the content to be displayed
* Call `setLoaded()` on load finish.
*/
public abstract void load();
public abstract IBackgroundTask getLoadTask();
public void setLoaded() {
this.loaded = true;
this.loaded.set(true);
discardAllEdits(); // disable 'undo' action to empty state (before load)
}
public void setUnLoaded() {
this.loaded.set(false);
}
public boolean isLoaded() {
return loaded.get();
}
/**
* Implement in this method the code that reloads node from cache and sets the new content to be
* displayed
@@ -15,6 +15,8 @@ import org.slf4j.LoggerFactory;
import jadx.api.ResourcesLoader;
import jadx.api.resources.ResourceContentType;
import jadx.gui.jobs.BackgroundExecutor;
import jadx.gui.jobs.IBackgroundTask;
import jadx.gui.jobs.TaskWithExtraOnFinish;
import jadx.gui.settings.JadxSettings;
import jadx.gui.settings.LineNumbersMode;
import jadx.gui.treemodel.JNode;
@@ -89,7 +91,7 @@ public class BinaryContentPanel extends AbstractCodeContentPanel {
Component codePanel = getSelectedPanel();
if (codePanel instanceof CodeArea) {
CodeArea codeArea = (CodeArea) codePanel;
bgExec.startLoading(codeArea::load);
codeArea.load();
} else {
bgExec.startLoading(this::loadHexView);
}
@@ -106,11 +108,17 @@ public class BinaryContentPanel extends AbstractCodeContentPanel {
@Override
public void scrollToPos(int pos) {
UiUtils.uiThreadGuard();
BackgroundExecutor bgExec = getMainWindow().getBackgroundExecutor();
if (getNode().getContentType() == ResourceContentType.CONTENT_TEXT) {
areaTabbedPane.setSelectedComponent(textCodePanel);
AbstractCodeArea codeArea = textCodePanel.getCodeArea();
bgExec.startLoading(codeArea::load, () -> codeArea.scrollToPos(pos));
if (codeArea.isLoaded()) {
codeArea.scrollToPos(pos);
} else {
IBackgroundTask loadTask = codeArea.getLoadTask();
bgExec.execute(new TaskWithExtraOnFinish(loadTask, () -> codeArea.scrollToPos(pos)));
}
} else {
areaTabbedPane.setSelectedComponent(hexPreviewPanel);
bgExec.startLoading(this::loadHexView, () -> hexPreviewPanel.scrollToOffset(pos));
@@ -21,8 +21,12 @@ import jadx.api.JavaClass;
import jadx.api.JavaNode;
import jadx.api.metadata.ICodeAnnotation;
import jadx.gui.JadxWrapper;
import jadx.gui.jobs.IBackgroundTask;
import jadx.gui.jobs.LoadTask;
import jadx.gui.jobs.TaskWithExtraOnFinish;
import jadx.gui.settings.JadxProject;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JLoadableNode;
import jadx.gui.treemodel.JNode;
import jadx.gui.treemodel.JResource;
import jadx.gui.ui.MainWindow;
@@ -120,12 +124,24 @@ public final class CodeArea extends AbstractCodeArea {
}
@Override
public void load() {
if (getText().isEmpty()) {
setText(getCodeInfo().getCodeStr());
setCaretPosition(0);
setLoaded();
public IBackgroundTask getLoadTask() {
if (node instanceof JLoadableNode) {
IBackgroundTask loadTask = ((JLoadableNode) node).getLoadTask();
if (loadTask != null) {
return new TaskWithExtraOnFinish(loadTask, () -> {
setText(getCodeInfo().getCodeStr());
setCaretPosition(0);
setLoaded();
});
}
}
return new LoadTask<>(
() -> getCodeInfo().getCodeStr(),
code -> {
setText(code);
setCaretPosition(0);
setLoaded();
});
}
@Override
@@ -37,6 +37,8 @@ import org.slf4j.LoggerFactory;
import jadx.api.ICodeInfo;
import jadx.gui.device.debugger.BreakpointManager;
import jadx.gui.device.debugger.DbgUtils;
import jadx.gui.jobs.IBackgroundTask;
import jadx.gui.jobs.LoadTask;
import jadx.gui.settings.JadxSettings;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
@@ -88,13 +90,15 @@ public final class SmaliArea extends AbstractCodeArea {
}
@Override
public void load() {
if (getText().isEmpty() || curVersion != shouldUseSmaliPrinterV2()) {
curVersion = shouldUseSmaliPrinterV2();
model.load();
setCaretPosition(0);
setLoaded();
}
public IBackgroundTask getLoadTask() {
return new LoadTask<>(
() -> model.loadCode(),
code -> {
curVersion = shouldUseSmaliPrinterV2();
model.loadUI(code);
setCaretPosition(0);
setLoaded();
});
}
@Override
@@ -121,7 +125,10 @@ public final class SmaliArea extends AbstractCodeArea {
if (model != null) {
model.unload();
}
model = shouldUseSmaliPrinterV2() ? new DebugModel() : new NormalModel(this);
curVersion = shouldUseSmaliPrinterV2();
model = curVersion ? new DebugModel() : new NormalModel(this);
setUnLoaded();
load();
}
public void scrollToDebugPos(int pos) {
@@ -153,7 +160,9 @@ public final class SmaliArea extends AbstractCodeArea {
}
private abstract class SmaliModel {
abstract void load();
abstract String loadCode();
abstract void loadUI(String code);
abstract void unload();
@@ -173,20 +182,23 @@ public final class SmaliArea extends AbstractCodeArea {
}
private class NormalModel extends SmaliModel {
public NormalModel(SmaliArea smaliArea) {
getContentPanel().getMainWindow().getEditorThemeManager().apply(smaliArea);
setSyntaxEditingStyle(SYNTAX_STYLE_SMALI);
}
@Override
public void load() {
setText(getJClass().getSmali());
public String loadCode() {
return getJClass().getSmali();
}
@Override
public void loadUI(String code) {
setText(code);
}
@Override
public void unload() {
}
}
@@ -210,7 +222,12 @@ public final class SmaliArea extends AbstractCodeArea {
}
@Override
public void load() {
String loadCode() {
return DbgUtils.getSmaliCode(((JClass) node).getCls().getClassNode());
}
@Override
void loadUI(String code) {
if (gutter == null) {
gutter = RSyntaxUtilities.getGutter(SmaliArea.this);
gutter.setBookmarkingEnabled(true);
@@ -218,7 +235,7 @@ public final class SmaliArea extends AbstractCodeArea {
Font baseFont = SmaliArea.super.getFont();
gutter.setLineNumberFont(baseFont.deriveFont(baseFont.getSize2D() - 1.0f));
}
setText(DbgUtils.getSmaliCode(((JClass) node).getCls().getClassNode()));
setText(code);
loadV2Style();
loadBreakpoints();
}
@@ -12,6 +12,11 @@ import jadx.gui.utils.NLS;
public class StartPageNode extends JNode {
private static final long serialVersionUID = 8983134608645736174L;
@Override
public boolean hasContent() {
return true;
}
@Override
public ContentPanel getContentPanel(TabbedPane tabbedPane) {
return new StartPagePanel(tabbedPane, this);
@@ -23,6 +23,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.gui.jobs.SilentTask;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.MainWindow;
@@ -221,7 +222,7 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener {
private @Nullable ContentPanel showCode(JumpPosition jumpPos) {
JNode jumpNode = jumpPos.getNode();
ContentPanel contentPanel = getContentPanel(jumpNode);
ContentPanel contentPanel = getTabByNode(jumpNode);
if (contentPanel == null) {
return null;
}
@@ -314,11 +315,6 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener {
return (TabComponent) component;
}
private @Nullable ContentPanel getContentPanel(JNode node) {
controller.openTab(node);
return getTabByNode(node);
}
public void refresh(JNode node) {
ContentPanel panel = getTabByNode(node);
if (panel != null) {
@@ -421,7 +417,7 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener {
@Override
public void onTabSelect(TabBlueprint blueprint) {
ContentPanel contentPanel = getContentPanel(blueprint.getNode());
ContentPanel contentPanel = getTabByNode(blueprint.getNode());
if (contentPanel != null) {
setSelectedComponent(contentPanel);
}
@@ -429,7 +425,8 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener {
@Override
public void onTabCodeJump(TabBlueprint blueprint, @Nullable JumpPosition prevPos, JumpPosition position) {
showCode(position);
// queue task to wait completion of loading tasks
mainWindow.getBackgroundExecutor().execute(new SilentTask(() -> showCode(position)));
}
@Override
@@ -15,6 +15,7 @@ import jadx.api.JavaClass;
import jadx.api.metadata.ICodeAnnotation;
import jadx.api.metadata.ICodeNodeRef;
import jadx.api.metadata.annotations.NodeDeclareRef;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.gui.jobs.SimpleTask;
import jadx.gui.jobs.TaskWithExtraOnFinish;
import jadx.gui.treemodel.JClass;
@@ -36,7 +37,9 @@ public class TabsController {
public TabsController(MainWindow mainWindow) {
this.mainWindow = mainWindow;
// addListener(new LogTabStates());
if (UiUtils.JADX_GUI_DEBUG) {
addListener(new LogTabStates());
}
}
public MainWindow getMainWindow() {
@@ -59,11 +62,10 @@ public class TabsController {
return openTab(node, false, false);
}
public TabBlueprint openTab(JNode node, boolean hidden) {
return openTab(node, hidden, false);
}
public TabBlueprint openTab(JNode node, boolean hidden, boolean preview) {
if (!node.hasContent()) {
throw new JadxRuntimeException("Can't open tab for node without content");
}
TabBlueprint blueprint = getTabByNode(node);
if (blueprint == null) {
TabBlueprint newBlueprint = new TabBlueprint(node);
@@ -77,10 +79,6 @@ public class TabsController {
blueprint = newBlueprint;
}
setTabHiddenInternal(blueprint, hidden);
if (!blueprint.isCreated()) {
LOG.warn("No content panel for node: {}", node);
closeTabForce(blueprint);
}
return blueprint;
}
@@ -89,10 +87,7 @@ public class TabsController {
if (blueprint != null) {
closeTab(blueprint.getNode());
}
blueprint = openTab(node, false, true);
return blueprint;
return openTab(node, false, true);
}
public void selectTab(JNode node) {
@@ -125,6 +120,9 @@ public class TabsController {
public void codeJump(JNode node, boolean fromTree) {
JClass parentCls = node.getJParent();
if (parentCls != null) {
// handle jump to inner class, method or field:
// - load parent
// - search position and jump to it
JavaClass cls = node.getJParent().getCls();
JavaClass origTopCls = cls.getOriginalTopParentClass();
JavaClass codeParent = cls.getTopParentClass();
@@ -134,19 +132,12 @@ public class TabsController {
return;
}
}
// Not an inline node, jump normally
if (node.getPos() > 0) {
codeJump(new JumpPosition(node), fromTree);
return;
}
if (node.getRootClass() == null) {
// not a class, select tab without position scroll
// not a class, select tab (without position scroll)
selectTab(node, fromTree);
return;
}
// node need loading
loadCodeWithUIAction(node.getRootClass(), () -> codeJump(new JumpPosition(node), fromTree));
codeJump(new JumpPosition(node), fromTree);
}
private void loadCodeWithUIAction(JClass cls, Runnable action) {
@@ -194,6 +194,11 @@ public class SummaryNode extends JNode {
return String.format("%d (%.2f%%)", value, value * 100 / ((double) total));
}
@Override
public boolean hasContent() {
return true;
}
@Override
public ContentPanel getContentPanel(TabbedPane tabbedPane) {
return new HtmlPanel(tabbedPane, this);
@@ -1,6 +1,6 @@
package jadx.gui.ui.treenodes;
import javax.swing.*;
import javax.swing.Icon;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
@@ -19,6 +19,11 @@ public class UndisplayedStringsNode extends JNode {
this.undisplayedStings = undisplayedStings;
}
@Override
public boolean hasContent() {
return true;
}
@Override
public ContentPanel getContentPanel(TabbedPane tabbedPane) {
return new UndisplayedStringsPanel(tabbedPane, this);
@@ -430,17 +430,17 @@ public class UiUtils {
* Uses single thread, so all tasks are ordered.
*/
public static void bgRun(Runnable runnable) {
BACKGROUND_THREAD.submit(runnable);
BACKGROUND_THREAD.execute(runnable);
}
public static void uiThreadGuard() {
if (!SwingUtilities.isEventDispatchThread()) {
if (JADX_GUI_DEBUG && !SwingUtilities.isEventDispatchThread()) {
LOG.warn("Expect UI thread, got: {}", Thread.currentThread(), new JadxRuntimeException());
}
}
public static void notUiThreadGuard() {
if (SwingUtilities.isEventDispatchThread()) {
if (JADX_GUI_DEBUG && SwingUtilities.isEventDispatchThread()) {
LOG.warn("Expect background thread, got: {}", Thread.currentThread(), new JadxRuntimeException());
}
}