From 19db458d425be3c2c1a5a51b60edb1715a9db61a Mon Sep 17 00:00:00 2001 From: benfry Date: Sun, 22 Nov 2009 10:02:14 +0000 Subject: [PATCH] adding android source --- app/.classpath | 2 + app/src/processing/app/Base.java | 61 ++-- app/src/processing/app/Editor.java | 181 ++++++---- .../app/{debug => }/StreamRedirectThread.java | 13 +- .../processing/app/tools/android/Android.java | 339 ++++++++++++++++++ .../processing/app/tools/android/Build.java | 290 ++++++++++----- .../processing/app/tools/android/Debug.java | 92 +++++ .../processing/app/tools/android/Device.java | 218 +++++++++++ .../app/tools/android/ExportAppHandler.java | 38 ++ .../app/tools/android/ExportHandler.java | 38 ++ .../app/tools/android/PresentHandler.java | 43 +++ .../app/tools/android/RunHandler.java | 37 ++ .../tools/android/StringRedirectThread.java | 55 +++ build/linux/make.sh | 1 + .../dist/Processing.app/Contents/Info.plist | 4 +- build/macosx/make.sh | 1 + build/windows/make.sh | 1 + todo.txt | 31 ++ 18 files changed, 1256 insertions(+), 189 deletions(-) rename app/src/processing/app/{debug => }/StreamRedirectThread.java (93%) create mode 100644 app/src/processing/app/tools/android/Android.java create mode 100644 app/src/processing/app/tools/android/Debug.java create mode 100644 app/src/processing/app/tools/android/Device.java create mode 100644 app/src/processing/app/tools/android/ExportAppHandler.java create mode 100644 app/src/processing/app/tools/android/ExportHandler.java create mode 100644 app/src/processing/app/tools/android/PresentHandler.java create mode 100644 app/src/processing/app/tools/android/RunHandler.java create mode 100644 app/src/processing/app/tools/android/StringRedirectThread.java 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().)