adding android source

This commit is contained in:
benfry
2009-11-22 10:02:14 +00:00
parent 42d3787474
commit 19db458d42
18 changed files with 1256 additions and 189 deletions
+2
View File
@@ -16,5 +16,7 @@
<classpathentry kind="lib" path="lib/antlr.jar"/>
<classpathentry kind="lib" path="lib/jna.jar"/>
<classpathentry kind="lib" path="lib/ecj.jar"/>
<classpathentry kind="lib" path="lib/ant.jar"/>
<classpathentry kind="lib" path="lib/ant-launcher.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
+37 -24
View File
@@ -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>());
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).
+116 -65
View File
@@ -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);
}
}
}
}
@@ -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.
@@ -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<String> available = new ArrayList<String>();
// 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;
}
}
+193 -97
View File
@@ -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("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
writer.println("<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" ");
writer.println(" package=\"" + defaultPackage + "\" ");
writer.println(" package=\"" + getPackageName() + "\" ");
writer.println(" android:versionCode=\"1\" ");
writer.println(" android:versionName=\"1.0\">");
writer.println(" <uses-sdk android:minSdkVersion=" + q("4") + " />");
writer.println(" <application android:label=\"@string/app_name\">");
writer.println(" <activity android:name=\"." + className + "\"");
// writer.println(" <application android:label=" + q(sketchName) + ">");
writer.println(" <activity android:name=" + q("." + className));
// writer.println(" android:label=" + q(sketchName) + ">");
writer.println(" android:label=\"@string/app_name\">");
writer.println(" <intent-filter>");
writer.println(" <action android:name=\"android.intent.action.MAIN\" />");
@@ -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("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
@@ -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 + "\"";
}
}
@@ -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;
}
}
@@ -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) { }
}
}
@@ -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() {
}
}
@@ -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() {
}
}
@@ -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);
}
}
}
@@ -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());
}
}
@@ -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;
}
}
+1
View File
@@ -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
+2 -2
View File
@@ -66,13 +66,13 @@
<string>processing.app.Base</string>
<key>JVMVersion</key>
<string>1.5*</string>
<string>1.5+</string>
<key>ClassPath</key>
<!-- In 0149, removed /System/Library/Java from the CLASSPATH because
it can cause problems if users have installed weird files there.
http://dev.processing.org/bugs/show_bug.cgi?id=1045 -->
<string>$JAVAROOT/pde.jar:$JAVAROOT/core.jar:$JAVAROOT/antlr.jar:$JAVAROOT/ecj.jar:$JAVAROOT/registry.jar:$JAVAROOT/quaqua.jar</string>
<string>$JAVAROOT/pde.jar:$JAVAROOT/core.jar:$JAVAROOT/antlr.jar:$JAVAROOT/ecj.jar:$JAVAROOT/jna.jar:$JAVAROOT/quaqua.jar</string>
<key>JVMArchs</key>
<array>
+1
View File
@@ -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
+1
View File
@@ -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
+31
View File
@@ -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().)