diff --git a/app/.classpath b/app/.classpath
index 26775c499..a7fecb0e5 100644
--- a/app/.classpath
+++ b/app/.classpath
@@ -16,5 +16,7 @@
+
+
diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java
index 77143ce59..09d0db665 100644
--- a/app/src/processing/app/Base.java
+++ b/app/src/processing/app/Base.java
@@ -29,6 +29,9 @@ import java.util.*;
import javax.swing.*;
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+
import processing.app.debug.Compiler;
import processing.core.*;
@@ -98,27 +101,8 @@ public class Base {
// ArrayList editors = Collections.synchronizedList(new ArrayList());
Editor activeEditor;
-// int nextEditorX;
-// int nextEditorY;
-
-// import com.sun.jna.Library;
-// import com.sun.jna.Native;
-
-// public interface CLibrary extends Library {
-// CLibrary INSTANCE = (CLibrary)Native.loadLibrary("c", CLibrary.class);
-// int setenv(String name, String value, int overwrite);
-// String getenv(String name);
-// int unsetenv(String name);
-// int putenv(String string);
-// }
-
-
+
static public void main(String args[]) {
-// /Users/fry/coconut/sketchbook/libraries/gsvideo/library
-// CLibrary clib = CLibrary.INSTANCE;
-// clib.setenv("DYLD_LIBRARY_PATH", "/Users/fry/coconut/sketchbook/libraries/gsvideo/library", 1);
-// System.out.println("env is now " + clib.getenv("DYLD_LIBRARY_PATH"));
-
try {
File versionFile = getContentFile("lib/version.txt");
if (versionFile.exists()) {
@@ -184,10 +168,12 @@ public class Base {
try {
platform.setLookAndFeel();
} catch (Exception e) {
- System.err.println("Non-fatal error while setting the Look & Feel.");
- System.err.println("The error message follows, however Processing should run fine.");
- System.err.println(e.getMessage());
- //e.printStackTrace();
+ String mess = e.getMessage();
+ if (mess.indexOf("ch.randelshofer.quaqua.QuaquaLookAndFeel") == -1) {
+ System.err.println("Non-fatal error while setting the Look & Feel.");
+ System.err.println("The error message follows, however Processing should run fine.");
+ System.err.println(mess);
+ }
}
// Create a location for untitled sketches
@@ -1775,6 +1761,33 @@ public class Base {
// ...................................................................
+ public interface CLibrary extends Library {
+ CLibrary INSTANCE = (CLibrary)Native.loadLibrary("c", CLibrary.class);
+ int setenv(String name, String value, int overwrite);
+ String getenv(String name);
+ int unsetenv(String name);
+ int putenv(String string);
+ }
+
+
+ static public void setenv(String variable, String value) {
+ CLibrary clib = CLibrary.INSTANCE;
+ clib.setenv(variable, value, 1);
+ }
+
+
+ static public String getenv(String variable) {
+ CLibrary clib = CLibrary.INSTANCE;
+ return clib.getenv(variable);
+ }
+
+
+ static public int unsetenv(String variable) {
+ CLibrary clib = CLibrary.INSTANCE;
+ return clib.unsetenv(variable);
+ }
+
+
/**
* Get the number of lines in a file by counting the number of newline
* characters inside a String (and adding 1).
diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java
index b052bf7f0..57ff09687 100644
--- a/app/src/processing/app/Editor.java
+++ b/app/src/processing/app/Editor.java
@@ -115,7 +115,7 @@ public class Editor extends JFrame implements RunnerListener {
JMenuItem saveAsMenuItem;
boolean running;
- boolean presenting;
+ //boolean presenting;
// undo fellers
JMenuItem undoItem, redoItem;
@@ -126,6 +126,11 @@ public class Editor extends JFrame implements RunnerListener {
CompoundEdit compoundEdit;
FindReplace find;
+
+ Runnable runHandler;
+ Runnable presentHandler;
+ Runnable exportHandler;
+ Runnable exportAppHandler;
public Editor(Base ibase, String path, int[] location) {
@@ -134,6 +139,9 @@ public class Editor extends JFrame implements RunnerListener {
Base.setIcon(this);
+ // Install default actions for Run, Present, etc.
+ resetHandlers();
+
// add listener to handle window close box hit event
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
@@ -784,12 +792,10 @@ public class Editor extends JFrame implements RunnerListener {
menu.add(createToolMenuItem("processing.app.tools.Archiver"));
menu.add(createToolMenuItem("processing.app.tools.FixEncoding"));
- /*
//menu.add(createToolMenuItem("processing.app.tools.android.Build"));
- item = createToolMenuItem("processing.app.tools.android.Build");
+ item = createToolMenuItem("processing.app.tools.android.Android");
item.setAccelerator(KeyStroke.getKeyStroke('D', modifiers));
menu.add(item);
- */
return menu;
}
@@ -1144,6 +1150,32 @@ public class Editor extends JFrame implements RunnerListener {
}
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+ // these will be done in a more generic way soon, more like:
+ // setHandler("action name", Runnable);
+ // but for the time being, working out the kinks of how many things to
+ // abstract from the editor in this fashion.
+
+
+ public void setHandlers(Runnable runHandler, Runnable presentHandler,
+ Runnable exportHandler, Runnable exportAppHandler) {
+ this.runHandler = runHandler;
+ this.presentHandler = presentHandler;
+ this.exportHandler = exportHandler;
+ this.exportAppHandler = exportAppHandler;
+ }
+
+
+ public void resetHandlers() {
+ runHandler = new DefaultRunHandler();
+ presentHandler = new DefaultPresentHandler();
+ exportHandler = new DefaultExportHandler();
+ exportAppHandler = new DefaultExportAppHandler();
+ }
+
+
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
@@ -1578,28 +1610,42 @@ public class Editor extends JFrame implements RunnerListener {
console.clear();
}
- presenting = present;
-
- try {
- String appletClassName = sketch.compile();
- if (appletClassName != null) {
- runtime = new Runner(sketch, appletClassName, presenting, Editor.this);
-
- // Cannot use invokeLater() here, otherwise it gets
- // placed on the event thread and causes a hang--bad idea all around.
- Thread t = new Thread(new Runnable() {
- public void run() {
- runtime.launch();
- }
- });
- t.start();
- //runtime.start(appletLocation);
+ // Cannot use invokeLater() here, otherwise it gets
+ // placed on the event thread and causes a hang--bad idea all around.
+ if (present) {
+ new Thread(presentHandler).start();
+ } else {
+ new Thread(runHandler).start();
+ }
+ }
+
+
+ class DefaultRunHandler implements Runnable {
+ public void run() {
+ try {
+ String appletClassName = sketch.compile();
+ if (appletClassName != null) {
+ runtime = new Runner(sketch, appletClassName, false, Editor.this);
+ runtime.launch();
+ }
+ } catch (Exception e) {
+ statusError(e);
+ }
+ }
+ }
+
+
+ class DefaultPresentHandler implements Runnable {
+ public void run() {
+ try {
+ String appletClassName = sketch.compile();
+ if (appletClassName != null) {
+ runtime = new Runner(sketch, appletClassName, true, Editor.this);
+ runtime.launch();
+ }
+ } catch (Exception e) {
+ statusError(e);
}
-
- } catch (Exception e) {
- //System.err.println("exception reached editor");
- //e.printStackTrace();
- statusError(e);
}
}
@@ -1981,25 +2027,27 @@ public class Editor extends JFrame implements RunnerListener {
if (!handleExportCheckModified()) return;
toolbar.activate(EditorToolbar.EXPORT);
- //SwingUtilities.invokeLater(new Runnable() {
- Thread t = new Thread(new Runnable() {
- public void run() {
- try {
- boolean success = sketch.exportApplet();
- if (success) {
- File appletFolder = new File(sketch.getFolder(), "applet");
- Base.openFolder(appletFolder);
- statusNotice("Done exporting.");
- } else {
- // error message will already be visible
- }
- } catch (Exception e) {
- statusError(e);
- }
- //toolbar.clear();
- toolbar.deactivate(EditorToolbar.EXPORT);
- }});
- t.start();
+ new Thread(exportHandler).start();
+ }
+
+
+ class DefaultExportHandler implements Runnable {
+ public void run() {
+ try {
+ boolean success = sketch.exportApplet();
+ if (success) {
+ File appletFolder = new File(sketch.getFolder(), "applet");
+ Base.openFolder(appletFolder);
+ statusNotice("Done exporting.");
+ } else {
+ // error message will already be visible
+ }
+ } catch (Exception e) {
+ statusError(e);
+ }
+ //toolbar.clear();
+ toolbar.deactivate(EditorToolbar.EXPORT);
+ }
}
@@ -2010,25 +2058,29 @@ public class Editor extends JFrame implements RunnerListener {
if (!handleExportCheckModified()) return;
toolbar.activate(EditorToolbar.EXPORT);
- //SwingUtilities.invokeLater(new Runnable() {
- SwingUtilities.invokeLater(new Runnable() {
- public void run() {
- statusNotice("Exporting application...");
- try {
- if (sketch.exportApplicationPrompt()) {
- Base.openFolder(sketch.getFolder());
- statusNotice("Done exporting.");
- } else {
- // error message will already be visible
- // or there was no error, in which case it was canceled.
- }
- } catch (Exception e) {
- statusNotice("Error during export.");
- e.printStackTrace();
- }
- //toolbar.clear();
- toolbar.deactivate(EditorToolbar.EXPORT);
- }});
+ // previous was using SwingUtilities.invokeLater()
+ new Thread(exportAppHandler).start();
+ }
+
+
+ class DefaultExportAppHandler implements Runnable {
+ public void run() {
+ statusNotice("Exporting application...");
+ try {
+ if (sketch.exportApplicationPrompt()) {
+ Base.openFolder(sketch.getFolder());
+ statusNotice("Done exporting.");
+ } else {
+ // error message will already be visible
+ // or there was no error, in which case it was canceled.
+ }
+ } catch (Exception e) {
+ statusNotice("Error during export.");
+ e.printStackTrace();
+ }
+ //toolbar.clear();
+ toolbar.deactivate(EditorToolbar.EXPORT);
+ }
}
@@ -2317,5 +2369,4 @@ public class Editor extends JFrame implements RunnerListener {
super.show(component, x, y);
}
}
-}
-
+}
\ No newline at end of file
diff --git a/app/src/processing/app/debug/StreamRedirectThread.java b/app/src/processing/app/StreamRedirectThread.java
similarity index 93%
rename from app/src/processing/app/debug/StreamRedirectThread.java
rename to app/src/processing/app/StreamRedirectThread.java
index 795e8c199..0d27a56e6 100644
--- a/app/src/processing/app/debug/StreamRedirectThread.java
+++ b/app/src/processing/app/StreamRedirectThread.java
@@ -1,4 +1,5 @@
/*
+
* @(#)StreamRedirectThread.java 1.4 03/01/23
*
* Copyright 2003 Sun Microsystems, Inc. All rights reserved.
@@ -31,7 +32,7 @@
* facility. Licensee represents and warrants that it will not use or
* redistribute the Software for such purposes.
*/
-package processing.app.debug;
+package processing.app;
import java.io.*;
@@ -49,6 +50,7 @@ public class StreamRedirectThread extends Thread {
private static final int BUFFER_SIZE = 2048;
+
/**
* Set up for copy.
* @param name Name of the thread
@@ -61,6 +63,15 @@ public class StreamRedirectThread extends Thread {
this.out = new OutputStreamWriter(out);
setPriority(Thread.MAX_PRIORITY-1);
}
+
+
+ public StreamRedirectThread(String name, Reader in, Writer out) {
+ super(name);
+ this.in = in;
+ this.out = out;
+ setPriority(Thread.MAX_PRIORITY-1);
+ }
+
/**
* Copy.
diff --git a/app/src/processing/app/tools/android/Android.java b/app/src/processing/app/tools/android/Android.java
new file mode 100644
index 000000000..fc54b60f7
--- /dev/null
+++ b/app/src/processing/app/tools/android/Android.java
@@ -0,0 +1,339 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2009 Ben Fry and Casey Reas
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+package processing.app.tools.android;
+
+import java.io.File;
+import java.io.IOException;
+//import java.util.ArrayList;
+
+import processing.app.*;
+import processing.app.tools.Tool;
+import processing.core.PApplet;
+
+
+// http://dl.google.com/android/android-sdk_r3-mac.zip
+// http://dl.google.com/android/repository/repository.xml
+// http://dl.google.com/android/repository/tools_r03-macosx.zip
+
+public class Android implements Tool {
+ static String sdkPath;
+ static String toolsPath;
+
+ private Editor editor;
+ Build build;
+
+ String emulator;
+ Process emulatorProcess;
+
+
+ public String getMenuTitle() {
+ return "Android";
+ }
+
+
+ public void init(Editor parent) {
+ this.editor = parent;
+ }
+
+
+ public void run() {
+ editor.statusNotice("Loading Android tools.");
+
+ checkPath();
+ Device.checkDefaults();
+
+ editor.setHandlers(new RunHandler(this), new PresentHandler(this),
+ new ExportHandler(this), new ExportAppHandler(this));
+ build = new Build(editor);
+ editor.statusNotice("Done loading Android tools.");
+ }
+
+
+ static protected void checkPath() {
+ // System.out.println("PATH is " + Base.getenv("PATH"));
+ // System.out.println("PATH from System is " + System.getenv("PATH"));
+ if (Base.getenv("ANDROID_SDK") == null) {
+ Base.setenv("ANDROID_SDK", "/opt/android");
+ }
+ sdkPath = Base.getenv("ANDROID_SDK");
+// System.out.println("sdk path is " + sdkPath);
+ // sdkPath = "/opt/android";
+ String toolsPath = sdkPath + File.separator + "tools";
+ Base.setenv("PATH", Base.getenv("PATH") + File.pathSeparator + toolsPath);
+// System.out.println("path after set is " + Base.getenv("PATH"));
+ }
+
+
+ public Editor getEditor() {
+ return editor;
+ }
+
+
+ public Sketch getSketch() {
+ return editor.getSketch();
+ }
+
+
+ public Build getBuilder() {
+ return build;
+ }
+
+
+ /**
+ * Launch an emulator if not already running.
+ * @return The name of the emulator.
+ */
+ public String findEmulator() {
+ String portString = Preferences.get("android.emulator.port");
+ if (portString == null) {
+ portString = "5566";
+ Preferences.set("android.emulator.port", portString);
+ }
+ //int port = Integer.parseInt(portString);
+ String name = "emulator-" + portString;
+
+ try {
+ String[] devices = Debug.listDevices();
+ for (String s : devices) {
+ if (s.equals(name)) {
+ return name;
+ }
+ }
+ } catch (IOException e) {
+ //e.printStackTrace();
+ editor.statusError(e);
+ return null;
+ }
+
+ //# starts and uses port 5554 for communication (but not logs)
+ //emulator -avd gee1 -port 5554
+ //# only informative messages and up (emulator -help-logcat for more info)
+ //emulator -avd gee1 -logcat '*:i'
+ //# faster boot
+ //emulator -avd gee1 -logcat '*:i' -no-boot-anim
+ //# only get System.out and System.err
+ //emulator -avd gee1 -logcat 'System.*:i' -no-boot-anim
+ //# though lots of messages aren't through System.*, so that's not great
+ //# need to instead use the adb interface
+
+ // launch emulator because it's not running yet
+ try {
+ editor.statusNotice("Starting new Android emulator.");
+ emulatorProcess = Runtime.getRuntime().exec(new String[] {
+ "emulator",
+ "-avd", getDefaultDevice(),
+ "-port", portString,
+ "-logcat", "'*:i'",
+ "-no-boot-anim"
+ });
+ System.out.println(PApplet.join(new String[] {
+ "emulator",
+ "-avd", getDefaultDevice(),
+ "-port", portString,
+ "-logcat", "'*:i'",
+ "-no-boot-anim"
+ }, " "));
+ // make sure that the streams are drained properly
+ new StreamRedirectThread("android-emulator-out",
+ emulatorProcess.getInputStream(), System.out).start();
+ new StreamRedirectThread("android-emulator-err",
+ emulatorProcess.getErrorStream(), System.err).start();
+ return name;
+
+ } catch (IOException e) {
+ //e.printStackTrace();
+ editor.statusError(e);
+ return null;
+ }
+ }
+
+
+ public String getDefaultDevice() {
+ return Device.avdDonut.name;
+ }
+
+
+ /** Find connected devices */
+ public String findDevice() {
+ String[] devices;
+ try {
+ devices = Debug.listDevices();
+ } catch (IOException e) {
+ editor.statusError(e);
+ //e.printStackTrace();
+ return null;
+ }
+// ArrayList available = new ArrayList();
+// for (String s : devices) {
+// }
+ // just go with the first non-emulator device
+ for (String name : devices) {
+ if (!name.startsWith("emulator-")) {
+ return name;
+ }
+ }
+ return null;
+ }
+
+
+//adb -s emulator-5556 install helloWorld.apk
+
+//: adb -s HT91MLC00031 install bin/Brightness-debug.apk
+//532 KB/s (190588 bytes in 0.349s)
+// pkg: /data/local/tmp/Brightness-debug.apk
+//Failure [INSTALL_FAILED_ALREADY_EXISTS]
+
+//: adb -s HT91MLC00031 install -r bin/Brightness-debug.apk
+//1151 KB/s (190588 bytes in 0.161s)
+// pkg: /data/local/tmp/Brightness-debug.apk
+//Success
+
+//safe to just always include the -r (reinstall) flag
+
+ // 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
+
+ public void installAndRun(String target, String device) {
+ boolean success;
+
+ Build build = getBuilder();
+ success = build.createProject();
+ if (!success) return;
+
+ // now run the ant debug or release version
+ success = build.antBuild("debug");
+ //System.out.println("ant build complete " + success);
+ if (!success) return;
+
+ // // if no emulator is running, start an emulator
+ // String name = findEmulator();
+
+ success = installSketch(device);
+ if (!success) return;
+
+ success = startSketch(device);
+ }
+
+
+ boolean installSketch(String device) {
+ // install the new package into the emulator
+ //System.out.println("installing onto emulator/device");
+ boolean emu = device.startsWith("emulator");
+ editor.statusNotice("Sending sketch to the " +
+ (emu ? "emulator" : "phone") + ".");
+ try {
+ Device.sendMenuButton(device); // wake up
+ Device.sendHomeButton(device); // kill any running app
+
+ String[] cmd = new String[] {
+ "adb",
+ "-s", device,
+ "install", "-r", // safe to always use -r switch
+ build.getPathForAPK("debug")
+ };
+ Process p = Runtime.getRuntime().exec(cmd);
+
+ System.out.println();
+ System.out.print("Install command: ");
+ System.out.println(PApplet.join(cmd, " "));
+
+ StringRedirectThread error = new StringRedirectThread(p.getErrorStream());
+ StringRedirectThread output = new StringRedirectThread(p.getInputStream());
+
+ int result = p.waitFor();
+ if (result != 0) {
+ for (String err : error.getLines()) {
+ System.err.println(err);
+ }
+ //editor.statusNotice("“adb install” returned " + result + ".");
+ //System.out.println("Could not install the sketch.");
+ editor.statusError("Could not install the sketch.");
+ System.out.println("“adb install” returned " + result + ".");
+ return false;
+
+ } else {
+ String errorMsg = null;
+ for (String out : output.getLines()) {
+ if (out.startsWith("Failure")) {
+// String[] stuff = PApplet.match(out, "\\[(.*)\\]");
+// if (stuff != null) {
+// errorMsg = stuff[1];
+// } else {
+ errorMsg = out.substring(8);
+// }
+ System.err.println(out);
+ } else {
+ System.out.println(out);
+ }
+ }
+ if (errorMsg == null) {
+ editor.statusNotice("Done installing.");
+ return true;
+
+ } else {
+ editor.statusError("Error while installing " + errorMsg);
+ }
+ }
+ } catch (IOException e) {
+ editor.statusError(e);
+
+ } catch (InterruptedException e) { }
+
+ return false;
+ }
+
+
+ // better 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/
+ boolean startSketch(String device) {
+ try {
+ Device.sendMenuButton(device); // wake up
+ Device.sendHomeButton(device); // kill any running app
+
+ //"am start -a android.intent.action.MAIN -n com.android.browser/.BrowserActivity"
+ Process p = Runtime.getRuntime().exec(new String[] {
+ "adb",
+ "-s", device,
+ "shell", "am", "start",
+ "-a", "android.intent.action.MAIN", "-n",
+ build.getPackageName() + "/." + build.getClassName()
+ });
+ int result = p.waitFor();
+ if (result != 0) {
+ editor.statusError("Could not start the sketch.");
+ System.out.println("“adb shell” for “am start” returned " + result + ".");
+
+ } else {
+ boolean emu = device.startsWith("emulator");
+ editor.statusNotice("Sketch started on the " +
+ (emu ? "emulator" : "phone") + ".");
+
+ return true;
+ }
+ } catch (IOException e) {
+ editor.statusError(e);
+
+ } catch (InterruptedException e) { }
+
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/app/src/processing/app/tools/android/Build.java b/app/src/processing/app/tools/android/Build.java
index ef44eefd5..a69ed622d 100644
--- a/app/src/processing/app/tools/android/Build.java
+++ b/app/src/processing/app/tools/android/Build.java
@@ -1,109 +1,158 @@
package processing.app.tools.android;
-//import java.awt.*;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
-//import javax.swing.*;
+import org.apache.tools.ant.*;
import processing.app.*;
import processing.app.debug.RunnerException;
import processing.app.preproc.PdePreprocessor;
-import processing.app.tools.Tool;
import processing.core.PApplet;
-public class Build implements Tool {
+// build/create apk
+// build non-debug version of apk
+// call this "export to application" or something?
+// "Export to Package" cmd-shift-E
+// install to device (release version)
+// "Export to Device" cmd-E
+// run/debug in emulator
+// cmd-r
+// run/debug on device
+// cmd-shift-r (like present mode)
+
+// download/install sdk
+// create new avd for debugging
+// set the avd to use
+// set the sdk location
+// does this require PATH to be set as well?
+// or can it be done with $ANDROID_SDK?
+// or do we need to set PATH each time P5 starts up
+
+/*
+android bugs/wishes
+
++ uninstalled sdk version 3, without first uninstalling google APIs v3
+ removes the google APIs from the list (so no way to uninstall)
+ and produces the following error every time it's loaded:
+ Error: Ignoring add-on 'google_apis-3-r03': Unable to find base platform with API level '3'
+
++ install android sdk from command line (and download components)
+
++ syntax for "create avd" is WWWxHHH not WWW-HHH
+ http://developer.android.com/guide/developing/tools/avd.html
+
++ half the tools use --option-name the other half use -option-name
+
++ http://developer.android.com/guide/developing/other-ide.html
+ set JAVA_HOME=c:\Prora~1\Java\
+ that should be progra~1 (it's missing a G)
+
++ "If there is no emulator/device running, adb returns no device."
+ not true, it just shows up blank
+ http://developer.android.com/guide/developing/tools/adb.html
+*/
+
+/*
+android create project -t 3 -n test_name -p test_path -a test_activity -k test.pkg
+file:///opt/android/docs/guide/developing/other-ide.html
+# compile code for a project
+ant debug
+# this pulls in tons of other .jar files for android ant
+# local.properties (in the android folder) has the sdk location
+ */
+
+public class Build {
+ static SimpleDateFormat dateFormat = new SimpleDateFormat("yyMMdd.HHmm");
+
+ static String basePackage = "processing.android.test";
+ static String sdkLocation = "/opt/android";
+
Editor editor;
- SimpleDateFormat dateFormat = new SimpleDateFormat("yyMMdd.HHmm");
-
- String defaultPackage = "processing.android.test";
- String sdkLocation = "/opt/android";
+ String className;
+ File androidFolder;
+ File buildFile;
- public String getMenuTitle() {
- return "Android";
+ public Build(Editor editor) {
+ this.editor = editor;
}
-
- public void init(Editor parent) {
- this.editor = parent;
-
- /*
- SwingUtilities.invokeLater(new Runnable() {
- public void run() {
- JMenuBar mb = null;
- while (mb == null) {
- mb = editor.getJMenuBar();
- if (mb != null) {
- int menuCount = mb.getMenuCount();
- for (int i = 0; i < menuCount; i++) {
- JMenu menu = mb.getMenu(i);
- String menuName = menu.getName();
- System.out.println(menu.getName());
- if (menuName == null) continue;
- //if (menu.getName().equals("Tools")) {
-// if (menuName.equals("Tools")) {
- int itemCount = menu.getItemCount();
- for (int j = 0; j < itemCount; j++) {
- JMenuItem item = menu.getItem(j);
- System.out.println(item.getName());
- if (item.getName().equals("Android")) {
- System.out.println("done");
- int modifiers = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
- item.setAccelerator(KeyStroke.getKeyStroke('D', modifiers));
- //item.setShortcut(new MenuShortcut('D'));
- return;
- }
- }
-// }
- }
- }
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
+
+ protected int[] getSketchSize() {
+ int wide = Device.DEFAULT_WIDTH;
+ int high = Device.DEFAULT_HEIGHT;
+// String renderer = "";
+
+ // This matches against any uses of the size() function, whether numbers
+ // or variables or whatever. This way, no warning is shown if size() isn't
+ // actually used in the applet, which is the case especially for anyone
+ // who is cutting/pasting from the reference.
+ String sizeRegex =
+ "(?:^|\\s|;)size\\s*\\(\\s*(\\S+)\\s*,\\s*(\\d+),?\\s*([^\\)]*)\\s*\\)";
+
+ Sketch sketch = editor.getSketch();
+ String scrubbed = Sketch.scrubComments(sketch.getCode(0).getProgram());
+ String[] matches = PApplet.match(scrubbed, sizeRegex);
+
+ if (matches != null) {
+ try {
+ wide = Integer.parseInt(matches[1]);
+ high = Integer.parseInt(matches[2]);
+
+ // Adding back the trim() for 0136 to handle Bug #769
+// if (matches.length == 4) renderer = matches[3].trim();
+
+ } catch (NumberFormatException e) {
+ // found a reference to size, but it didn't
+ // seem to contain numbers
+ final String message =
+ "The size of this applet could not automatically be\n" +
+ "determined from your code.\n" +
+ "Use only numeric values (not variables) for the size()\n" +
+ "command. See the size() reference for an explanation.";
+
+ Base.showWarning("Could not find sketch size", message, null);
}
- });
- */
+ } // else no size() command found
+ return new int[] { wide, high };
}
-
- public void run() {
+
+ public boolean createProject() {
Sketch sketch = editor.getSketch();
// Create the 'android' build folder, and move any existing version out.
- File androidFolder = new File(sketch.getFolder(), "android");
+ 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) {
Base.showWarning("Failed to rename",
- "Could not rename the old \"android\" build folder.\n" +
+ "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;
+ return false;
}
} 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 permissions to sort out?", null);
- return;
+ "Perhaps you have some file permissions to sort out?", null);
+ return false;
}
}
// Create the 'src' folder with the preprocessed code.
File srcFolder = new File(androidFolder, "src");
- File javaFolder = new File(srcFolder, defaultPackage.replace('.', '/'));
+ File javaFolder = new File(srcFolder, getPackageName().replace('.', '/'));
javaFolder.mkdirs();
//File srcFile = new File(actualSrc, className + ".java");
String buildPath = javaFolder.getAbsolutePath();
@@ -115,77 +164,114 @@ public class Build implements Tool {
try {
// need to change to a better set of imports here
- String className = sketch.preprocess(buildPath, new Preproc());
+ className = sketch.preprocess(buildPath, new Preproc());
if (className != null) {
- writeAndroidManifest(new File(androidFolder, "AndroidManifest.xml"), className);
+ File androidXML = new File(androidFolder, "AndroidManifest.xml");
+ writeAndroidManifest(androidXML, sketch.getName(), className);
writeBuildProps(new File(androidFolder, "build.properties"));
- File buildFile = new File(androidFolder, "build.xml");
+ buildFile = new File(androidFolder, "build.xml");
writeBuildXML(buildFile, sketch.getName());
writeDefaultProps(new File(androidFolder, "default.properties"));
writeLocalProps(new File(androidFolder, "local.properties"));
writeRes(new File(androidFolder, "res"), className);
writeLibs(new File(androidFolder, "libs"));
- Base.openFolder(androidFolder);
+ //Base.openFolder(androidFolder);
// looking for BUILD SUCCESSFUL or BUILD FAILED
// Process p = Runtime.getRuntime().exec(new String[] {
// "ant", "-f", buildFile.getAbsolutePath()
// });
// BufferedReader stdout = PApplet.createReader(p.getInputStream());
-// String line = null;
+// String line = null;
}
// } catch (IOException ioe) {
// ioe.printStackTrace();
} catch (RunnerException e) {
- e.printStackTrace();
+ //e.printStackTrace();
+ editor.statusError(e);
+ // set this back, even if there's an error
+ Preferences.set("preproc.imports", prefsLine);
+ return false;
}
+ return true;
+ }
+
+
+ /**
+ * @param buildFile location of the build.xml for the sketch
+ * @param target "debug" or "release"
+ */
+ boolean antBuild(String target) {
+ Project p = new Project();
+ p.setUserProperty("ant.file", buildFile.getAbsolutePath());
- // set this back, even if there's an error
- Preferences.set("preproc.imports", prefsLine);
+ DefaultLogger consoleLogger = new DefaultLogger();
+ consoleLogger.setErrorPrintStream(System.err);
+ consoleLogger.setOutputPrintStream(System.out);
+ consoleLogger.setMessageOutputLevel(Project.MSG_INFO);
+ p.addBuildListener(consoleLogger);
+
+ try {
+ editor.statusNotice("Building sketch for Android...");
+ p.fireBuildStarted();
+ p.init();
+ ProjectHelper helper = ProjectHelper.getProjectHelper();
+ p.addReference("ant.projectHelper", helper);
+ helper.parse(p, buildFile);
+ //p.executeTarget(p.getDefaultTarget());
+ p.executeTarget("debug");
+ editor.statusNotice("Finished building sketch.");
+ return true;
+
+ } catch (BuildException e) {
+ p.fireBuildFinished(e);
+ //e.printStackTrace(); // ??
+ editor.statusError(e);
+ }
+ return false;
+ }
+
+
+ String getPackageName() {
+ return basePackage + "." + editor.getSketch().getName().toLowerCase();
+ }
+
+
+ String getClassName() {
+ return className;
+ }
+
+
+ String getPathForAPK(String target) {
+ Sketch sketch = editor.getSketch();
+ File apkFile = new File(androidFolder, "bin/" + sketch.getName() + "-" + target + ".apk");
+ return apkFile.getAbsolutePath();
}
class Preproc extends PdePreprocessor {
public int writeImports(PrintStream out) {
- out.println("package " + defaultPackage + ";");
+ out.println("package " + getPackageName() + ";");
out.println();
return super.writeImports(out);
}
}
- /*
-android create project -t 3 -n test_name -p test_path -a test_activity -k test.pkg
-file:///opt/android/docs/guide/developing/other-ide.html
-# compile code for a project
-ant debug
-# this pulls in tons of other .jar files for android ant
-# local.properties (in the android folder) has the sdk location
-
-# starts and uses port 5554 for communication (but not logs)
-emulator -avd gee1 -port 5554
-
-# only informative messages and up (emulator -help-logcat for more info)
-emulator -avd gee1 -logcat '*:i'
-# faster boot
-emulator -avd gee1 -logcat '*:i' -no-boot-anim
-# only get System.out and System.err
-emulator -avd gee1 -logcat 'System.*:i' -no-boot-anim
-# though lots of messages aren't through System.*, so that's not great
-
-# need to instead use the adb interface
- */
-
-
- void writeAndroidManifest(File file, String className) {
+ void writeAndroidManifest(File file, String sketchName, String className) {
PrintWriter writer = PApplet.createWriter(file);
writer.println("");
writer.println("");
+
+ writer.println(" ");
+
writer.println(" ");
- writer.println(" ");
+ writer.println(" ");
writer.println(" android:label=\"@string/app_name\">");
writer.println(" ");
writer.println(" ");
@@ -201,7 +287,7 @@ emulator -avd gee1 -logcat 'System.*:i' -no-boot-anim
void writeBuildProps(File file) {
PrintWriter writer = PApplet.createWriter(file);
- writer.println("application-package=" + defaultPackage);
+ writer.println("application-package=" + getPackageName());
writer.flush();
writer.close();
}
@@ -238,7 +324,7 @@ emulator -avd gee1 -logcat 'System.*:i' -no-boot-anim
void writeDefaultProps(File file) {
PrintWriter writer = PApplet.createWriter(file);
- writer.println("target=Google Inc.:Google APIs:3");
+ writer.println("target=Google Inc.:Google APIs:4");
writer.flush();
writer.close();
}
@@ -284,6 +370,7 @@ emulator -avd gee1 -logcat 'System.*:i' -no-boot-anim
}
+ /** This recommended to be a string resource so that it can be localized. */
void writeResValuesStrings(File file, String className) {
PrintWriter writer = PApplet.createWriter(file);
writer.println("");
@@ -300,4 +387,13 @@ emulator -avd gee1 -logcat 'System.*:i' -no-boot-anim
InputStream input = getClass().getResourceAsStream("processing-core.zip");
PApplet.saveStream(new File(libsFolder, "processing-core.jar"), input);
}
+
+
+ /**
+ * Place quotes around a string to avoid dreadful syntax mess of escaping
+ * quotes near quoted strings. Mmmm!
+ */
+ static final String q(String what) {
+ return "\"" + what + "\"";
+ }
}
\ No newline at end of file
diff --git a/app/src/processing/app/tools/android/Debug.java b/app/src/processing/app/tools/android/Debug.java
new file mode 100644
index 000000000..b598ff2d0
--- /dev/null
+++ b/app/src/processing/app/tools/android/Debug.java
@@ -0,0 +1,92 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2009 Ben Fry and Casey Reas
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+package processing.app.tools.android;
+
+import java.io.IOException;
+
+import processing.app.Base;
+import processing.core.PApplet;
+
+
+public class Debug {
+
+ static final String ADB_DEVICES_ERROR =
+ "Received unfamiliar output from “avd devices”.\n" +
+ "The device list may have errors.";
+
+ static protected String[] listDevices() throws IOException {
+ String[] cmd = { "adb", "devices" };
+ Process p = Runtime.getRuntime().exec(cmd);
+ StringRedirectThread error = new StringRedirectThread(p.getErrorStream());
+ StringRedirectThread output = new StringRedirectThread(p.getInputStream());
+
+ try {
+ int result = p.waitFor();
+ if (result == 0) {
+ String[] lines = output.getLines();
+ // First line starts "List of devices", last line is blank.
+
+ // when an emulator is started with a debug port, then it shows up
+ // in the list of devices.
+
+// List of devices attached
+// HT91MLC00031 device
+// emulator-5554 offline
+
+// List of devices attached
+// HT91MLC00031 device
+// emulator-5554 device
+
+ if (lines == null) {
+ // result was 0, so we're ok, but this is odd.
+ System.out.println("No devices found.");
+ return new String[] { };
+ }
+
+ if (!lines[0].startsWith("List of devices") ||
+ lines[lines.length-1].trim().length() != 0) {
+ Base.showWarning("Android Error", ADB_DEVICES_ERROR, null);
+ }
+ String[] devices = new String[lines.length - 2];
+ int deviceIndex = 0;
+ for (int i = 1; i < lines.length - 1; i++) {
+ int tab = lines[i].indexOf('\t');
+ if (tab != -1) {
+ devices[deviceIndex++] = lines[i].substring(0, tab);
+ } else if (lines[i].trim().length() != 0) {
+ System.out.println("Unknown “adb devices” response: " + lines[i]);
+ }
+ }
+ devices = PApplet.subset(devices, 0, deviceIndex);
+ return devices;
+
+ } else {
+ for (String s : error.getLines()) {
+ System.err.println(s);
+ }
+ }
+ } catch (InterruptedException ie) {
+ // ignored, just other thread fun
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/app/src/processing/app/tools/android/Device.java b/app/src/processing/app/tools/android/Device.java
new file mode 100644
index 000000000..3aef82aec
--- /dev/null
+++ b/app/src/processing/app/tools/android/Device.java
@@ -0,0 +1,218 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2009 Ben Fry and Casey Reas
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+package processing.app.tools.android;
+
+import java.io.IOException;
+
+import processing.app.Base;
+import processing.core.PApplet;
+
+
+public class Device {
+
+ /** Name of this device. */
+ String name;
+
+ /** "android-4" or "Google Inc.:Google APIs:4" */
+ String target;
+
+ /** Width of the skin used in the emulator. */
+ int width;
+
+ /** Height of the skin used in the emulator. */
+ int height;
+
+ /**
+ * Default virtual device used by Processing, intended to be similar to
+ * a device like a T-Mobile G1 or myTouch 3G. Uses Android 1.6 (Donut) APIs,
+ * and the screen is 480x320 pixels, or HVGA (Half VGA).
+ */
+ static Device avdDonut =
+ // Using the generic AVD causes a prompt to show up on the console,
+ // so using the Google version instead which doesn't ask for a profile.
+ new Device("Processing-Donut", "Google Inc.:Google APIs:4", 480, 320);
+
+ /**
+ * Default virtual device used by Processing, designed to be similar to
+ * a device like the Moto Droid. Uses Android 2.0 APIs, and the screen
+ * is set to WVGA854 (854x480), the same aspect ratio (with rounding),
+ * as 1920x1080, or 16:9.
+ */
+ static Device avdEclair =
+ new Device("Processing-Eclair", "Google Inc.:Google APIs:5", 854, 480);
+
+ static final String AVD_CREATE_ERROR =
+ "An error occurred while running “android create avd”\n" +
+ "to set up the default Android emulator. Make sure that the\n" +
+ "Android SDK is installed properly, and that the Android\n" +
+ "and Google APIs are installed for levels 4 and 5.";
+
+ static final int DEFAULT_WIDTH = 320;
+ static final int DEFAULT_HEIGHT = 480;
+
+
+ Device(String name, String target, int width, int height) {
+ this.name = name;
+ this.target = target;
+ this.width = width;
+ this.height = height;
+ }
+
+
+ static boolean checkDefaults() {
+ try {
+ if (!avdDonut.exists()) {
+ if (!avdDonut.create()) {
+ Base.showWarning("Android Error",
+ "An error occurred while running “android create avd”\n" +
+ "to set up the default Android emulator.", null);
+ }
+ }
+ if (!avdEclair.exists()) {
+ if (!avdEclair.create()) {
+ Base.showWarning("Android Error", AVD_CREATE_ERROR, null);
+ }
+ }
+ return true;
+
+ } catch (Exception e) {
+ Base.showWarning("Android Error", AVD_CREATE_ERROR, null);
+ }
+ return false;
+ }
+
+
+ protected boolean exists() throws IOException {
+ String[] cmd = { "android", "list", "avds" };
+ Process p = Runtime.getRuntime().exec(cmd);
+ StringRedirectThread error = new StringRedirectThread(p.getErrorStream());
+ StringRedirectThread output = new StringRedirectThread(p.getInputStream());
+
+ try {
+ int result = p.waitFor();
+ //System.out.println("res is " + result);
+ if (result == 0) {
+ String[] lines = output.getLines();
+ //PApplet.println(lines);
+ for (String line : lines) {
+ String[] m = PApplet.match(line, "\\s+Name:\\s+(\\S+)");
+ //PApplet.println(m);
+ if (m != null) {
+ if (m[1].equals(name)) return true;
+ }
+ }
+ } else {
+ for (String s : error.getLines()) {
+ System.err.println(s);
+ }
+ }
+ } catch (InterruptedException ie) { }
+
+ return false;
+ }
+
+
+ protected boolean create() throws IOException {
+ String[] cmd = { "android", "create", "avd",
+ "-n", name,
+ "-t", target
+// "-s", width + "x" + height
+ };
+ //System.out.println(PApplet.join(cmd, " "));
+ Process p = Runtime.getRuntime().exec(cmd);
+ StringRedirectThread error = new StringRedirectThread(p.getErrorStream());
+ StringRedirectThread output = new StringRedirectThread(p.getInputStream());
+
+ try {
+ int result = p.waitFor();
+ //System.out.println("res is " + res);
+ if (result == 0) {
+ //String[] lines = output.getLines();
+ //PApplet.println(lines);
+ for (String s : output.getLines()) {
+ // mumble the result into the console
+ System.out.println(s);
+ }
+ return true;
+
+ } else {
+ for (String s : error.getLines()) {
+ System.err.println(s);
+ }
+ }
+ } catch (InterruptedException ie) { }
+
+ return false;
+ }
+
+
+ static void sendMenuButton(String device) throws IOException {
+ // adb -s emulator-5566 shell getevent
+
+ // back on G1
+// /dev/input/event3: 0001 008b 00000001
+// /dev/input/event3: 0001 008b 00000000
+
+ // back on the emulator
+// /dev/input/event0: 0001 00e5 00000001
+// /dev/input/event0: 0001 00e5 00000000
+
+ if (device.startsWith("emulator-")) {
+ sendKey(device, 0x00e5);
+ } else {
+ sendKey(device, 0x008b);
+ }
+ }
+
+
+ static void sendHomeButton(String device) throws IOException {
+ sendKey(device, 0x0066); // 102
+
+ // home on the G1
+// /dev/input/event3: 0001 0066 00000001
+// /dev/input/event3: 0001 0066 00000000
+
+ // home on the emulator
+// /dev/input/event0: 0001 0066 00000001
+// /dev/input/event0: 0001 0066 00000000
+
+ }
+
+
+ static void sendKey(String device, int key) throws IOException {
+ String inputDevice =
+ device.startsWith("emulator") ? "/dev/input/event0" : "/dev/input/event3";
+ String[] cmd = new String[] {
+ "adb",
+ "-s", device,
+ "shell", "sendevent",
+ inputDevice, "1", String.valueOf(key),
+ "1" // start with key down
+ };
+ try {
+ Runtime.getRuntime().exec(cmd).waitFor();
+ cmd[cmd.length - 1] = "0"; // send key up
+ Runtime.getRuntime().exec(cmd).waitFor();
+
+ } catch (InterruptedException ie) { }
+ }
+}
\ No newline at end of file
diff --git a/app/src/processing/app/tools/android/ExportAppHandler.java b/app/src/processing/app/tools/android/ExportAppHandler.java
new file mode 100644
index 000000000..c479455ce
--- /dev/null
+++ b/app/src/processing/app/tools/android/ExportAppHandler.java
@@ -0,0 +1,38 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2009 Ben Fry and Casey Reas
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+package processing.app.tools.android;
+
+
+public class ExportAppHandler implements Runnable {
+ Android parent;
+
+
+ public ExportAppHandler(Android parent) {
+ this.parent = parent;
+ }
+
+
+ public void run() {
+
+ }
+}
+
diff --git a/app/src/processing/app/tools/android/ExportHandler.java b/app/src/processing/app/tools/android/ExportHandler.java
new file mode 100644
index 000000000..ac98fe972
--- /dev/null
+++ b/app/src/processing/app/tools/android/ExportHandler.java
@@ -0,0 +1,38 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2009 Ben Fry and Casey Reas
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+package processing.app.tools.android;
+
+
+public class ExportHandler implements Runnable {
+ Android parent;
+
+
+ public ExportHandler(Android parent) {
+ this.parent = parent;
+ }
+
+
+ public void run() {
+
+ }
+}
+
diff --git a/app/src/processing/app/tools/android/PresentHandler.java b/app/src/processing/app/tools/android/PresentHandler.java
new file mode 100644
index 000000000..e7dc6167b
--- /dev/null
+++ b/app/src/processing/app/tools/android/PresentHandler.java
@@ -0,0 +1,43 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2009 Ben Fry and Casey Reas
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+package processing.app.tools.android;
+
+
+public class PresentHandler implements Runnable {
+ Android parent;
+
+
+ public PresentHandler(Android parent) {
+ this.parent = parent;
+ }
+
+
+ public void run() {
+ String device = parent.findDevice();
+ if (device == null) {
+ parent.getEditor().statusError("No device found.");
+ } else {
+ parent.installAndRun("debug", device);
+ }
+ }
+}
+
diff --git a/app/src/processing/app/tools/android/RunHandler.java b/app/src/processing/app/tools/android/RunHandler.java
new file mode 100644
index 000000000..44e3bf8da
--- /dev/null
+++ b/app/src/processing/app/tools/android/RunHandler.java
@@ -0,0 +1,37 @@
+/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2009 Ben Fry and Casey Reas
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+package processing.app.tools.android;
+
+
+public class RunHandler implements Runnable {
+ Android parent;
+
+
+ public RunHandler(Android parent) {
+ this.parent = parent;
+ }
+
+
+ public void run() {
+ parent.installAndRun("debug", parent.findEmulator());
+ }
+}
\ No newline at end of file
diff --git a/app/src/processing/app/tools/android/StringRedirectThread.java b/app/src/processing/app/tools/android/StringRedirectThread.java
new file mode 100644
index 000000000..dab5f5380
--- /dev/null
+++ b/app/src/processing/app/tools/android/StringRedirectThread.java
@@ -0,0 +1,55 @@
+package processing.app.tools.android;
+
+import java.io.*;
+
+import processing.core.PApplet;
+
+
+public class StringRedirectThread extends Thread {
+ private Reader in;
+ private StringWriter out;
+ private boolean finished;
+
+ private static final int BUFFER_SIZE = 2048;
+
+
+ public StringRedirectThread(InputStream in) {
+ this.in = new InputStreamReader(in);
+ this.out = new StringWriter();
+ setPriority(Thread.MAX_PRIORITY-1);
+ start(); // [fry]
+ }
+
+
+ public void run() {
+ try {
+ char[] cbuf = new char[BUFFER_SIZE];
+ int count;
+ while ((count = in.read(cbuf, 0, BUFFER_SIZE)) >= 0) {
+ out.write(cbuf, 0, count);
+ // had to add the flush() here.. maybe shouldn't be using writer? [fry]
+ out.flush();
+ }
+ out.flush();
+
+ } catch (IOException exc) {
+ exc.printStackTrace();
+ }
+ finished = true;
+ }
+
+
+ public boolean isFinished() {
+ return finished;
+ }
+
+
+ public String getString() {
+ return (finished) ? out.toString() : null;
+ }
+
+
+ public String[] getLines() {
+ return finished ? PApplet.split(out.toString(), '\n') : null;
+ }
+}
\ No newline at end of file
diff --git a/build/linux/make.sh b/build/linux/make.sh
index cb4bbda71..3c0b6ae65 100755
--- a/build/linux/make.sh
+++ b/build/linux/make.sh
@@ -138,6 +138,7 @@ mkdir ../build/linux/work/classes
src/processing/app/preproc/*.java \
src/processing/app/syntax/*.java \
src/processing/app/tools/*.java \
+ src/processing/app/tools/android/*.java \
src/antlr/*.java \
src/antlr/java/*.java
diff --git a/build/macosx/dist/Processing.app/Contents/Info.plist b/build/macosx/dist/Processing.app/Contents/Info.plist
index ebb62b9a7..f338734d5 100755
--- a/build/macosx/dist/Processing.app/Contents/Info.plist
+++ b/build/macosx/dist/Processing.app/Contents/Info.plist
@@ -66,13 +66,13 @@
processing.app.Base
JVMVersion
- 1.5*
+ 1.5+
ClassPath
- $JAVAROOT/pde.jar:$JAVAROOT/core.jar:$JAVAROOT/antlr.jar:$JAVAROOT/ecj.jar:$JAVAROOT/registry.jar:$JAVAROOT/quaqua.jar
+ $JAVAROOT/pde.jar:$JAVAROOT/core.jar:$JAVAROOT/antlr.jar:$JAVAROOT/ecj.jar:$JAVAROOT/jna.jar:$JAVAROOT/quaqua.jar
JVMArchs
diff --git a/build/macosx/make.sh b/build/macosx/make.sh
index 43bb673a5..758c1f836 100755
--- a/build/macosx/make.sh
+++ b/build/macosx/make.sh
@@ -137,6 +137,7 @@ javac \
src/processing/app/preproc/*.java \
src/processing/app/syntax/*.java \
src/processing/app/tools/*.java \
+ src/processing/app/tools/android/*.java \
src/antlr/*.java \
src/antlr/java/*.java
diff --git a/build/windows/make.sh b/build/windows/make.sh
index e7efec5f5..2b0d7254d 100755
--- a/build/windows/make.sh
+++ b/build/windows/make.sh
@@ -155,6 +155,7 @@ mkdir ../build/windows/work/classes
src/processing/app/syntax/*.java \
src/processing/app/preproc/*.java \
src/processing/app/tools/*.java \
+ src/processing/app/tools/android/*.java \
src/processing/app/windows/*.java \
src/antlr/*.java \
src/antlr/java/*.java
diff --git a/todo.txt b/todo.txt
index 31fa4d57e..47e4ab7a7 100644
--- a/todo.txt
+++ b/todo.txt
@@ -3,6 +3,37 @@ X use xdg-open as launcher on linux
X http://dev.processing.org/bugs/show_bug.cgi?id=1358
X change to different class id for export and export-opengl
+_ write quicktime uncompressed (w/o qtjava)
+_ http://blog.hslu.ch/rawcoder/2008/06/21/writing-quicktime-movies-in-pure-java/
+
+The only platform on which I can ever recall this happening was Netscape 4.5, where they would stop() and start() the applet in response to users scrolling. I don't think ANY current browser every actually start()s an applet after stop().
+Sadly, Safari does on Snow Leopard - it calls stop() when switching tabs and start() when you switch back.
+
+import javax.swing.*;
+public class StopCalled2 extends JApplet {
+ public StopCalled2() {
+ System.err.println("Created.");
+ }
+ public void stop() {
+ System.err.println("Stop called.");
+ }
+ public void start() {
+ System.err.println("Start called.");
+ }
+}
+
+and switching tabs produces:
+Oct 6 15:49:08 Shiny [0x0-0x12d12d].com.apple.Safari[3059]: Created.
+Oct 6 15:49:08 Shiny [0x0-0x12d12d].com.apple.Safari[3059]: Start called.
+Oct 6 15:49:11 Shiny [0x0-0x12d12d].com.apple.Safari[3059]: Stop called.
+Oct 6 15:49:14 Shiny [0x0-0x12d12d].com.apple.Safari[3059]: Start called.
+Oct 6 15:49:16 Shiny [0x0-0x12d12d].com.apple.Safari[3059]: Stop called.
+Oct 6 15:49:18 Shiny [0x0-0x12d12d].com.apple.Safari[3059]: Start called.
+
+_ console output has wrong character encoding
+_ http://dev.processing.org/bugs/show_bug.cgi?id=1367
+_ message(new String(b, offset, length), err, false);
+
_ improve the speed of file copying
_ use FileChannels, see FileInputStream.getChannel(),
_ and use transferFrom() or transferTo().)