Quick alert, or non-modal dialog in Android
It’s called a Toast.
Here’s a quick little tutorial on how to use it.
quick alert tutorial
Comments off
It’s called a Toast.
Here’s a quick little tutorial on how to use it.
quick alert tutorial
Comments off
A quick list of 3rd party apps that really make the Android experience for me:
There are several good file browsers out there, but I like the philosophy behind this one. I prefer the directories and folders metaphor over the idea of every app keeping it’s own list of files, a la iTunes, or the iPhone’s big list o’ apps home screen. This means access to the filesystem. At the moment, there isn’t a native file picker widget/view/whatever, so OpenIntents designed their file browser to provide that.
With that in mind, the next app is
This just allows the Android browser to download any file type. Currently, it only downloads files that have corresponding apps. This is by design. Since Android doesn’t have a native file browser, if you were to download something without an app to manage it, you’d never be able to delete it. People will make new apps, and those apps will be able to deal with different files, therefore they need a way to tell the browser what the new file types are. I guess Download Crutch just tells the browser that it’ll handle everything.
Because every computer needs a command line.
An SSH program for Android. If you have a Linux box running somewhere, you can just SSH into it, then use IRC, or any command line program you want. I think you can even use SSH tunneling, but I’m not sure.
It takes some configuring, and it’s really only good for browsing the web, but when I happen to have my laptop with me, and there’s no wifi available, this app is indispensable. T-Mobile isn’t keen on tethering, so this app can’t be released on the market, but lucky for us, Google lets us install whatever the heck we want on our phones. Eventually, Google will allow us to write native apps, and we’ll hopefully start to see true tethering apps, (You can already do this if you’ve rooted your phone) but until then, this app works pretty well.
So basically, what I really want is a very small computer that also happens to be able to make phone calls. Netbooks won’t fit in my pocket, and don’t serve as phones. The iPhone’s soft keyboard bugs me, I don’t like it’s overly managed interface, and the fact that it’s completely tied to iTunes. Windows Mobile just blows, and Palm is somewhat outdated, so we’re left with Android.
At it’s heart, a mobile phone is just a computer, and computers at their heart, are supposed to be able to do just about anything that you can figure out how to tell them to do. These apps aren’t everyday use programs, but they go a long way towards providing the kind of flexibility that you should have out of these devices.
Comments off
So, the previous method only really works for statically compiled programs, meaning it’s kinda useless for anything more complex, or for cross-compiling much of anything really.
Luckily, I found another page that details a better, though more time consuming way of doing things.
Basically, you’re downloading the Android source and compiling it to get a cross-compiler that links to the appropriate Android libraries, and anything else you might need.
CAVEATS:
Comments off
As everyone probably knows by now, Doom has been ported to Android.
This is exciting for two reasons,
Now, Dalvik doesn’t have JNI, so how can you write something in C and run it?
This guy will lead you down a link-clicking rabbit hole that’ll tell you how. The important parts are the compiler (Choose ARM GNU/Linux and IA32 GNU/Linux), and the technique of running system commands from Dalvik, which is detailed on Gimite’s page.
One note, he links to a dynamic link method of getting everything to work, which doesn’t seem to be strictly necessary. I just compiled this:
#include <stdio.h>
int main (int argc, char** argv) {
printf("Hello world!\n");
return 0;
}
and it wrote to stdio just fine.
The other important part is getting the native code to actually run. You can put your binaries in your assets directory, but I’m thinking that the directory is within the .apk your app gets bundled into, and I don’t think can even run anything from there. I wound up copying the binaries from the assets directory to /data/data/com.joshsera (where com.joshsera is replaced by your package name), chmodding it, and running it.
File newHello = new File("/data/data/com.joshsera/hello");
try {
newHello.createNewFile();
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(newHello));
BufferedInputStream in = new BufferedInputStream(this.getAssets().open("hello"));
int b;
while ((b = in.read()) != -1) {
out.write(b);
}
//
out.flush();
out.close();
in.close();
// chmod?
this.doCommand("/system/bin/chmod", "777", "/data/data/com.joshsera/hello");
} catch (IOException ex) {
}
doCommand is where I stuck the code to run system commands.
public void doCommand(String command, String arg0, String arg1) {
try {
// android.os.Exec is not included in android.jar so we need to use reflection.
Class> execClass = Class.forName("android.os.Exec");
Method createSubprocess = execClass.getMethod("createSubprocess",
String.class, String.class, String.class, int[].class);
Method waitFor = execClass.getMethod("waitFor", int.class);
// Executes the command.
// NOTE: createSubprocess() is asynchronous.
int[] pid = new int[1];
FileDescriptor fd = (FileDescriptor)createSubprocess.invoke(
null, command, arg0, arg1, pid);
// Reads stdout.
// NOTE: You can write to stdin of the command using new FileOutputStream(fd).
FileInputStream in = new FileInputStream(fd);
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String output = "";
try {
String line;
while ((line = reader.readLine()) != null) {
output += line + "\n";
}
} catch (IOException e) {
// It seems IOException is thrown when it reaches EOF.
}
// Waits for the command to finish.
waitFor.invoke(null, pid[0]);
// send output to the textbox
this.addText(output);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e.getMessage());
} catch (SecurityException e) {
throw new RuntimeException(e.getMessage());
} catch (NoSuchMethodException e) {
throw new RuntimeException(e.getMessage());
} catch (IllegalArgumentException e) {
throw new RuntimeException(e.getMessage());
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
} catch (InvocationTargetException e) {
throw new RuntimeException(e.getMessage());
}
}
Anyway, this has a few issues:
Comments off
At one point, I thought about using the accelerometer and compass in the G1 to turn the phone into a gyromouse/Wiimote style thing. After playing around with the accelerometer and compass, I decided that they were a bit too noisy to do that easily, so I shelved it because of time constraints.
Well, googling around today, I found this article on Kalman filtering, which might help me to cut down on some of that noise.
Maybe, if I can get that working, Wiimote-style pointing could find it’s way into RemoteDroid sometime in the future.
Comments off
So I recently found out that only one thread is ever allowed to update things like ImageViews and whatnot. If you try to make another thread update them, everything breaks, and all of a sudden, that ImageView never ever updates again. This was causing trouble in RemoteDroid because I wanted the tap to click to update the on-screen button state, but the tap events had to come from a Timer object, which put everything in a different thread.
Anyway, if you need to make a graphical change from another thread, the solution is to use the Handler class. The you use it is by creating a handler in your UI thread, then create some Runnable objects that call all your graphics methods. Here’s an example:
public class Thing extends Activity {
private Handler = new Handler();
// runnables for updating state
private runnable buttonOn = new Runnable() {
private void run() {
drawImageOn();
}
};
private runnable buttonOff = new Runnable() {
private void run() {
drawImageOff();
}
};
// our ImageView
private ImageView iv;
// a Timer object, which creates another thread for doing something else
private Timer timer = new Timer();
public void onCreate(Bundle savedInstanceState) {
// set ref to ImageView
this.iv = (ImageView)this.findViewById(R.id.whatever);
//
this.handler.post(this.buttonOn);
// Let's start up another thread for whatever reason
this.timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
// If we tried to call drawImageOff() here directly, something would break.
handler.post(buttonOff);
}
}, 0, 500);
}
// drawing methods
public void drawImageOn() {
// draw in our ImageView here
...
}
public void drawImageOff() {
// draw in our ImageView here
...
}
}
So basically, you’re setting up a few Runnables for changing graphical state, and then making every thread use the Handler for changing between those states.
Comments off
Recently spotted on Twitter: a RemoteDroid user’s report that I believe was about the server app working fine on Windows 7, which is still in beta. The tweet was in German, so I’m guessing to some degree on the meaning. But if anyone out there has RemoteDroid up and running on Windows 7 — and can post a comment or drop me a line in English to confirm — that would be great, thanks.
Comments off
Hi, Josh’s web site co-collaborator here to let you know about the latest version of RemoteDroid, released today. A number of new features were added, many in response to user feedback:
The Preferences menu, mentioned above, also lets you choose which new features you want to globally turn on or off, such as tap to click or using your trackball as a scroll wheel.
The new version of RemoteDroid has been tested with computers running Mac OS (Tiger, Leopard), Windows (XP, Vista) and Linux. However, there will inevitably be certain operating system and computer configuration combos that will, for one reason or another, work less than perfectly. If you’ve read the Support page and have ruled out all of the possible reasons for the app not working listed there, you can report an issue using the contact form.
As always, the phone app is available in the Android Marketplace. And make sure you also have the latest version (1.2) of the server app, available here.
Comments off
In the first version of the server app, I was telling Windows users to start the program by clicking on a .bat file because for some reason, the thing wasn’t starting up when clicking on the .jar file directly.
I’d seen other apps that did start from the jar file just fine, and after a lot of hunting around, I figured out why. When you search google for load image from jar file, most of the hits have you getting a URL object like this:
URL url = this.getClass().getResource("the_image.jpg");
then using that to load the actual image.
This works just fine on OSX, and Linux, but does absolutely nothing on Windows for some reason. Instead, on Windows, you have to get a JarFile object for the jar you’re loading from, then get a ZipEntry (JarEntry, whatever) from that, then get an InputStream from the ZipEntry, turn that into a BufferedInputStream, then pass that off to your default Toolkit to turn into an image.
Of course this means you have to figure out whether the app’s being run in a jar file in the first place or not. This is how I’m doing it:
URL fileURL = this.getClass().getProtectionDomain().getCodeSource().getLocation();
String sBase = fileURL.toString();
if ("jar".equals(sBase.substring(sBase.length()-3, sBase.length()))) {
MyClass.jar = new JarFile(new File(fileURL.toURI()));
}
after that, you can load the image ike this:
Image imReturn = null;
try {
if (jar == null) {
// This is used if you're not loading from a jar
imReturn = this.toolkit.createImage(this.getClass().getClassLoader().getResource(sImage));
} else {
//
BufferedInputStream bis = new BufferedInputStream(jar.getInputStream(jar.getEntry(sImage)));
ByteArrayOutputStream buffer=new ByteArrayOutputStream(4096);
int b;
while((b=bis.read())!=-1) {
buffer.write(b);
}
byte[] imageBuffer=buffer.toByteArray();
imReturn = this.toolkit.createImage(imageBuffer);
bis.close();
buffer.close();
}
} catch (IOException ex) {
}
return imReturn;
Comments off