adding export, cleaning up

This commit is contained in:
benfry
2010-04-13 22:44:56 +00:00
parent cb05e282e1
commit bb5e072a9e
3 changed files with 235 additions and 71 deletions
@@ -22,22 +22,16 @@
package processing.app.tools.android;
import java.awt.Frame;
import java.io.File;
import java.io.*;
import java.net.URL;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import processing.app.Base;
import processing.app.Editor;
import processing.app.Sketch;
import processing.app.debug.Runner;
import processing.app.debug.RunnerException;
import java.util.*;
import java.util.concurrent.*;
import java.util.regex.*;
import processing.app.*;
import processing.app.debug.*;
import processing.app.tools.Tool;
import processing.core.PApplet;
// http://dl.google.com/android/repository/repository.xml
@@ -118,17 +112,17 @@ public class AndroidTool implements Tool, DeviceListener {
return true;
}
public Editor getEditor() {
return editor;
}
// public Editor getEditor() {
// return editor;
// }
public Sketch getSketch() {
return editor.getSketch();
}
// public Sketch getSketch() {
// return editor.getSketch();
// }
public Build getBuilder() {
return build;
}
// public Build getBuilder() {
// return build;
// }
// if user asks for 480x320, 320x480, 854x480 etc, then launch like that
// though would need to query the emulator to see if it can do that
@@ -165,11 +159,12 @@ public class AndroidTool implements Tool, DeviceListener {
} catch (final TimeoutException expected) {
}
}
editor
.statusError("No, on second thought, I'm giving up on waiting for that device to show up.");
editor.statusError("No, on second thought, I'm giving up " +
"on waiting for that device to show up.");
return null;
}
private volatile AndroidDevice lastRunDevice = null;
private void runSketchOnDevice(final Future<AndroidDevice> deviceFuture)
@@ -179,8 +174,8 @@ public class AndroidTool implements Tool, DeviceListener {
"Building and launching...",
"Creating project...");
try {
final Build build = getBuilder();
if (!build.createProject()) {
// final Build build = getBuilder();
if (build.createProject() != null) {
return;
}
try {
@@ -232,6 +227,119 @@ public class AndroidTool implements Tool, DeviceListener {
monitor.close();
}
}
private void buildReleaseForExport() throws Cancelled {
final IndeterminateProgressMonitor monitor =
new IndeterminateProgressMonitor(editor,
"Building and exporting...",
"Creating project...");
try {
File tempFolder = build.createProject();
if (tempFolder == null) {
return;
}
try {
if (monitor.isCanceled()) {
throw new Cancelled();
}
monitor.setNote("Building release version...");
if (!build.antBuild("release")) {
return;
}
System.out.println("build like.");
if (monitor.isCanceled()) {
throw new Cancelled();
}
// If things built successfully, copy the contents to the export folder
File exportFolder = build.createExportFolder();
// Base.openFolder(tempFolder);
// System.out.println("folder will be " + exportFolder);
if (exportFolder != null) {
Base.copyDir(tempFolder, exportFolder);
editor.statusNotice("Done with export.");
Base.openFolder(exportFolder);
} else {
System.err.println("bad copy");
editor.statusError("Could not copy files to export folder.");
}
} catch (IOException e) {
System.err.println("bad copy ioe");
editor.statusError(e);
} finally {
build.cleanup();
}
} finally {
monitor.close();
}
}
private void buildReleaseForDevice(final Future<AndroidDevice> deviceFuture) throws Cancelled {
final IndeterminateProgressMonitor monitor =
new IndeterminateProgressMonitor(editor,
"Building and running...",
"Creating project...");
try {
File tempFolder = build.createProject();
if (tempFolder == null) {
return;
}
try {
if (monitor.isCanceled()) {
throw new Cancelled();
}
monitor.setNote("Building...");
if (!build.antBuild("release")) {
return;
}
if (monitor.isCanceled()) {
throw new Cancelled();
}
monitor.setNote("Waiting for device to become available...");
final AndroidDevice device = waitForDevice(deviceFuture, monitor);
if (device == null || !device.isAlive()) {
editor.statusError("Device killed or disconnected.");
return;
}
device.addListener(this);
if (monitor.isCanceled()) {
throw new Cancelled();
}
monitor.setNote("Installing sketch on " + device.getId());
if (!device.installApp(build.getPathForAPK("debug"), editor)) {
editor.statusError("Device killed or disconnected.");
return;
}
if (monitor.isCanceled()) {
throw new Cancelled();
}
monitor.setNote("Starting sketch on " + device.getId());
if (startSketch(device)) {
editor.statusNotice("Sketch launched on the "
+ (device.isEmulator() ? "emulator" : "phone") + ".");
} else {
editor.statusError("Could not start the sketch.");
}
lastRunDevice = device;
} finally {
build.cleanup();
}
} finally {
monitor.close();
}
}
private static final Pattern LOCATION =
Pattern.compile("\\(([^:]+):(\\d+)\\)");
@@ -323,9 +431,15 @@ public class AndroidTool implements Tool, DeviceListener {
/**
* Create a release build of the sketch and have its apk files ready.
* If users want a debug build, they can do that from the command line.
*/
private static class ExportHandler implements Runnable {
private class ExportHandler implements Runnable {
public void run() {
try {
buildReleaseForExport();
} catch (final Cancelled ok) {
editor.statusNotice("Cancelled.");
}
}
}
@@ -333,8 +447,13 @@ public class AndroidTool implements Tool, DeviceListener {
* Create a release build of the sketch and install its apk files on the
* attached device.
*/
private static class ExportAppHandler implements Runnable {
private class ExportAppHandler implements Runnable {
public void run() {
try {
buildReleaseForDevice(AndroidEnvironment.getInstance().getHardware());
} catch (final Cancelled ok) {
editor.statusNotice("Cancelled.");
}
}
}
@@ -1,27 +1,18 @@
package processing.app.tools.android;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.List;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DefaultLogger;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectHelper;
import processing.app.Base;
import processing.app.Editor;
import processing.app.Preferences;
import processing.app.Sketch;
import java.util.*;
import org.apache.tools.ant.*;
import processing.app.*;
import processing.app.exec.*;
import processing.app.debug.RunnerException;
import processing.app.preproc.PdePreprocessor;
import processing.core.PApplet;
class Build {
static SimpleDateFormat dateFormat = new SimpleDateFormat("yyMMdd.HHmm");
@@ -32,7 +23,7 @@ class Build {
String className;
File androidFolder;
File tempBuildFolder;
File buildFile;
@@ -44,6 +35,7 @@ class Build {
}
// TODO this needs to be a generic function inside Sketch or elsewhere
/*
protected int[] getSketchSize() {
int wide = AVD.DEFAULT_WIDTH;
int high = AVD.DEFAULT_HEIGHT;
@@ -81,19 +73,21 @@ class Build {
}
return new int[] { wide, high };
}
*/
public boolean createProject() {
public File createProject() {
final Sketch sketch = editor.getSketch();
try {
androidFolder = createAndroidBuildFolder(sketch);
tempBuildFolder = createTempBuildFolder(sketch);
} catch (final IOException e) {
editor.statusError(e);
return false;
return null;
}
// Create the 'src' folder with the preprocessed code.
final File srcFolder = new File(androidFolder, "src");
final File srcFolder = new File(tempBuildFolder, "src");
try {
final File javaFolder = mkdirs(srcFolder, getPackageName().replace('.',
@@ -111,17 +105,17 @@ class Build {
sketch.prepare();
className = sketch.preprocess(buildPath, new Preproc(sketch.getName()));
if (className != null) {
final File androidXML = new File(androidFolder, "AndroidManifest.xml");
final File androidXML = new File(tempBuildFolder, "AndroidManifest.xml");
writeAndroidManifest(androidXML, sketch.getName(), className);
writeBuildProps(new File(androidFolder, "build.properties"));
buildFile = new File(androidFolder, "build.xml");
writeBuildProps(new File(tempBuildFolder, "build.properties"));
buildFile = new File(tempBuildFolder, "build.xml");
writeBuildXML(buildFile, sketch.getName());
writeDefaultProps(new File(androidFolder, "default.properties"));
writeLocalProps(new File(androidFolder, "local.properties"));
writeRes(new File(androidFolder, "res"), className);
writeDefaultProps(new File(tempBuildFolder, "default.properties"));
writeLocalProps(new File(tempBuildFolder, "local.properties"));
writeRes(new File(tempBuildFolder, "res"), className);
final File libsFolder = mkdirs(androidFolder, "libs");
final File assetsFolder = mkdirs(androidFolder, "assets");
final File libsFolder = mkdirs(tempBuildFolder, "libs");
final File assetsFolder = mkdirs(tempBuildFolder, "assets");
final InputStream input = PApplet.createInput(AndroidTool
.getCoreZipFile());
@@ -144,18 +138,19 @@ class Build {
}
} catch (final RunnerException e) {
editor.statusError(e);
return false;
return null;
} catch (final IOException e) {
editor.statusError(e);
return false;
return null;
}
return true;
return tempBuildFolder;
}
/**
* The Android dex util pukes on paths containing spaces, which will happen
* most of the time on Windows, since Processing sketches wind up in
* "My Documents". Therefore, build android in a temp file.
* http://code.google.com/p/android/issues/detail?id=4567
*
* TODO: better would be to retrieve the 8.3 name for the sketch folder!
*
@@ -163,7 +158,7 @@ class Build {
* @return A folder in which to build the android sketch
* @throws IOException
*/
private File createAndroidBuildFolder(final Sketch sketch) throws IOException {
private File createTempBuildFolder(final Sketch sketch) throws IOException {
final File tmp = File.createTempFile("android", ".pde");
if (!(tmp.delete() && tmp.mkdir())) {
throw new IOException("Cannot create temp dir " + tmp
@@ -171,15 +166,64 @@ class Build {
}
return tmp;
}
protected File createExportFolder() throws IOException {
Sketch sketch = editor.getSketch();
// Create the 'android' build folder, and move any existing version out.
File androidFolder = new File(sketch.getFolder(), "android");
if (androidFolder.exists()) {
Date mod = new Date(androidFolder.lastModified());
File dest = new File(sketch.getFolder(), "android." + dateFormat.format(mod));
boolean result = androidFolder.renameTo(dest);
if (!result) {
ProcessHelper mv;
ProcessResult pr;
try {
System.err.println("createProject renameTo() failed, resorting to mv/move instead.");
mv = new ProcessHelper("mv", androidFolder.getAbsolutePath(), dest.getAbsolutePath());
pr = mv.execute();
} catch (IOException e) {
editor.statusError(e);
return null;
} catch (InterruptedException e) {
e.printStackTrace();
return null;
}
if (!pr.succeeded()) {
System.err.println(pr.getStderr());
Base.showWarning("Failed to rename",
"Could not rename the old “android” build folder.\n" +
"Please delete, close, or rename the folder\n" +
androidFolder.getAbsolutePath() + "\n" +
"and try again." , null);
Base.openFolder(sketch.getFolder());
return null;
}
}
} else {
boolean result = androidFolder.mkdirs();
if (!result) {
Base.showWarning("Folders, folders, folders",
"Could not create the necessary folders to build.\n" +
"Perhaps you have some file permissions to sort out?", null);
return null;
}
}
return androidFolder;
}
/**
* @param target
* "debug" or "release"
* @param target "debug" or "release"
*/
boolean antBuild(final String target) {
final Project p = new Project();
p.setUserProperty("ant.file", buildFile.getAbsolutePath()
.replace('\\', '/'));
String path = buildFile.getAbsolutePath().replace('\\', '/');
p.setUserProperty("ant.file", path);
// deals with a problem where javac error messages weren't coming through
p.setUserProperty("build.compiler", "extJavac");
@@ -319,7 +363,7 @@ class Build {
String getPathForAPK(final String target) {
final Sketch sketch = editor.getSketch();
final File apkFile = new File(androidFolder, "bin/" + sketch.getName()
final File apkFile = new File(tempBuildFolder, "bin/" + sketch.getName()
+ "-" + target + ".apk");
return apkFile.getAbsolutePath();
}
@@ -365,8 +409,8 @@ class Build {
//"java.util.zip.*", "java.util.regex.*" // not necessary w/ newer i/o
};
Preferences.set("android.preproc.imports.list", PApplet.join(
androidImports, ","));
Preferences.set("android.preproc.imports.list",
PApplet.join(androidImports, ","));
return androidImports;
}
@@ -606,7 +650,7 @@ class Build {
}
public void cleanup() {
rm(androidFolder);
rm(tempBuildFolder);
}
private void rm(final File f) {
@@ -58,6 +58,7 @@ class EmulatorController {
// beginning execution of the emulator, so we are now officially "Launched"
setState(State.WAITING_FOR_BOOT);
// TODO need to suppress this warning on OS X
// 2010-04-13 15:26:56.380 emulator[91699:903] Warning once: This
// application, or a library it uses, is using NSQuickDrawView, which has
// been deprecated. Apps should cease use of QuickDraw and move to Quartz.