mirror of
https://github.com/processing/processing4.git
synced 2026-06-16 04:26:26 +02:00
adding android source
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+12
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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().)
|
||||
|
||||
Reference in New Issue
Block a user