mirror of
https://github.com/processing/processing4.git
synced 2026-06-16 04:26:26 +02:00
Now have working AndroidEnvironment, with AndroidDevices that let you listen to stdout and stderr from specific apps.
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
package processing.app.tools.android;
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import processing.app.tools.android.LogEntry.Severity;
|
||||
|
||||
public class AndroidDevice implements AndroidDeviceProperties {
|
||||
private final AndroidEnvironment env;
|
||||
private final String id;
|
||||
private final AndroidProcesses processes;
|
||||
private final Map<String, List<ProcessOutputListener>> outputListeners = new HashMap<String, List<ProcessOutputListener>>();
|
||||
|
||||
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
|
||||
|
||||
// mutable state
|
||||
private Process logcat;
|
||||
|
||||
public AndroidDevice(final AndroidEnvironment env, final String id) {
|
||||
this.env = env;
|
||||
this.id = id;
|
||||
this.processes = new AndroidProcesses(this);
|
||||
}
|
||||
|
||||
// adb shell am start -n com.android.launcher2/.Launcher
|
||||
|
||||
private class LogLineProcessor implements LineProcessor {
|
||||
@Override
|
||||
public void processLine(final String line) {
|
||||
final LogEntry entry = new LogEntry(line);
|
||||
|
||||
final String src = entry.source;
|
||||
final String msg = entry.message;
|
||||
final Severity sev = entry.severity;
|
||||
if (src.equals("ActivityManager") && msg.startsWith("Start proc")) {
|
||||
handleStartProcEntry(entry);
|
||||
} else {
|
||||
if ((src.equals("AndroidRuntime") && sev == Severity.Error)
|
||||
|| src.equals("System.out") || src.equals("System.err")) {
|
||||
final List<ProcessOutputListener> listeners = getListeners(entry.sourcePid);
|
||||
if (listeners != null) {
|
||||
for (final ProcessOutputListener listener : listeners) {
|
||||
if (sev == Severity.Warning || sev == Severity.Error
|
||||
|| sev == Severity.Fatal) {
|
||||
listener.handleStderr(msg);
|
||||
} else {
|
||||
listener.handleStdout(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//System.err.println(entry.source + "/" + entry.message);
|
||||
}
|
||||
}
|
||||
|
||||
public void initialize() throws IOException, InterruptedException {
|
||||
processes.refresh();
|
||||
new ProcessHelper(generateAdbCommand("logcat", "-c")).execute();
|
||||
logcat = Runtime.getRuntime().exec(generateAdbCommand("logcat"));
|
||||
new StreamPump(logcat.getInputStream()).addTarget(new LogLineProcessor())
|
||||
.start();
|
||||
new StreamPump(logcat.getErrorStream()).addTarget(System.err).start();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
for (final PropertyChangeListener pcl : Arrays.asList(pcs
|
||||
.getPropertyChangeListeners())) {
|
||||
pcs.removePropertyChangeListener(pcl);
|
||||
}
|
||||
outputListeners.clear();
|
||||
if (logcat != null) {
|
||||
logcat.destroy();
|
||||
}
|
||||
env.deviceRemoved(this);
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public AndroidEnvironment getEnv() {
|
||||
return env;
|
||||
}
|
||||
|
||||
public List<AndroidProcess> ps() {
|
||||
return processes.getProcesses();
|
||||
}
|
||||
|
||||
public void addOutputListener(final String processName,
|
||||
final ProcessOutputListener listener) {
|
||||
if (!outputListeners.containsKey(processName)) {
|
||||
outputListeners.put(processName, new ArrayList<ProcessOutputListener>());
|
||||
}
|
||||
outputListeners.get(processName).add(listener);
|
||||
}
|
||||
|
||||
public void removeOutputListener(final String processName,
|
||||
final ProcessOutputListener listener) {
|
||||
final List<ProcessOutputListener> listeners = outputListeners
|
||||
.get(processName);
|
||||
if (listeners != null) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Pattern START_PROC = Pattern
|
||||
.compile("^Start proc (\\S+) for \\S+ [^:]+: pid=(\\d+).+$");
|
||||
|
||||
protected List<ProcessOutputListener> getListeners(final String pid) {
|
||||
final AndroidProcess process = processes.byPid(pid);
|
||||
if (process == null) {
|
||||
System.err.println("I don't recognize process id " + pid + "!");
|
||||
return null;
|
||||
}
|
||||
return outputListeners.get(process.name);
|
||||
}
|
||||
|
||||
private void handleStartProcEntry(final LogEntry entry) {
|
||||
final Matcher m = START_PROC.matcher(entry.message);
|
||||
if (m.matches()) {
|
||||
startProc(m.group(1), m.group(2));
|
||||
} else {
|
||||
System.err.println("I don't recognize this start proc message:\n"
|
||||
+ entry.message);
|
||||
}
|
||||
}
|
||||
|
||||
private void startProc(final String name, final String pid) {
|
||||
final AndroidProcess proc = new AndroidProcess(pid, name);
|
||||
processes.refresh();
|
||||
firePropertyChange(APP_STARTED, null, proc);
|
||||
}
|
||||
|
||||
private void endProc(final String pid) {
|
||||
final AndroidProcess proc = processes.byPid(pid);
|
||||
if (proc == null) {
|
||||
System.err.println("Process " + pid
|
||||
+ " ended, but I hadn't known about it.");
|
||||
} else {
|
||||
processes.refresh();
|
||||
firePropertyChange(APP_ENDED, proc, null);
|
||||
}
|
||||
}
|
||||
|
||||
String[] generateAdbCommand(final String... cmd) {
|
||||
final String[] adbCmd = new String[3 + cmd.length];
|
||||
adbCmd[0] = "adb";
|
||||
adbCmd[1] = "-s";
|
||||
adbCmd[2] = getId();
|
||||
System.arraycopy(cmd, 0, adbCmd, 3, cmd.length);
|
||||
return adbCmd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[AndroidDevice " + getId() + "]";
|
||||
}
|
||||
|
||||
public void addPropertyChangeListener(final PropertyChangeListener listener) {
|
||||
pcs.addPropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
private void firePropertyChange(final String propertyName,
|
||||
final Object oldValue, final Object newValue) {
|
||||
pcs.firePropertyChange(propertyName, oldValue, newValue);
|
||||
}
|
||||
|
||||
public void removePropertyChangeListener(final PropertyChangeListener listener) {
|
||||
pcs.removePropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package processing.app.tools.android;
|
||||
|
||||
public interface AndroidDeviceProperties {
|
||||
public static final String APP_STARTED = "android.device.app.started";
|
||||
public static final String APP_ENDED = "android.device.app.ended";
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
package processing.app.tools.android;
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* <p>An AndroidEnvironment is an object that periodically polls for the existence
|
||||
* of running Android devices, both emulated and hardware. You can register to
|
||||
* be notified when devices are added to or removed from the environment. You
|
||||
* can also ask for an emulator or a hardware device specifically.
|
||||
*
|
||||
* <pre> AndroidEnvironment env = new AndroidEnvironment();
|
||||
* env.addPropertyChangeListener(...);
|
||||
* env.initialize();
|
||||
*
|
||||
* AndroidDevice n1 = env.getHardware();</pre>
|
||||
*
|
||||
* @author Jonathan Feinberg <jdf@us.ibm.com>
|
||||
*
|
||||
*/
|
||||
public class AndroidEnvironment implements AndroidEnvironmentProperties {
|
||||
private final Map<String, AndroidDevice> devices = new ConcurrentHashMap<String, AndroidDevice>();
|
||||
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
|
||||
private final Timer refreshTimer = new Timer("AndroidEnvironment refresher");
|
||||
|
||||
public AndroidEnvironment() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Cause this model of the Android environment to begin discovering
|
||||
* devices. If you wish to be informed about device discovery, make
|
||||
* sure you register yourself as a listener before calling this
|
||||
* method.
|
||||
*
|
||||
* This method starts up a thread that must be nuked via the
|
||||
* shutdown() method in order for your app to exit cleanly.
|
||||
*/
|
||||
public void initialize() {
|
||||
for (final String deviceId : listDevices()) {
|
||||
addDevice(new AndroidDevice(this, deviceId));
|
||||
}
|
||||
refreshTimer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
refresh();
|
||||
}
|
||||
}, TimeUnit.SECONDS.toMillis(2), TimeUnit.SECONDS.toMillis(2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn off the environment's device-discovery polling.
|
||||
*/
|
||||
public void shutdown() {
|
||||
refreshTimer.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the first Android emulator known to be running, or null if there are none.
|
||||
*/
|
||||
public AndroidDevice getEmulator() {
|
||||
for (final AndroidDevice device : devices.values()) {
|
||||
if (device.getId().contains("emulator")) {
|
||||
return device;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the first Android hardware device known to be running, or null if there are none.
|
||||
*/
|
||||
public AndroidDevice getHardware() {
|
||||
for (final AndroidDevice device : devices.values()) {
|
||||
if (!device.getId().contains("emulator")) {
|
||||
return device;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
final List<String> activeDevices = listDevices();
|
||||
for (final String deviceId : activeDevices) {
|
||||
if (!devices.containsKey(deviceId)) {
|
||||
addDevice(new AndroidDevice(this, deviceId));
|
||||
}
|
||||
}
|
||||
for (final String deviceId : new ArrayList<String>(devices.keySet())) {
|
||||
if (!activeDevices.contains(deviceId)) {
|
||||
devices.get(deviceId).shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addDevice(final AndroidDevice device) {
|
||||
if (devices.put(device.getId(), device) != null) {
|
||||
throw new IllegalStateException("Adding " + device
|
||||
+ ", which already exists!");
|
||||
}
|
||||
try {
|
||||
device.initialize();
|
||||
} catch (final Exception e) {
|
||||
System.err.println("Cannot initialize " + device + ": " + e);
|
||||
devices.remove(device.getId());
|
||||
}
|
||||
firePropertyChange(DEVICE_ADDED, null, device);
|
||||
}
|
||||
|
||||
void deviceRemoved(final AndroidDevice device) {
|
||||
if (devices.remove(device.getId()) == null) {
|
||||
throw new IllegalStateException("I didn't know about device "
|
||||
+ device.getId() + "!");
|
||||
}
|
||||
firePropertyChange(DEVICE_REMOVED, device, null);
|
||||
}
|
||||
|
||||
public void addPropertyChangeListener(final PropertyChangeListener listener) {
|
||||
pcs.addPropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
public void firePropertyChange(final String propertyName,
|
||||
final Object oldValue, final Object newValue) {
|
||||
pcs.firePropertyChange(propertyName, oldValue, newValue);
|
||||
}
|
||||
|
||||
public void removePropertyChangeListener(final PropertyChangeListener listener) {
|
||||
pcs.removePropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
static final String ADB_DEVICES_ERROR = "Received unfamiliar output from “adb devices”.\n"
|
||||
+ "The device list may have errors.";
|
||||
|
||||
/**
|
||||
* <p>First line starts "List of devices"
|
||||
|
||||
<p>When an emulator is started with a debug port, then it shows up
|
||||
in the list of devices.
|
||||
|
||||
<p>List of devices attached
|
||||
<br>HT91MLC00031 device
|
||||
<br>emulator-5554 offline
|
||||
|
||||
<p>List of devices attached
|
||||
<br>HT91MLC00031 device
|
||||
<br>emulator-5554 device
|
||||
|
||||
* @return list of device identifiers
|
||||
* @throws IOException
|
||||
*/
|
||||
private static List<String> listDevices() {
|
||||
ProcessResult result;
|
||||
try {
|
||||
result = new ProcessHelper("adb", "devices").execute();
|
||||
} catch (final InterruptedException e) {
|
||||
return Collections.emptyList();
|
||||
} catch (final IOException e) {
|
||||
System.err.println(e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (!result.succeeded()) {
|
||||
System.err.println(result);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// might read "List of devices attached"
|
||||
if (!result.getStdout().startsWith("List of devices")) {
|
||||
System.err.println(ADB_DEVICES_ERROR);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final List<String> devices = new ArrayList<String>();
|
||||
for (final String line : result) {
|
||||
if (!line.contains("\t")) {
|
||||
continue;
|
||||
}
|
||||
devices.add(line.split("\t")[0]);
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package processing.app.tools.android;
|
||||
|
||||
public interface AndroidEnvironmentProperties {
|
||||
public static final String DEVICE_REMOVED = "android.device.removed";
|
||||
public static final String DEVICE_ADDED = "android.device.added";
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package processing.app.tools.android;
|
||||
|
||||
public class AndroidProcess {
|
||||
public final String pid;
|
||||
public final String name;
|
||||
|
||||
public AndroidProcess(final String pid, final String name) {
|
||||
if (pid == null) {
|
||||
throw new IllegalArgumentException("null pid");
|
||||
}
|
||||
if (name == null) {
|
||||
throw new IllegalArgumentException("null name");
|
||||
}
|
||||
this.pid = pid;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name + " (" + pid + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (!(obj instanceof AndroidProcess)) {
|
||||
return false;
|
||||
}
|
||||
final AndroidProcess o = (AndroidProcess) obj;
|
||||
return pid.equals(o.pid) && name.equals(o.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return pid.hashCode() * 17 + name.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package processing.app.tools.android;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class AndroidProcesses {
|
||||
private final List<AndroidProcess> processes = new ArrayList<AndroidProcess>();
|
||||
private final AndroidDevice device;
|
||||
|
||||
public AndroidProcesses(final AndroidDevice device) {
|
||||
this.device = device;
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
processes.clear();
|
||||
try {
|
||||
for (final String line : new ProcessHelper(this.device
|
||||
.generateAdbCommand("shell", "ps")).execute()) {
|
||||
final String[] fields = line.split("\\s+");
|
||||
if (fields.length != 9 || fields[0].equals("USER")) {
|
||||
continue;
|
||||
}
|
||||
final String pid = fields[1];
|
||||
final String name = fields[8];
|
||||
processes.add(new AndroidProcess(pid, name));
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
System.err.println("Unable to refresh processes on " + device + ":");
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
List<AndroidProcess> getProcesses() {
|
||||
return Collections.unmodifiableList(processes);
|
||||
}
|
||||
|
||||
public AndroidProcess byPid(final String pid) {
|
||||
for (final AndroidProcess p : processes) {
|
||||
if (p.pid.equals(pid)) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public AndroidProcess byName(final String name) {
|
||||
for (final AndroidProcess p : processes) {
|
||||
if (p.name.equals(name)) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void add(final AndroidProcess androidProcess) {
|
||||
processes.add(androidProcess);
|
||||
}
|
||||
}
|
||||
@@ -133,19 +133,6 @@ public class AndroidRunner extends Runner {
|
||||
@Override
|
||||
protected void generateTrace(final PrintWriter writer) {
|
||||
|
||||
final Thread logcatter = new Thread(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
new SketchLogCatter().start();
|
||||
} catch (final InterruptedException e) {
|
||||
System.err.println("logcat interrupted");
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}, "logcatter");
|
||||
logcatter.start();
|
||||
|
||||
// At this point, disable the run button.
|
||||
// This happens when the sketch is exited by hitting ESC,
|
||||
// or the user manually closes the sketch window.
|
||||
|
||||
@@ -716,7 +716,6 @@ public class AndroidTool implements Tool {
|
||||
final boolean emu = device.startsWith("emulator");
|
||||
editor.statusNotice("Sketch started on the "
|
||||
+ (emu ? "emulator" : "phone") + ".");
|
||||
new AndroidRunner(editor, editor.getSketch()).launch(ADB_SOCKET_PORT);
|
||||
} else {
|
||||
editor.statusError("Could not start the sketch.");
|
||||
System.err.println(result);
|
||||
|
||||
@@ -205,8 +205,6 @@ public class Device {
|
||||
// }
|
||||
|
||||
if (lines.length == 0) {
|
||||
// result was 0, so we're ok, but this is odd.
|
||||
System.out.println("No devices found.");
|
||||
return new String[] {};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package processing.app.tools.android;
|
||||
|
||||
public class EmulatorController {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package processing.app.tools.android;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
|
||||
public class Harness implements AndroidEnvironmentProperties,
|
||||
AndroidDeviceProperties {
|
||||
public static void main(final String[] args) throws Exception {
|
||||
final AndroidEnvironment env = new AndroidEnvironment();
|
||||
env.addPropertyChangeListener(PCD);
|
||||
env.addPropertyChangeListener(new PropertyChangeListener() {
|
||||
@Override
|
||||
public void propertyChange(final PropertyChangeEvent evt) {
|
||||
if (evt.getPropertyName().equals(DEVICE_ADDED)) {
|
||||
final AndroidDevice device = (AndroidDevice) evt.getNewValue();
|
||||
device.addOutputListener("processing.android.test.testconsole",
|
||||
new ProcessOutputListener() {
|
||||
@Override
|
||||
public void handleStdout(final String line) {
|
||||
System.out.println("testconsole: " + line);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleStderr(final String line) {
|
||||
System.err.println("testconsole: " + line);
|
||||
}
|
||||
});
|
||||
|
||||
device.addOutputListener("processing.android.test.testmouse",
|
||||
new ProcessOutputListener() {
|
||||
@Override
|
||||
public void handleStdout(final String line) {
|
||||
System.out.println("testmouse: " + line);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleStderr(final String line) {
|
||||
System.err.println("testmouse: " + line);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
env.initialize();
|
||||
}
|
||||
|
||||
private static final PropertyChangeListener PCD = new PropertyChangeListener() {
|
||||
public void propertyChange(final PropertyChangeEvent evt) {
|
||||
System.err.println(evt.getSource());
|
||||
System.err.println(" " + evt.getPropertyName());
|
||||
System.err.println(" old: " + evt.getOldValue());
|
||||
System.err.println(" new: " + evt.getNewValue());
|
||||
System.err.println();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package processing.app.tools.android;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class LogEntry {
|
||||
public static enum Severity {
|
||||
Verbose, Debug, Info, Warning, Error, Fatal;
|
||||
static Severity fromChar(final char c) {
|
||||
if (c == 'V') {
|
||||
return Verbose;
|
||||
} else if (c == 'D') {
|
||||
return Debug;
|
||||
} else if (c == 'I') {
|
||||
return Info;
|
||||
} else if (c == 'W') {
|
||||
return Warning;
|
||||
} else if (c == 'E') {
|
||||
return Error;
|
||||
} else if (c == 'F') {
|
||||
return Fatal;
|
||||
} else {
|
||||
throw new IllegalArgumentException("I don't know how to interpret '"
|
||||
+ c + "' as a log severity");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final Severity severity;
|
||||
public final String source;
|
||||
public final String sourcePid;
|
||||
public final String message;
|
||||
|
||||
private static final Pattern PARSER = Pattern
|
||||
.compile("^([VDIWEF])/([^\\(]+)\\(\\s*(\\d+)\\):\\s*(.+)$");
|
||||
|
||||
public LogEntry(final String line) {
|
||||
final Matcher m = PARSER.matcher(line);
|
||||
if (!m.matches()) {
|
||||
throw new RuntimeException("I can't understand log entry\n" + line);
|
||||
}
|
||||
this.severity = Severity.fromChar(m.group(1).charAt(0));
|
||||
this.source = m.group(2);
|
||||
this.sourcePid = m.group(3);
|
||||
this.message = m.group(4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return severity + "/" + source + "(" + sourcePid + "): " + message;
|
||||
}
|
||||
}
|
||||
@@ -91,9 +91,13 @@ class ProcessHelper {
|
||||
errpump.addTarget(System.err);
|
||||
}
|
||||
errpump.start();
|
||||
|
||||
return new ProcessResult(getCommand(), process.waitFor(), outWriter
|
||||
.toString(), errWriter.toString(), System.currentTimeMillis()
|
||||
- startTime);
|
||||
try {
|
||||
return new ProcessResult(getCommand(), process.waitFor(), outWriter
|
||||
.toString(), errWriter.toString(), System.currentTimeMillis()
|
||||
- startTime);
|
||||
} catch (final InterruptedException e) {
|
||||
System.err.println("Interrupted: " + getCommand());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package processing.app.tools.android;
|
||||
|
||||
public interface ProcessOutputListener {
|
||||
public void handleStdout(final String line);
|
||||
|
||||
public void handleStderr(final String line);
|
||||
}
|
||||
@@ -96,6 +96,5 @@ class StreamPump implements Runnable {
|
||||
public void processLine(final String line) {
|
||||
writer.println(line);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user