fix(gui): use UI thread for adding logcat messages (#2811)

* fix(gui): use UI thread for adding logcat messages
other minor/logging improvements for debugger and adb connection

* checkstyle
This commit is contained in:
Jan S.
2026-03-04 21:06:30 +01:00
committed by GitHub
parent ff4dde62ae
commit ff778ab372
7 changed files with 155 additions and 162 deletions
@@ -9,16 +9,18 @@ import java.util.regex.Pattern;
*/
public class LogUtils {
private static final Pattern ALFA_NUMERIC = Pattern.compile("\\w*");
/**
* We replace everything except alphanumeric characters, underscore, dots, colon, semicolon, comma,
* spaces, minus
*/
private static final Pattern REPLACE_PATTERN = Pattern.compile("[^\\w\\.:;, -]");
public static String escape(String input) {
if (input == null) {
return "null";
}
if (ALFA_NUMERIC.matcher(input).matches()) {
return input;
}
return input.replaceAll("\\W", ".");
return REPLACE_PATTERN.matcher(input).replaceAll(".");
}
public static String escape(byte[] input) {
@@ -8,6 +8,8 @@ class LogUtilsTest {
@Test
void escape() {
assertThat(LogUtils.escape("Guest'%0AUser:'Admin")).isEqualTo("Guest..0AUser..Admin");
String src = "a.b,c:d;e disallowed\"a'b#c*d\te\rf\ng";
String out = "a.b,c:d;e disallowed.a.b.c.d.e.f.g";
assertThat(LogUtils.escape(src)).isEqualTo(out);
}
}
@@ -8,8 +8,10 @@ import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -26,7 +28,7 @@ public class LogcatController {
private final String timezone;
private LogcatInfo recent = null;
private List<LogcatInfo> events = new ArrayList<>();
private LogcatFilter filter = new LogcatFilter(null, null);
private LogcatFilter filter = new LogcatFilter();
private String status = "null";
public LogcatController(LogcatPanel logcatPanel, ADBDevice adbDevice) throws IOException {
@@ -155,7 +157,7 @@ public class LogcatController {
public void exit() {
stopLogcat();
filter = new LogcatFilter(null, null);
filter = new LogcatFilter();
recent = null;
}
@@ -164,44 +166,25 @@ public class LogcatController {
}
public class LogcatFilter {
private final List<Integer> pid;
private List<Byte> msgType = new ArrayList<>() {
{
add((byte) 1);
add((byte) 2);
add((byte) 3);
add((byte) 4);
add((byte) 5);
add((byte) 6);
add((byte) 7);
add((byte) 8);
}
};
public LogcatFilter(ArrayList<Integer> pid, ArrayList<Byte> msgType) {
if (pid != null) {
this.pid = pid;
} else {
this.pid = new ArrayList<>();
}
private final Set<Integer> pid;
private final Set<Byte> msgType;
if (msgType != null) {
this.msgType = msgType;
}
public LogcatFilter() {
this(new TreeSet<>(), new TreeSet<>(List.of((byte) 1, (byte) 2, (byte) 3, (byte) 4, (byte) 5, (byte) 6, (byte) 7, (byte) 8)));
}
public LogcatFilter(Set<Integer> pid, Set<Byte> msgType) {
this.pid = pid;
this.msgType = msgType;
}
public void addPid(int pid) {
if (!this.pid.contains(pid)) {
this.pid.add(pid);
}
this.pid.add(pid);
}
public void removePid(int pid) {
int pidPos = this.pid.indexOf(pid);
if (pidPos >= 0) {
this.pid.remove(pidPos);
}
this.pid.remove(pid);
}
public void togglePid(int pid, boolean state) {
@@ -213,16 +196,11 @@ public class LogcatController {
}
public void addMsgType(byte msgType) {
if (!this.msgType.contains(msgType)) {
this.msgType.add(msgType);
}
this.msgType.add(msgType);
}
public void removeMsgType(byte msgType) {
int typePos = this.msgType.indexOf(msgType);
if (typePos >= 0) {
this.msgType.remove(typePos);
}
this.msgType.remove(msgType);
}
public void toggleMsgType(byte msgType, boolean state) {
@@ -234,10 +212,7 @@ public class LogcatController {
}
public boolean doFilter(LogcatInfo inInfo) {
if (pid.contains(inInfo.getPid())) {
return msgType.contains(inInfo.getMsgType());
}
return false;
return (pid.contains(inInfo.getPid())) && msgType.contains(inInfo.getMsgType());
}
public List<LogcatInfo> getFilteredList(List<LogcatInfo> inInfoList) {
@@ -6,14 +6,18 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -24,6 +28,9 @@ import jadx.core.utils.log.LogUtils;
import jadx.gui.utils.IOUtils;
public class ADB {
public static final Charset ADB_CHARSET = StandardCharsets.UTF_8;
private static final Logger LOG = LoggerFactory.getLogger(ADB.class);
private static final int DEFAULT_PORT = 5037;
@@ -31,10 +38,10 @@ public class ADB {
private static final String CMD_FEATURES = "000dhost:features";
private static final String CMD_TRACK_DEVICES = "0014host:track-devices-l";
private static final byte[] OKAY = "OKAY".getBytes();
private static final byte[] FAIL = "FAIL".getBytes();
private static final byte[] OKAY = "OKAY".getBytes(ADB_CHARSET);
private static final byte[] FAIL = "FAIL".getBytes(ADB_CHARSET);
static boolean isOkay(InputStream stream) throws IOException {
static boolean isOkay(InputStream stream, String command) throws IOException {
byte[] buf = IOUtils.readNBytes(stream, 4);
if (Arrays.equals(buf, OKAY)) {
return true;
@@ -45,13 +52,13 @@ public class ADB {
// int msgLen = Integer.parseInt(new String(IOUtils.readNBytes(stream, 4)), 16);
// byte[] errorMsg = IOUtils.readNBytes(stream, msgLen);
// LOG.error("isOkay failed: received error message: {}", new String(errorMsg));
LOG.error("isOkay failed");
LOG.error("isOkay failed for command: {}", command);
return false;
}
if (buf == null) {
throw new IOException("isOkay failed - steam ended");
}
throw new IOException("isOkay failed - unexpected response " + new String(buf));
throw new IOException("isOkay failed - unexpected response " + new String(buf, ADB_CHARSET));
}
public static byte[] exec(String cmd, OutputStream outputStream, InputStream inputStream) throws IOException {
@@ -73,13 +80,13 @@ public class ADB {
}
static boolean execCommandAsync(OutputStream outputStream, InputStream inputStream, String cmd) throws IOException {
outputStream.write(cmd.getBytes());
return isOkay(inputStream);
outputStream.write(cmd.getBytes(ADB_CHARSET));
return isOkay(inputStream, "execCommandAsync");
}
private static byte[] execCommandSync(OutputStream outputStream, InputStream inputStream, String cmd) throws IOException {
outputStream.write(cmd.getBytes());
if (isOkay(inputStream)) {
outputStream.write(cmd.getBytes(ADB_CHARSET));
if (isOkay(inputStream, "execCommandSync")) {
return readServiceProtocol(inputStream);
}
return null;
@@ -91,7 +98,7 @@ public class ADB {
if (buf == null) {
return null;
}
int len = unhex(buf);
int len = hexToInt(buf);
byte[] result;
if (len == 0) {
result = new byte[0];
@@ -111,13 +118,15 @@ public class ADB {
}
static boolean setSerial(String serial, OutputStream outputStream, InputStream inputStream) throws IOException {
checkSerial(serial);
LOG.trace("setSerial({})", serial);
String setSerialCmd = String.format("host:tport:serial:%s", serial);
setSerialCmd = String.format("%04x%s", setSerialCmd.length(), setSerialCmd);
outputStream.write(setSerialCmd.getBytes());
boolean ok = isOkay(inputStream);
outputStream.write(setSerialCmd.getBytes(ADB_CHARSET));
boolean ok = isOkay(inputStream, setSerialCmd);
if (ok) {
// skip the shell-state-id returned by ADB server, it's not important for the following actions.
IOUtils.readNBytes(inputStream, 8);
inputStream.readNBytes(8);
} else {
LOG.error("setSerial command {} failed", LogUtils.escape(setSerialCmd));
}
@@ -127,8 +136,8 @@ public class ADB {
private static byte[] execShellCommandRaw(String cmd, OutputStream outputStream, InputStream inputStream) throws IOException {
cmd = String.format("shell,v2,TERM=xterm-256color,raw:%s", cmd);
cmd = String.format("%04x%s", cmd.length(), cmd);
outputStream.write(cmd.getBytes());
if (isOkay(inputStream)) {
outputStream.write(cmd.getBytes(ADB_CHARSET));
if (isOkay(inputStream, cmd)) {
return ShellProtocol.readStdout(inputStream);
}
return null;
@@ -144,7 +153,7 @@ public class ADB {
public static List<String> getFeatures() throws IOException {
byte[] rst = exec(CMD_FEATURES);
if (rst != null) {
return Arrays.asList(new String(rst).trim().split(","));
return Arrays.asList(new String(rst, ADB_CHARSET).trim().split(","));
}
return Collections.emptyList();
}
@@ -202,7 +211,7 @@ public class ADB {
break; // socket disconnected
}
if (listener != null) {
String payload = new String(res);
String payload = new String(res, ADB_CHARSET);
String[] deviceLines = payload.split("\n");
List<ADBDeviceInfo> deviceInfoList = new ArrayList<>(deviceLines.length);
for (String deviceLine : deviceLines) {
@@ -225,11 +234,11 @@ public class ADB {
String cmd = "0011host:list-forward";
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
outputStream.write(cmd.getBytes());
if (isOkay(inputStream)) {
outputStream.write(cmd.getBytes(ADB_CHARSET));
if (isOkay(inputStream, "listForward")) {
byte[] bytes = readServiceProtocol(inputStream);
if (bytes != null) {
String[] forwards = new String(bytes).split("\n");
String[] forwards = new String(bytes, ADB_CHARSET).split("\n");
return Stream.of(forwards).map(String::trim).collect(Collectors.toList());
}
}
@@ -244,8 +253,8 @@ public class ADB {
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
if (setSerial(serial, outputStream, inputStream)) {
outputStream.write(cmd.getBytes());
return isOkay(inputStream) && isOkay(inputStream);
outputStream.write(cmd.getBytes(ADB_CHARSET));
return isOkay(inputStream, "removeForward1") && isOkay(inputStream, "removeForward2");
}
}
return false;
@@ -267,7 +276,21 @@ public class ADB {
return rst;
}
private static int unhex(byte[] hex) {
private static final Pattern SERIAL_PATTERN = Pattern.compile("^[\\w-]{10,20}$");
private static void checkSerial(String serial) {
if (!SERIAL_PATTERN.matcher(serial).matches()) {
throw new IllegalArgumentException("Invalid serial: " + serial);
}
}
/**
* Convert 4 hex characters to int
*
* @param hex
* @return
*/
private static int hexToInt(byte[] hex) {
int n = 0;
byte b;
for (int i = 0; i < 4; i++) {
@@ -340,6 +363,16 @@ public class ADB {
}
return null;
}
@Override
public String toString() {
return new StringJoiner(", ", Process.class.getSimpleName() + "[", "]")
.add("user='" + user + "'")
.add("pid='" + pid + "'")
.add("ppid='" + ppid + "'")
.add("name='" + name + "'")
.toString();
}
}
private static class ShellProtocol {
@@ -9,7 +9,6 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -21,6 +20,8 @@ import jadx.core.utils.log.LogUtils;
import jadx.gui.device.protocol.ADB.JDWPProcessListener;
import jadx.gui.device.protocol.ADB.Process;
import static jadx.gui.device.protocol.ADB.ADB_CHARSET;
public class ADBDevice {
private static final Logger LOG = LoggerFactory.getLogger(ADBDevice.class);
@@ -65,16 +66,16 @@ public class ADBDevice {
OutputStream outputStream = socket.getOutputStream();
ForwardResult rst;
if (ADB.setSerial(info.getSerial(), outputStream, inputStream)) {
outputStream.write(cmd.getBytes());
if (!ADB.isOkay(inputStream)) {
outputStream.write(cmd.getBytes(ADB_CHARSET));
if (!ADB.isOkay(inputStream, "forwardJDWP1")) {
rst = new ForwardResult(1, ADB.readServiceProtocol(inputStream));
} else if (!ADB.isOkay(inputStream)) {
} else if (!ADB.isOkay(inputStream, "forwardJDWP2")) {
rst = new ForwardResult(2, ADB.readServiceProtocol(inputStream));
} else {
rst = new ForwardResult(0, null);
}
} else {
rst = new ForwardResult(1, "Unknown error.".getBytes());
rst = new ForwardResult(1, "Unknown error.".getBytes(ADB_CHARSET));
}
return rst;
}
@@ -89,7 +90,7 @@ public class ADBDevice {
public ForwardResult(int state, byte[] desc) {
if (desc != null) {
this.desc = new String(desc);
this.desc = new String(desc, ADB_CHARSET);
} else {
this.desc = "";
}
@@ -109,7 +110,7 @@ public class ADBDevice {
return -1;
}
}
String rst = new String(res).trim();
String rst = new String(res, ADB_CHARSET).trim();
if (rst.startsWith("Starting: Intent {") && rst.endsWith(fullAppName + " }")) {
Thread.sleep(40);
String pkg = fullAppName.split("/")[0];
@@ -124,10 +125,10 @@ public class ADBDevice {
* @return binary output of logcat
*/
public byte[] getBinaryLogcat() throws IOException {
Socket socket = ADB.connect(info.getAdbHost(), info.getAdbPort());
String cmd = "logcat -dB";
return ADB.execShellCommandRaw(info.getSerial(), cmd, socket.getOutputStream(), socket.getInputStream());
try (Socket socket = ADB.connect(info.getAdbHost(), info.getAdbPort())) {
String cmd = "logcat -dB";
return ADB.execShellCommandRaw(info.getSerial(), cmd, socket.getOutputStream(), socket.getInputStream());
}
}
/**
@@ -135,32 +136,38 @@ public class ADBDevice {
* Timestamp is in the format 09-08 02:18:03.131
*/
public byte[] getBinaryLogcat(String timestamp) throws IOException {
Socket socket = ADB.connect(info.getAdbHost(), info.getAdbPort());
Matcher matcher = TIMESTAMP_FORMAT.matcher(timestamp);
if (!matcher.find()) {
LOG.error("Invalid Logcat Timestamp " + timestamp);
try (Socket socket = ADB.connect(info.getAdbHost(), info.getAdbPort())) {
Matcher matcher = TIMESTAMP_FORMAT.matcher(timestamp);
if (!matcher.find()) {
LOG.error("Invalid Logcat Timestamp {}", timestamp);
}
String cmd = "logcat -dB -t \"" + timestamp + "\"";
return ADB.execShellCommandRaw(info.getSerial(), cmd, socket.getOutputStream(), socket.getInputStream());
}
String cmd = "logcat -dB -t \"" + timestamp + "\"";
return ADB.execShellCommandRaw(info.getSerial(), cmd, socket.getOutputStream(), socket.getInputStream());
}
/**
* Binary output of logcat -c
*/
public void clearLogcat() throws IOException {
Socket socket = ADB.connect(info.getAdbHost(), info.getAdbPort());
String cmd = "logcat -c";
ADB.execShellCommandRaw(info.getSerial(), cmd, socket.getOutputStream(), socket.getInputStream());
try (Socket socket = ADB.connect(info.getAdbHost(), info.getAdbPort())) {
String cmd = "logcat -c";
ADB.execShellCommandRaw(info.getSerial(), cmd, socket.getOutputStream(), socket.getInputStream());
}
}
/**
* @return Timezone for the attached android device
*/
public String getTimezone() throws IOException {
Socket socket = ADB.connect(info.getAdbHost(), info.getAdbPort());
String cmd = "getprop persist.sys.timezone";
byte[] tz = ADB.execShellCommandRaw(info.getSerial(), cmd, socket.getOutputStream(), socket.getInputStream());
return new String(tz).trim();
try (Socket socket = ADB.connect(info.getAdbHost(), info.getAdbPort())) {
String cmd = "getprop persist.sys.timezone";
byte[] tz = ADB.execShellCommandRaw(info.getSerial(), cmd, socket.getOutputStream(), socket.getInputStream());
if (tz == null) {
throw new IOException("Failed to get timezone");
}
return new String(tz, ADB_CHARSET).trim();
}
}
public String getAndroidReleaseVersion() {
@@ -169,9 +176,10 @@ public class ADBDevice {
}
try {
List<String> list = getProp("ro.build.version.release");
if (list.size() != 0) {
androidReleaseVer = list.get(0);
if (!list.isEmpty()) {
return list.get(0);
}
LOG.error("Failed to get android release version - no result");
} catch (Exception e) {
LOG.error("Failed to get android release version", e);
androidReleaseVer = "";
@@ -180,6 +188,7 @@ public class ADBDevice {
}
public List<String> getProp(String entry) throws IOException {
LOG.debug("ADB getProp({})", entry);
try (Socket socket = ADB.connect(info.getAdbHost(), info.getAdbPort())) {
List<String> props = Collections.emptyList();
String cmd = "getprop";
@@ -190,7 +199,7 @@ public class ADBDevice {
socket.getOutputStream(), socket.getInputStream());
if (payload != null) {
props = new ArrayList<>();
String[] lines = new String(payload).split("\n");
String[] lines = new String(payload, ADB_CHARSET).split("\n");
for (String line : lines) {
line = line.trim();
if (!line.isEmpty()) {
@@ -198,6 +207,7 @@ public class ADBDevice {
}
}
}
LOG.trace("ADB getProp({}) = {}", entry, props);
return props;
}
}
@@ -216,7 +226,8 @@ public class ADBDevice {
byte[] payload = ADB.execShellCommandRaw(info.getSerial(), cmd,
socket.getOutputStream(), socket.getInputStream());
if (payload != null) {
String ps = new String(payload);
String ps = new String(payload, ADB_CHARSET);
// LOG.trace("ADB getProcessList({}) = {}", cmd, ps);
String[] psLines = ps.split("\n");
for (String line : psLines) {
line = line.trim();
@@ -244,12 +255,12 @@ public class ADBDevice {
OutputStream outputStream = jdwpListenerSock.getOutputStream();
if (ADB.setSerial(info.getSerial(), outputStream, inputStream)
&& ADB.execCommandAsync(outputStream, inputStream, CMD_TRACK_JDWP)) {
Executors.newFixedThreadPool(1).execute(() -> {
new Thread(() -> {
for (;;) {
byte[] res = ADB.readServiceProtocol(inputStream);
if (res != null) {
if (listener != null) {
String payload = new String(res);
String payload = new String(res, ADB_CHARSET);
String[] ids = payload.split("\n");
Set<String> idList = new HashSet<>(ids.length);
for (String id : ids) {
@@ -257,6 +268,9 @@ public class ADBDevice {
idList.add(id);
}
}
if (idList.isEmpty()) {
LOG.info("No debuggable app process found on device {}", info.getSerial());
}
listener.jdwpProcessOccurred(this, idList);
}
} else { // socket disconnected
@@ -267,13 +281,13 @@ public class ADBDevice {
this.jdwpListenerSock = null;
listener.jdwpListenerClosed(this);
}
});
}).start();
return true;
} else {
jdwpListenerSock.close();
jdwpListenerSock = null;
return false;
}
return true;
}
public void stopListenForJDWP() {
@@ -279,6 +279,7 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J
@Override
public void onDeviceStatusChange(List<ADBDeviceInfo> deviceInfoList) {
LOG.debug("onDeviceStatusChange {}", deviceInfoList);
List<DeviceNode> nodes = new ArrayList<>(deviceInfoList.size());
info_loop: for (ADBDeviceInfo info : deviceInfoList) {
for (DeviceNode deviceNode : deviceNodes) {
@@ -14,6 +14,7 @@ import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.swing.AbstractAction;
@@ -61,6 +62,10 @@ public class LogcatPanel extends JPanel {
private final AttributeSet fatalAset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.decode("#d33682"));
private final AttributeSet silentAset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.decode("#002b36"));
private final Map<Byte, AttributeSet> asetMap =
Map.of((byte) 1, defaultAset, (byte) 2, verboseAset, (byte) 3, debugAset, (byte) 4, infoAset, (byte) 5, warningAset,
(byte) 6, errorAset, (byte) 7, fatalAset, (byte) 8, silentAset);
private static final ImageIcon ICON_PAUSE = UiUtils.openSvgIcon("debugger/threadFrozen");
private static final ImageIcon ICON_RUN = UiUtils.openSvgIcon("debugger/execute");
private static final ImageIcon CLEAR_LOGCAT = UiUtils.openSvgIcon("debugger/trash");
@@ -199,64 +204,29 @@ public class LogcatPanel extends JPanel {
}
public void log(LogcatController.LogcatInfo logcatInfo) {
boolean atBottom = false;
int len = logcatPane.getDocument().getLength();
JScrollBar scrollbar = logcatScroll.getVerticalScrollBar();
if (isAtBottom(scrollbar)) {
atBottom = true;
boolean atBottom = isAtBottom(scrollbar);
String logString = " > " + logcatInfo.getTimestamp() + " [pid: " + logcatInfo.getPid() + "] "
+ logcatInfo.getMsgTypeString() + ": " + logcatInfo.getMsg() + "\n";
if (logcatInfo.getMsgType() == 0) {
return; // ignore unknown
}
StringBuilder sb = new StringBuilder();
sb.append(" > ")
.append(logcatInfo.getTimestamp())
.append(" [pid: ")
.append(logcatInfo.getPid())
.append("] ")
.append(logcatInfo.getMsgTypeString())
.append(": ")
.append(logcatInfo.getMsg())
.append("\n");
AttributeSet attrSet = asetMap.get(logcatInfo.getMsgType());
try {
switch (logcatInfo.getMsgType()) {
case 0: // Unknown
break;
case 1: // Default
logcatPane.getDocument().insertString(len, sb.toString(), defaultAset);
break;
case 2: // Verbose
logcatPane.getDocument().insertString(len, sb.toString(), verboseAset);
break;
case 3: // Debug
logcatPane.getDocument().insertString(len, sb.toString(), debugAset);
break;
case 4: // Info
logcatPane.getDocument().insertString(len, sb.toString(), infoAset);
break;
case 5: // Warn
logcatPane.getDocument().insertString(len, sb.toString(), warningAset);
break;
case 6: // Error
logcatPane.getDocument().insertString(len, sb.toString(), errorAset);
break;
case 7: // Fatal
logcatPane.getDocument().insertString(len, sb.toString(), fatalAset);
break;
case 8: // Silent
logcatPane.getDocument().insertString(len, sb.toString(), silentAset);
break;
default:
logcatPane.getDocument().insertString(len, sb.toString(), null);
break;
UiUtils.uiRun(() -> {
try {
logcatPane.getDocument().insertString(len, logString, attrSet);
} catch (Exception e) {
LOG.error("Failed to add logcat message", e);
}
} catch (Exception e) {
LOG.error("Failed to write logcat message", e);
}
if (atBottom) {
EventQueue.invokeLater(() -> scrollbar.setValue(scrollbar.getMaximum()));
}
if (atBottom) {
EventQueue.invokeLater(() -> scrollbar.setValue(scrollbar.getMaximum()));
}
});
}
public void exit() {
@@ -282,7 +252,7 @@ public class LogcatPanel extends JPanel {
}
public void actionPerformed(ActionEvent e) {
JComboBox cb = (JComboBox) e.getSource();
JComboBox<?> cb = (JComboBox<?>) e.getSource();
CheckComboStore store = (CheckComboStore) cb.getSelectedItem();
CheckComboRenderer ccr = (CheckComboRenderer) cb.getRenderer();
store.state = !store.state;
@@ -350,11 +320,7 @@ public class LogcatPanel extends JPanel {
public void selectAllBut(int ind) {
for (int i = 0; i < combo.getItemCount(); i++) {
CheckComboStore ccs = combo.getItemAt(i);
if (i != ind) {
ccs.state = false;
} else {
ccs.state = true;
}
ccs.state = (i == ind);
switch (type) {
case 1: // process
logcatController.getFilter().togglePid(ccs.index, ccs.state);