Skip to content

Commit 5b7b3d3

Browse files
committed
refactor: some SwingWorkers and blocking behavior to avoid buggy UI
1 parent 5ea7cee commit 5b7b3d3

7 files changed

Lines changed: 149 additions & 46 deletions

File tree

.idea/codeStyles/Project.xml

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/model/AppState.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,6 @@ public static void setCurrentProcess(Process p) {
145145
return;
146146
}
147147
as.appFrame.repaint();
148-
System.out.println("New Process" + p);
149148
if (as.currentProcess != Process.NONE && p != Process.NONE) {
150149
String message = "Process cancelled: " + as.currentProcess + "is still active";
151150
throw new RuntimeException(message);
@@ -160,8 +159,9 @@ public static void setCurrentProcess(Process p) {
160159
default:
161160
as.isNestedLog = false;
162161
}
163-
System.out.println("Beginning process: " + p);
164-
System.out.println(as.isNestedLog);
162+
if (p != Process.NONE) {
163+
System.out.println("Beginning process: " + p);
164+
}
165165
as.currentProcess = p;
166166
}
167167
public static void endCurrentProcess(Process p) {

src/ui/UIPlaylistActions.java

Lines changed: 81 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package ui;
22

33
import model.AppState;
4-
import model.CardDetail;
54
import model.DropmixSharedAssets;
5+
import model.Process;
66
import util.UtilAdb;
77
import util.UtilApk;
88

@@ -12,16 +12,15 @@
1212
import java.awt.event.ActionListener;
1313
import java.io.File;
1414
import java.io.IOException;
15-
import java.nio.charset.StandardCharsets;
1615
import java.nio.file.Files;
1716
import java.nio.file.Path;
1817
import java.nio.file.Paths;
19-
import java.util.ArrayList;
2018
import java.util.TreeMap;
2119

2220
public class UIPlaylistActions extends JPanel {
2321
public String verifiedModApk;
2422
public static final String modDir = "dropmix_modded_src";
23+
2524
public UIPlaylistActions() {
2625
setLayout(new GridLayout(5, 1));
2726
try {
@@ -33,14 +32,23 @@ public void renderActions() {
3332
removeAll();
3433
AppState as = AppState.getInstance();
3534

35+
JButton resignedApkBtn = SwingFactory.buildButton("Build Re-Signed APK", new ActionListener() {
36+
@Override
37+
public void actionPerformed(ActionEvent e) {
38+
freshDecompile(false);
39+
}
40+
});
41+
resignedApkBtn.setEnabled(verifiedModApk == null && as.currentProcess.equals(Process.NONE) && as.playlistSwap.isEmpty());
42+
add(resignedApkBtn);
43+
3644
JButton modApkBtn = SwingFactory.buildButton("Validate Modified APK", new ActionListener() {
3745
@Override
3846
public void actionPerformed(ActionEvent e) {
39-
modApk();
47+
freshDecompile(true);
4048
System.out.println("Modified dropmix generated");
4149
}
4250
});
43-
modApkBtn.setEnabled(as.playlistSwap.size() > 0 && verifiedModApk == null);
51+
modApkBtn.setEnabled(as.playlistSwap.size() > 0 && verifiedModApk == null && as.currentProcess.equals(Process.NONE));
4452
add(modApkBtn);
4553

4654
JButton installApkBtn = SwingFactory.buildButton("Install APK", new ActionListener() {
@@ -81,31 +89,77 @@ public void actionPerformed(ActionEvent e) {
8189
add(installApkBtn);
8290
add(saveApk);
8391
}
84-
public String modApk() {
92+
private void freshDecompile(boolean useMod) {
8593
AppState as = AppState.getInstance();
8694
Path tempDir = Paths.get(modDir).toAbsolutePath();
87-
UtilApk.decompileApk(
88-
as.apkFile.getAbsolutePath(),
89-
tempDir.toString()
90-
);
91-
TreeMap<String, String> swapObj = AppState.getCardSwapFromPlaylist(as.playlistSwap);
92-
93-
byte[] modBytes = as.assetsHandler.applySwap(swapObj);
94-
Path assetsPath = Paths.get(tempDir.toString() + DropmixSharedAssets.assetsRelativePath);
95-
try {
96-
System.out.println("writing to "+ assetsPath.toAbsolutePath().toString());
97-
Files.deleteIfExists(assetsPath);
98-
Files.write(assetsPath, modBytes);
99-
Files.write(Paths.get("moddedFile"), modBytes);
100-
System.out.println("mod applied");
101-
} catch (IOException e) {
102-
System.out.println("mod write fail");
103-
}
104-
String output = UtilApk.recompile(tempDir.toAbsolutePath().toString(), "Dropmix190mod.apk");
105-
this.verifiedModApk = output;
106-
this.renderActions();
107-
return output;
95+
AppState.setCurrentProcess(Process.DECOMPILING);
96+
SwingWorker decompile = new SwingWorker() {
97+
@Override
98+
protected Object doInBackground() throws Exception {
99+
renderActions();
100+
UtilApk.decompileApk(as.apkFile.getAbsolutePath(), tempDir.toString());
101+
if (useMod) {
102+
AppState.switchCurrentProcess(Process.DECOMPILING, Process.GENERATING_MOD);
103+
freshModify();
104+
} else {
105+
AppState.switchCurrentProcess(Process.DECOMPILING, Process.RECOMPILING);
106+
recompile(useMod);
107+
}
108+
return null;
109+
}
110+
};
111+
decompile.execute();
108112
}
113+
// apply mod
114+
private void freshModify() {
115+
AppState as = AppState.getInstance();
116+
SwingWorker modify = new SwingWorker() {
117+
@Override
118+
protected Object doInBackground() throws Exception {
119+
renderActions();
120+
TreeMap<String, String> swapObj = AppState.getCardSwapFromPlaylist(as.playlistSwap);
121+
Path tempDir = Paths.get(modDir).toAbsolutePath();
122+
byte[] modBytes = as.assetsHandler.applySwap(swapObj);
123+
Path assetsPath = Paths.get(tempDir + DropmixSharedAssets.assetsRelativePath);
124+
try {
125+
System.out.println("writing to "+ assetsPath.toAbsolutePath().toString());
126+
Files.deleteIfExists(assetsPath);
127+
Files.write(assetsPath, modBytes);
128+
Files.write(Paths.get("moddedFile"), modBytes);
129+
System.out.println("mod applied");
130+
AppState.switchCurrentProcess(Process.GENERATING_MOD, Process.RECOMPILING);
131+
recompile(true);
132+
} catch (IOException e) {
133+
System.out.println("mod write fail");
134+
}
135+
return null;
136+
}
137+
};
138+
modify.execute();
139+
}
140+
// recompile and sign
141+
public void recompile(boolean useMod) {
142+
UIPlaylistActions that = this;
143+
SwingWorker sw = new SwingWorker() {
144+
@Override
145+
protected Object doInBackground() throws Exception {
146+
renderActions();
147+
Path tempDir = Paths.get(modDir).toAbsolutePath();
148+
String output = UtilApk.recompile(tempDir.toAbsolutePath().toString(), "Dropmix190mod.apk");
149+
that.verifiedModApk = output;
150+
that.renderActions();
151+
AppState.switchCurrentProcess(Process.RECOMPILING, Process.NONE);
152+
if (useMod) {
153+
System.out.println("Modified APK is now ready");
154+
} else {
155+
System.out.println("Re-signed APK generated");
156+
}
157+
return output;
158+
}
159+
};
160+
sw.execute();
161+
}
162+
109163
public void clearState() {
110164
try {
111165
if (this.verifiedModApk != null) {

src/ui/UIPlaylistsPanel.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import model.AppState;
44
import model.PlaylistDetail;
5+
import model.Process;
56

67
import javax.swing.*;
78
import java.awt.*;
@@ -84,6 +85,24 @@ public JComboBox<String> getPlaylistComboBox(String playlist) {
8485
@Override
8586
public void itemStateChanged(ItemEvent event) {
8687
if (event.getStateChange() == ItemEvent.SELECTED) {
88+
// prevent automatic setting of prior value from triggering further logic
89+
if (event.getItem() == oldSelectionItem) {
90+
return;
91+
}
92+
// Don't allow edits if process is active
93+
if (AppState.getInstance().currentProcess != Process.NONE) {
94+
System.out.println("Invalid item change action, process active "+AppState.getInstance().currentProcess);
95+
box.setSelectedIndex((Integer) oldSelectionItem);
96+
return;
97+
}
98+
// display warning message if changing from values used to build mod
99+
if (actions.verifiedModApk != null) {
100+
int i = JOptionPane.showConfirmDialog(null, "This action will clear the current APK mod, continue?");
101+
if (i != JOptionPane.YES_OPTION) {
102+
box.setSelectedIndex((Integer) oldSelectionItem);
103+
return;
104+
}
105+
}
87106
try {
88107
System.out.println(event.getItem() + "<-->" + playlist);
89108
// clear both fields if this value is selected

src/ui/UISetup.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -158,15 +158,26 @@ public void renderApkInput(int row) {
158158
@Override
159159
public void actionPerformed(ActionEvent ev) {
160160
System.out.println("Verifying APK: Setting up database...");
161-
decompileApk(as.apkFile.getAbsolutePath(), DropmixSharedAssets.decompiledPath);
162-
parentFrame.addCardsPanel();
163-
parentFrame.addPlaylistsPanel();
164-
that.apkVerified = true;
161+
AppState.setCurrentProcess(Process.DECOMPILING);
165162
refresh();
163+
SwingWorker sw = new SwingWorker() {
164+
@Override
165+
protected Object doInBackground() throws Exception {
166+
decompileApk(as.apkFile.getAbsolutePath(), DropmixSharedAssets.decompiledPath);
167+
AppState.endCurrentProcess(Process.DECOMPILING);
168+
parentFrame.addCardsPanel();
169+
parentFrame.addPlaylistsPanel();
170+
that.apkVerified = true;
171+
System.out.println("Swap panel is now ready");
172+
refresh();
173+
return null;
174+
}
175+
};
176+
sw.execute();
166177
}
167178
}
168179
);
169-
decompileBtn.setEnabled(!that.apkVerified);
180+
decompileBtn.setEnabled(!that.apkVerified || AppState.getInstance().currentProcess != Process.NONE);
170181
add(decompileBtn, c);
171182
}
172183
}

src/util/UtilAdb.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public class UtilAdb {
2222
private UtilAdb() {
2323

2424
}
25+
2526
public static void setAdbPath() {
2627
UtilAdb.adbPath = getAdbPath();
2728
}

src/util/UtilApk.java

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,41 @@
1010
import java.nio.file.Files;
1111
import java.nio.file.Paths;
1212

13-
public class UtilApk extends Thread {
13+
public class UtilApk implements Runnable {
1414
private String returnValue;
1515
private Process currentProcess;
1616
static String keyPath;
1717
static String certPath;
18+
public String input;
19+
public String output;
20+
public Process proc;
21+
22+
public UtilApk(Process p, String input, String output) {
23+
this.proc = p;
24+
this.input = input;
25+
this.output = output;
26+
}
1827

1928
// experimental multithreaded solution
20-
public void run(Process p, String input, String output) {
29+
@Override
30+
public synchronized void run() {
2131
long id = Thread.currentThread().getId();
2232
try {
23-
currentProcess = p;
33+
currentProcess = this.proc;
2434
// Displaying the thread that is running
2535
System.out.println(
26-
p + ": " + id
36+
this.proc + ": " + id
2737
+ " is running");
28-
if (p == Process.DECOMPILING) {
38+
if (this.proc == Process.DECOMPILING) {
2939
returnValue = decompileApk(input, output);
3040
}
31-
if (p == Process.RECOMPILING) {
41+
if (this.proc == Process.RECOMPILING) {
3242
returnValue = recompile(input, output);
3343
}
44+
notifyAll();
3445
}
3546
catch (Exception e) {
47+
e.printStackTrace();
3648
// Throwing an exception
3749
System.out.println("Exception is caught");
3850
}
@@ -45,11 +57,9 @@ public Process getProcess() {
4557
}
4658
public static String decompileApk(String apkPath, String outputPath) {
4759
try {
48-
AppState.setCurrentProcess(Process.DECOMPILING);
4960
brut.apktool.Main.main(new String[]{"d", "-rf", apkPath, "-o", outputPath});
5061
byte[] assetsFile = util.Helpers.loadLocalFile(outputPath + "/" + DropmixSharedAssets.assetsRelativePath);
5162

52-
AppState.endCurrentProcess(Process.DECOMPILING);
5363
if (assetsFile.length > 100000) {
5464
AppState.getInstance().setData(assetsFile);
5565
return outputPath;
@@ -63,9 +73,7 @@ public static String decompileApk(String apkPath, String outputPath) {
6373
public static String recompile(String inputPath, String outputPath) {
6474
getKeyAndCert();
6575
try {
66-
AppState.setCurrentProcess(Process.RECOMPILING);
6776
brut.apktool.Main.main(new String[]{"b", inputPath, "-o", DropmixSharedAssets.unsignedPath});
68-
AppState.switchCurrentProcess(Process.RECOMPILING, Process.SIGNING);
6977
// TODO is this worth splitting into its own function?
7078
ApkSignerTool.main(new String[]{
7179
"sign",
@@ -79,7 +87,6 @@ public static String recompile(String inputPath, String outputPath) {
7987
outputPath,
8088
});
8189
Files.deleteIfExists(Paths.get(DropmixSharedAssets.unsignedPath));
82-
AppState.endCurrentProcess(Process.SIGNING);
8390
System.out.println("Signed: "+ outputPath);
8491
return outputPath;
8592
} catch (BrutException | IOException e) {

0 commit comments

Comments
 (0)