From b4b153b7dcbb6afe1e0c0d6b68b3ba946cbed97d Mon Sep 17 00:00:00 2001 From: benfry Date: Fri, 23 Mar 2012 00:39:33 +0000 Subject: [PATCH] fighting with Android quirks in the latest Google SDK --- android/core/src/processing/core/PImage.java | 9 +- android/todo.txt | 5 + app/src/processing/app/Base.java | 2 +- .../processing/app/exec/ProcessHelper.java | 63 +++++- .../processing/mode/android/AndroidBuild.java | 205 ++++++++++++------ .../mode/android/AndroidRunner.java | 3 + app/src/processing/mode/android/Device.java | 19 +- 7 files changed, 223 insertions(+), 83 deletions(-) diff --git a/android/core/src/processing/core/PImage.java b/android/core/src/processing/core/PImage.java index 2d99cac61..dad152548 100644 --- a/android/core/src/processing/core/PImage.java +++ b/android/core/src/processing/core/PImage.java @@ -524,10 +524,17 @@ public class PImage implements PConstants, Cloneable { h += y; // clip off some of the height y = 0; } - + if (x + w > width) w = width - x; if (y + h > height) h = height - y; + if (w < 0) { + w = 0; + } + if (h < 0) { + h = 0; + } + return getImpl(x, y, w, h); } diff --git a/android/todo.txt b/android/todo.txt index cce7d3372..2a823db61 100644 --- a/android/todo.txt +++ b/android/todo.txt @@ -4,6 +4,11 @@ X http://code.google.com/p/processing/issues/detail?id=899 X http://code.google.com/p/processing/issues/detail?id=769 X /opt/android using version #s again? fix build script (earlier) X smooth() is now the default +X update to Android tools 17 +X add workarounds for problem with tools 17 version of dex +o import statements with android.* break things +X http://code.google.com/p/processing/issues/detail?id=989 +X was already fixed _ now requiring SDK 10 (2.3.3) because of OpenGL issues _ not SDK 9, which is 2.3.1 (and still Gingerbread) diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index 8a3eaec56..e0aa56474 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -57,7 +57,7 @@ public class Base { /** True if heavy debugging error/log messages are enabled */ static public boolean DEBUG = false; - //static public boolean DEBUG = true; +// static public boolean DEBUG = true; static HashMap platformNames = new HashMap(); diff --git a/app/src/processing/app/exec/ProcessHelper.java b/app/src/processing/app/exec/ProcessHelper.java index c1b5c0bdc..3037e75f1 100644 --- a/app/src/processing/app/exec/ProcessHelper.java +++ b/app/src/processing/app/exec/ProcessHelper.java @@ -19,8 +19,8 @@ */ package processing.app.exec; -import java.io.IOException; -import java.io.StringWriter; +import java.io.*; + import processing.core.PApplet; /** @@ -31,13 +31,26 @@ import processing.core.PApplet; */ public class ProcessHelper { private final String[] cmd; +// private final String exe; +// private final String[] args; + private final File dir; + public ProcessHelper(final String... cmd) { this.cmd = cmd; + this.dir = null; } + + public ProcessHelper(File dir, final String... cmd) { + this.cmd = cmd; + this.dir = dir; + } + + @Override public String toString() { + /* final StringBuffer buffer = new StringBuffer(); for (int i = 0; i < cmd.length; i++) { if (i != 0) { @@ -46,8 +59,12 @@ public class ProcessHelper { buffer.append(cmd[i]); } return buffer.toString(); + */ +// return exe + " " + PApplet.join(args, " "); + return PApplet.join(cmd, " "); } + /** * Blocking execution. * @return exit value of process @@ -62,10 +79,13 @@ public class ProcessHelper { final String prettyCommand = toString(); // System.err.println("ProcessHelper: >>>>> " + Thread.currentThread().getId() // + " " + prettyCommand); - final Process process = Runtime.getRuntime().exec(cmd); +// final Process process = Runtime.getRuntime().exec(cmd); + final Process process = dir == null ? + Runtime.getRuntime().exec(cmd) : + Runtime.getRuntime().exec(cmd, new String[] { }, dir); ProcessRegistry.watch(process); try { - String title = PApplet.join(cmd, ' '); + String title = prettyCommand; new StreamPump(process.getInputStream(), "out: " + title).addTarget(outWriter).start(); new StreamPump(process.getErrorStream(), "err: " + title).addTarget(errWriter).start(); try { @@ -85,4 +105,39 @@ public class ProcessHelper { ProcessRegistry.unwatch(process); } } + + +// static public ProcessResult execute(String exe, String[] args, File dir) +// throws InterruptedException, IOException { +// final StringWriter outWriter = new StringWriter(); +// final StringWriter errWriter = new StringWriter(); +// final long startTime = System.currentTimeMillis(); +// +// final String prettyCommand = exe + " " + PApplet.join(args, " "); +// System.out.println("pretty cmd is " + prettyCommand); +// final Process process = dir == null ? +// Runtime.getRuntime().exec(exe, args) : +// Runtime.getRuntime().exec(exe, args, dir); +// ProcessRegistry.watch(process); +// try { +// String title = prettyCommand; +// new StreamPump(process.getInputStream(), "out: " + title).addTarget(outWriter).start(); +// new StreamPump(process.getErrorStream(), "err: " + title).addTarget(errWriter).start(); +// try { +// final int result = process.waitFor(); +// final long time = System.currentTimeMillis() - startTime; +// // System.err.println("ProcessHelper: <<<<< " +// // + Thread.currentThread().getId() + " " + cmd[0] + " (" + time +// // + "ms)"); +// return new ProcessResult(prettyCommand, result, outWriter.toString(), +// errWriter.toString(), time); +// } catch (final InterruptedException e) { +// System.err.println("Interrupted: " + prettyCommand); +// throw e; +// } +// } finally { +// process.destroy(); +// ProcessRegistry.unwatch(process); +// } +// } } \ No newline at end of file diff --git a/app/src/processing/mode/android/AndroidBuild.java b/app/src/processing/mode/android/AndroidBuild.java index a46062e3b..7ea1b8db0 100644 --- a/app/src/processing/mode/android/AndroidBuild.java +++ b/app/src/processing/mode/android/AndroidBuild.java @@ -37,10 +37,10 @@ class AndroidBuild extends JavaBuild { static final String sdkVersion = "10"; // Android 2.3.3 (Gingerbread) static final String sdkTarget = "android-" + sdkVersion; -// private final Editor editor; private final AndroidSDK sdk; private final File coreZipFile; + /** whether this is a "debug" or "release" build */ private String target; private Manifest manifest; @@ -56,14 +56,12 @@ class AndroidBuild extends JavaBuild { sdk = mode.getSDK(); coreZipFile = mode.getCoreZipLocation(); - -// AVD.ensureEclairAVD(sdk); } /** * Build into temporary folders (needed for the Windows 8.3 bugs in the Android SDK). - * @param target + * @param target "debug" or "release" * @throws SketchException * @throws IOException */ @@ -148,29 +146,17 @@ class AndroidBuild extends JavaBuild { // PApplet.saveStream(new File(libsFolder, "processing-core.jar"), input); Base.copyFile(coreZipFile, new File(libsFolder, "processing-core.jar")); -// try { - // Copy any imported libraries or code folder contents to the project + // Copy any imported libraries (their libs and assets), + // and anything in the code folder contents to the project. copyLibraries(libsFolder, assetsFolder); copyCodeFolder(libsFolder); - // Copy the data folder, if one exists, to the 'assets' folder of the - // project + // Copy the data folder (if one exists) to the project's 'assets' folder final File sketchDataFolder = sketch.getDataFolder(); if (sketchDataFolder.exists()) { Base.copyDir(sketchDataFolder, assetsFolder); } -// } catch (final IOException e) { -// e.printStackTrace(); -// throw new SketchException(e.getMessage()); -// } } -// } catch (final SketchException e) { -// editor.statusError(e); -// return null; -// } catch (final IOException e) { -// editor.statusError(e); -// return null; -// } return tmpFolder; } @@ -188,10 +174,9 @@ class AndroidBuild extends JavaBuild { * @throws IOException */ private File createTempBuildFolder(final Sketch sketch) throws IOException { - final File tmp = File.createTempFile("android", ".pde"); + final File tmp = File.createTempFile("android", "sketch"); if (!(tmp.delete() && tmp.mkdir())) { - throw new IOException("Cannot create temp dir " + tmp - + " to build android sketch"); + throw new IOException("Cannot create temp dir " + tmp + " to build android sketch"); } return tmp; } @@ -277,11 +262,85 @@ class AndroidBuild extends JavaBuild { } - /** - * @param target "debug" or "release" - */ + // SDK tools 17 have a problem where 'dex' won't pick up the libs folder + // (which contains our friend processing-core.jar) unless your current + // working directory is the same as the build file. So this is an unpleasant + // workaround, at least until things are fixed or we hear of a better way. protected boolean antBuild() throws SketchException { + try { +// ProcessHelper helper = new ProcessHelper(tmpFolder, new String[] { "ant", target }); +// System.out.println("cp is " + System.getProperty("java.class.path")); + // Since Ant may or may not be installed, call it from the .jar file, + // though hopefully 'java' is in the classpath.. Given what we do in + // processing.mode.java.runner (and it that it works), should be ok. + String[] cmd = new String[] { + "java", + "-cp", System.getProperty("java.class.path"), + "org.apache.tools.ant.Main", target +// "ant", target + }; + ProcessHelper helper = new ProcessHelper(tmpFolder, cmd); + ProcessResult pr = helper.execute(); + if (pr.getResult() != 0) { +// System.err.println("mo builds, mo problems"); + System.err.println(pr.getStderr()); + System.out.println(pr.getStdout()); + // the actual javac errors and whatnot go to stdout + antBuildProblems(pr.getStdout()); + return false; + } + + } catch (InterruptedException e) { + return false; + + } catch (IOException e) { + e.printStackTrace(); + return false; + } + return true; + } + + /* + public class HopefullyTemporaryWorkaround extends org.apache.tools.ant.Main { + + protected void exit(int exitCode) { + // I want to exit, but let's not System.exit() + System.out.println("gonna exit"); + System.out.flush(); + System.err.flush(); + } + } + + + protected boolean antBuild() throws SketchException { + String[] cmd = new String[] { + "-main", "processing.mode.android.HopefullyTemporaryWorkaround", + "-Duser.dir=" + tmpFolder.getAbsolutePath(), + "-logfile", "/Users/fry/Desktop/ant-log.txt", + "-verbose", + "-help", +// "debug" + }; + HopefullyTemporaryWorkaround.main(cmd); + return true; +// ProcessResult listResult = +// new ProcessHelper("ant", "debug", tmpFolder).execute(); +// if (listResult.succeeded()) { +// boolean badness = false; +// for (String line : listResult) { +// } +// } + } + */ + + + protected boolean antBuild_normal() throws SketchException { +// System.setProperty("user.dir", tmpFolder.getAbsolutePath()); // oh why not { because it doesn't help } final Project p = new Project(); +// p.setBaseDir(tmpFolder); // doesn't seem to do anything + +// System.out.println(tmpFolder.getAbsolutePath()); +// p.setUserProperty("user.dir", tmpFolder.getAbsolutePath()); String path = buildFile.getAbsolutePath().replace('\\', '/'); p.setUserProperty("ant.file", path); @@ -295,7 +354,8 @@ class AndroidBuild extends JavaBuild { consoleLogger.setOutputPrintStream(System.out); // ? uncommented before // WARN, INFO, VERBOSE, DEBUG //consoleLogger.setMessageOutputLevel(Project.MSG_ERR); - consoleLogger.setMessageOutputLevel(Project.MSG_INFO); +// consoleLogger.setMessageOutputLevel(Project.MSG_INFO); + consoleLogger.setMessageOutputLevel(Project.MSG_DEBUG); p.addBuildListener(consoleLogger); // This logger is used to pick up javac errors to be parsed into @@ -308,7 +368,8 @@ class AndroidBuild extends JavaBuild { final ByteArrayOutputStream outb = new ByteArrayOutputStream(); final PrintStream outp = new PrintStream(outb); errorLogger.setOutputPrintStream(outp); - errorLogger.setMessageOutputLevel(Project.MSG_INFO); +// errorLogger.setMessageOutputLevel(Project.MSG_INFO); + errorLogger.setMessageOutputLevel(Project.MSG_DEBUG); p.addBuildListener(errorLogger); try { @@ -336,60 +397,64 @@ class AndroidBuild extends JavaBuild { // PApplet.println(errorLines); final String outPile = new String(outb.toByteArray()); - final String[] outLines = outPile.split(System.getProperty("line.separator")); - // PApplet.println(outLines); + antBuildProblems(outPile); + } + return false; + } + + + void antBuildProblems(String outPile) throws SketchException { + final String[] outLines = outPile.split(System.getProperty("line.separator")); +// System.err.println("lines are:"); +// PApplet.println(outLines); - for (final String line : outLines) { - final String javacPrefix = "[javac]"; - final int javacIndex = line.indexOf(javacPrefix); - if (javacIndex != -1) { - // System.out.println("checking: " + line); -// final Sketch sketch = editor.getSketch(); - // String sketchPath = sketch.getFolder().getAbsolutePath(); - int offset = javacIndex + javacPrefix.length() + 1; - String[] pieces = - PApplet.match(line.substring(offset), "^(.+):([0-9]+):\\s+(.+)$"); - if (pieces != null) { - // PApplet.println(pieces); - String fileName = pieces[1]; - // remove the path from the front of the filename - fileName = fileName.substring(fileName.lastIndexOf('/') + 1); - final int lineNumber = PApplet.parseInt(pieces[2]) - 1; - // PApplet.println("looking for " + fileName + " line " + - // lineNumber); - SketchException rex = placeException(pieces[3], fileName, lineNumber); - if (rex != null) { -// rex.hideStackTrace(); -// editor.statusError(rex); -// return false; // get outta here - throw rex; - } + for (final String line : outLines) { + final String javacPrefix = "[javac]"; + final int javacIndex = line.indexOf(javacPrefix); + if (javacIndex != -1) { +// System.out.println("checking: " + line); +// final Sketch sketch = editor.getSketch(); + // String sketchPath = sketch.getFolder().getAbsolutePath(); + int offset = javacIndex + javacPrefix.length() + 1; + String[] pieces = + PApplet.match(line.substring(offset), "^(.+):([0-9]+):\\s+(.+)$"); + if (pieces != null) { +// PApplet.println(pieces); + String fileName = pieces[1]; + // remove the path from the front of the filename + fileName = fileName.substring(fileName.lastIndexOf('/') + 1); + final int lineNumber = PApplet.parseInt(pieces[2]) - 1; +// PApplet.println("looking for " + fileName + " line " + lineNumber); + SketchException rex = placeException(pieces[3], fileName, lineNumber); + if (rex != null) { +// System.out.println("found a rex"); +// rex.hideStackTrace(); +// editor.statusError(rex); +// return false; // get outta here + throw rex; } } } - // this info will have already been printed -// System.err.println("Problem during build: " + e.getMessage()); - // this info is a huge stacktrace of where it died inside the ant code (totally useless) -// e.printStackTrace(); - // Couldn't parse the exception, so send something generic - SketchException skex = - new SketchException("Error from inside the Android tools, check the console."); - skex.hideStackTrace(); - throw skex; } - //return false; + // this info will have already been printed +// System.err.println("Problem during build: " + e.getMessage()); + // this info is a huge stacktrace of where it died inside the ant code (totally useless) +// e.printStackTrace(); + // Couldn't parse the exception, so send something generic + SketchException skex = + new SketchException("Error from inside the Android tools, check the console."); + skex.hideStackTrace(); + throw skex; } -// protected String getClassName() { -// return className; -// } - - String getPathForAPK() { String suffix = target.equals("release") ? "unsigned" : "debug"; String apkName = "bin/" + sketch.getName() + "-" + suffix + ".apk"; final File apkFile = new File(tmpFolder, apkName); + if (!apkFile.exists()) { + return null; + } return apkFile.getAbsolutePath(); } @@ -405,8 +470,6 @@ class AndroidBuild extends JavaBuild { private void writeBuildXML(final File file, final String projectName) { final PrintWriter writer = PApplet.createWriter(file); writer.println(""); - - writer.println(""); diff --git a/app/src/processing/mode/android/AndroidRunner.java b/app/src/processing/mode/android/AndroidRunner.java index 155169274..7244975d5 100644 --- a/app/src/processing/mode/android/AndroidRunner.java +++ b/app/src/processing/mode/android/AndroidRunner.java @@ -71,11 +71,14 @@ public class AndroidRunner implements DeviceListener { // monitor.setNote("Installing sketch on " + device.getId()); listener.statusNotice("Installing sketch on " + device.getId()); + // this stopped working with Android SDK tools revision 17 if (!device.installApp(build.getPathForAPK(), listener)) { listener.statusError("Lost connection with device while installing. Try again."); Devices.killAdbServer(); // see above return; } +// if (!build.antInstall()) { +// } // if (monitor.isCanceled()) { // throw new MonitorCanceled(); diff --git a/app/src/processing/mode/android/Device.java b/app/src/processing/mode/android/Device.java index 58d483f23..5bb32694c 100644 --- a/app/src/processing/mode/android/Device.java +++ b/app/src/processing/mode/android/Device.java @@ -39,8 +39,9 @@ class Device { public void bringLauncherToFront() { try { - adb("shell", "am", "start", "-a", "android.intent.action.MAIN", "-c", - "android.intent.category.HOME"); + adb("shell", "am", "start", + "-a", "android.intent.action.MAIN", + "-c", "android.intent.category.HOME"); } catch (final Exception e) { e.printStackTrace(System.err); } @@ -90,6 +91,7 @@ class Device { return false; } + // different version that actually runs through JDI: // http://asantoso.wordpress.com/2009/09/26/using-jdb-with-adb-to-debugging-of-android-app-on-a-real-device/ public boolean launchApp(final String packageName, final String className) @@ -97,10 +99,15 @@ class Device { if (!isAlive()) { return false; } - ProcessResult pr = adb("shell", "am", "start", "-e", "debug", "true", - "-a", "android.intent.action.MAIN", - "-c", "android.intent.category.LAUNCHER", - "-n", packageName + "/." + className); + String[] cmd = { + "shell", "am", "start", + "-e", "debug", "true", + "-a", "android.intent.action.MAIN", + "-c", "android.intent.category.LAUNCHER", + "-n", packageName + "/." + className + }; +// PApplet.println(cmd); + ProcessResult pr = adb(cmd); if (Base.DEBUG) { System.out.println(pr.toString()); }