You might remember that there was a mysterious MD5 hash floating around the pairing process. Specifically, when you enter a pin code on the desktop iTunes client, it combines that code with the MDNS Pair value and hashes them. It then asks the device “does this match what you expected?” Because I wrote the Android client, I would naïvely always answer “yep, they match.”
Well, yesterday Michael Paul Bailey figured out the mystery behind that MD5 hash. I had tried bruteforcing various combinations of the pairing data, but never succeeded. It turns out that it boils down to just concatenating them together with the pin code digits separated by null characters.
He posted the full C++ code over on his blog, and I boiled it down to some spiffy Python here:
import StringIO, md5
pair = "4EA92B4292701F31"
passcode = "8222"
expected = "BEFF520F8280591AC0BBCB83B468FAA5"
merged = StringIO.StringIO()
merged.write(pair)
for c in passcode:
merged.write(c)
merged.write("\x00")
found = md5.new(merged.getvalue()).hexdigest()
print "expected =", expected
print "found =", found.upper()
So what does this mean? Previously, we could write DACP clients easily because they could always return “yep, the MD5 matches” without even checking it. (This is why you could use any 4-digit pin code you wanted.)
Now, with this algorithm, we can do more than just check pin codes–we can write DACP servers that can pair with the original iPhone/iPod Remote app. For example, last year I wrote a DACP server for Amarok, but never got around to releasing it because the pairing process was very ugly. Now I need to find some time to polish and release it.
Tomorrow at Google I/O I’ll be presenting some tips on how developers can save battery life when writing Android apps. I’m really stoked about all the stuff going on at I/O this year.
Late last week, Virgil Dobjanschi, who you might remember from ADC1, threw out the idea of writing an Android app for Google I/O that would have all the session details. Both of us brainstormed on Friday and came up with a quick design, and hacked through the holiday weekend to get an app working.
We just released the app on Market a few minutes ago, so go check it out! It lets you do all sorts of things, like star sessions you’re interested in, and search across the entire full-text abstracts for all sessions. Plus it includes handy maps to help you find the right rooms, and links directly to Google Moderator for the sessions using it.
The source for the whole app is also available on Google Code, so check that out if you’re looking for a peek behind-the-scenes. Some of the code is still a bit rough, but some good example code. So grab the app before you head down to Moscone West tomorrow morning, and have a great couple of days at I/O!
Over the past few months I’ve been working on the new AppWidget framework that was released as part of the Android 1.5 SDK. I wanted to write a really in-depth widget and share it, so I decided to write a forecast widget.
It offers multiple configurations (both a 2×1 and tiny 1×1), and updates four times daily with the latest forecasts. You can also insert multiple widgets to keep track of the weather in different locations. And tapping the widget brings up a detailed forecast for the next few days. Here are some quick screenshots:
Now, let’s talk about some of the details. We’re storing widget configuration and cached forecast data in a ContentProvider called ForecastProvider. Pretty simple stuff, and it offers two handy Uri’s to pick all forecasts for a widget using /appwidgets/[id]/forecasts, and only the most-current forecast using /appwidgets/[id]/forecast_at/[time]. These come in handy later when for the update process and details dialog. Also, WebserviceHelper performs most of the backend work of parsing XML returned from the API, and stuffing the forecasts and alerts into the ContentProvider.
As for updates, we maintain our own schedule that wakes us up each day 5:50AM, 11:50AM, 5:50PM, and 11:50PM. These times were mostly chosen to be equally spread out across the day, and to be ready before your 6AM alarm goes off. Notice I said your 6AM alarm, lol. The math also works out if you set your alarm for noon.
Similar to the Wiktionary example, we handle our updates in UpdateService because we’re probably doing API queries that could expose our AppWidgetProvider to the ANR timeout. However, we handle things slightly different because we’re pushing unique updates for each widget. We push updates like this:
Which is actually doing a synchronization dance behind the scenes to make sure we only ever have a single thread running updates. Multiple updates are just added to an internal queue. Once the update thread has cleared its work queue, the thread and Service terminate gracefully. This is very similar to how the Calendar widget handles it’s updates. During the update passes, we’re also a good citizen and only update forecasts if the data is more than 3 hours old. (We’re doing our part to keep the boot process spiffy and fast.)
The one final thing to mention is that when we’re building the PendingIntents for when you click on widgets, we’re using the ContentProvider Uri for the data field. This is because PendingIntents don’t keep unique extras bundles, and the details dialog needs a way of telling which widget was selected.
This is a pretty complex example, and it exercises most everything you’ll encounter when writing a widget. Not to mention it actually works and provides useful information! The data comes from the National Weather Service NDFD API, which offers awesome data under a public domain license. And the forecast icons came from the Tango Desktop Project, also under a public domain license.
I’ve been keeping busy writing all sorts of fun stuff lately, but a few weeks ago I was really fighting with Android’s logcat debugging stream. It dumps out tons of useful information, but it’s easy to get lost in the flood of text.
So I whipped up a quick Python script that reformats the logcat output into a colorful stream that is much easier to visually follow.
One feature I really like is that it allocates unique colors for each “tag” used. This makes it really easy to visually separate dozens of tags into their source apps, and makes it easy to pick your app out in the crowd. This was inspired by the irssi nickcolor script, and the best way to explain is using an example:
There, isn’t that better? Just pipe your adb logcat output through the Python script to get started, or you can run the script directly to invoke adb:
To keep things simple, it assumes you’re using an ANSI-compatible terminal (most xterms are fine), and it uses a quick hack to detect your column width for wrapping.
It only took about 30 minutes to write up, but it’s already saved me more than that in lost debugging time. Feel free to use it yourself, and improve on it–here’s the source code released under an Apache 2.0 license.
So I’ve been rushing to wrap up some Android side projects, and I’d like to get them out there before I start my new job tomorrow. OilCan is Greasemonkey on steroids for Android. It lets you customize any website by inserting JavaScript to change the website and help it reach into the Android world using intents.
Vimeo is being a bit odd with videos that don’t have sound. Start playing the video, then drag the seek bar slightly to bring it back to normal speed. Or let it play through the video fast and press play again to watch at normal speed.
Using intents to call other Android apps really powerful, and opens the door to all sorts of web-based apps. For example, you can make a JavaScript call to scan a barcode, pick a contact, or launch into Maps or other Android apps. You really have to peek at this video to get an idea of what it does:
Greasemonkey scripts are known for customizing websites to your personal tastes, and this can really help when working on a small screen. One of the scripts in the video above trims away extra columns and margins on Wikipedia pages, giving it more screen real estate.
So I’ve been rushing to wrap up some Android side projects, and I’d like to get them out there before I start my new job tomorrow.
GroupHome is an app that organizes all the apps you’ve installed on your phone. It automatically groups together apps using the categories shown in Android Market. The “all apps” drawer on my homescreen has become pretty cluttered, and this grouping approach helps you find apps faster.
Oh, and one feature I really like is that you can long-press on an app to uninstall it or view its details.
I wrote GroupHome in about 3 days last week, so it’s still a bit rushed and still rough around the edges. The three remaining things are full-text search, remembering expanded/collapsed groups on close, and moving the static JSON category string to a server.
I’ve been swamped lately with Android and moving across the country, but was able to give an interview to my hometown ABC affiliate in Duluth, Minnesota over Thanksgiving. It ended up running as the main story for their 10PM news, and was also picked up by the Minneapolis ABC affiliate.
Seriously, if you’re building a MythTV box, this is the tuner to buy–it has two HDTV/Cable TV tuners and everything runs across normal Ethernet, making it perfect for sticking in the attic right next to your antenna for better SNR margins. And yay, they mentioned that I was homeschooled!
It was a great way to show how CompareEverywhere works, and how it integrates with the Android platform. I was also able to talk about Stateful Drawables and the Hierarchy Viewer, which are both excellent tools for developers.
I’m excited to continue getting the word out there about CompareEverywhere, and more new features are still down the road, like seamless wishlist importing and more.
Added together, these facts mean it might be hard for developers to get their hands on devices, especially if the G1 becomes a hot holiday item. Geeks are known for their superhuman ability to stand in line for hours on end, but this might not be enough. If you don’t get your hands on a device, it’s important that you leverage the emulator to best reflect an actual G1 experience:
Showing things in actual size on your screen. I’ve seen several apps with touch targets that would be almost impossible to trigger in real life. Bigger targets are always better, and your users will thank you. To give you an idea, the home screen icons are 48px square, and default list items are 64px high. Anything below 48px is going to be pretty hard to for fingers to hit. (Side note: you should be using device independant pixels, or dip, instead of raw pixel values–they will automatically scale to future devices with different DPIs.)
The phone dimensions are 115mm tall by 55mm wide. However, the emulator shows up double that size on my Linux desktop and 24″ monitor. This makes it hard to judge how finger-friendly your interface is. Thankfully the emulator provides a nice switch to solve this:
./emulator -scale 0.5
This makes the emulator 50% of its original size on my desktop, which is about perfect compared to a physical ruler. You might have to play with that value to make it appear right. Remember that the phone screen is much higher DPI (dots-per-inch) than your monitor, so scaling might make things harder to read on the emulator. I find it’s best to develop at the default scale, then launch it scaled down for finger testing.
Getting an actual G1 emulator skin. The default emulator skin is getting very old after almost a year of staring at it. About a week ago, T-Mobile released an interesting Flash-based G1 emulator. It’s cute, but you can’t install your apps on it.
So earlier today I created a new emulator skin using the background from that Flash player. Just copy the G1 folder into your tools/lib/images/skins/ folder and launch using the command line below. To flip the keyboard in/out press Numpad 7 or Ctrl+F12. This simple scenery change can really help boost motivation:
A few months ago I wrote some code to let you “cross-off” things in a ListView. By wiping your finger left-to-right over an item it will add a strike-through effect to the text, and right-to-left would reverse the effect. Also, we’d like to store the crossed-off status in a backend database.
First, an overview of the problem. The ListView already captures clicks and long-touches for its items, and catches vertical scrolling and flinging to browse through the list. We’re interested in capturing any horizontal events while still letting normal touch events pass through to the ListView. The best way to handle this is by creating a wrapper View that will hold the ListView. Then we can use onInterceptTouchEvent() to watch for our cross-off actions, or otherwise ignore the touch events and let them trickle down to the ListView. (Thanks to Romain Guy for helping me find the correct way to capture these events.) Here’s the core of that capture code:
protected MotionEvent downStart = null;
public boolean onInterceptTouchEvent(MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
// keep track of the starting down-event
downStart = MotionEvent.obtain(event);
break;
case MotionEvent.ACTION_MOVE:
// if moved horizontally more than slop*2, capture the event for ourselves
float deltaX = event.getX() - downStart.getX();
if(Math.abs(deltaX) > ViewConfiguration.getTouchSlop() * 2)
return true;
break;
}
// otherwise let the event slip through to children
return false;
}
public boolean onTouchEvent(MotionEvent event) {
// check if we crossed an item
float targetWidth = this.getWidth() / 4;
float deltaX = event.getX() - downStart.getX(),
deltaY = event.getY() - downStart.getY();
boolean movedAcross = (Math.abs(deltaX) > targetWidth);
boolean steadyHand = (Math.abs(deltaX / deltaY) > 2);
if(movedAcross && steadyHand) {
boolean crossed = (deltaX > 0);
// figure out which child view we crossed
ListView list = (ListView)this.findViewById(android.R.id.list);
int position = list.pointToPosition((int)downStart.getX(), (int)downStart.getY());
// pass crossed event to any listeners
for(OnCrossListener listener : listeners) {
listener.onCross(position, crossed);
}
// and return true to consume this event
return true;
}
return false;
}
Using this approach lets us capture these cross-off gestures while still letting the ListView behave normally. I’m using this technique in my CompareEverywhere application, but earlier today I wrote a quick TodoList app to show it in action.
There are two other cool things we’re doing in the process: using a ViewBinder to custom render the created time of each item, and a stateful drawable to handle the checkmarks shown on each item. The ViewBinder correctly sets the strike-through text effect based on the COL_CROSSED database column, and also shows a custom caption with a format similar to “4 hours ago” based on the COL_CREATED column.
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
switch(view.getId()) {
case android.R.id.content:
// binding to parent container should set the crossed value
ImageView icon = (ImageView)view.findViewById(android.R.id.icon);
TextView text1 = (TextView)view.findViewById(android.R.id.text1),
text2 = (TextView)view.findViewById(android.R.id.text2);
// read crossed status and set text flags for strikethrough
boolean crossed = Boolean.valueOf(cursor.getString(columnIndex));
if(crossed) {
icon.setImageState(new int[] { android.R.attr.state_checked }, true);
text1.setPaintFlags(text1.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
text2.setPaintFlags(text2.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
} else {
icon.setImageState(new int[] { }, true);
text1.setPaintFlags(text1.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
text2.setPaintFlags(text2.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
}
return true;
case android.R.id.text2:
// binding to second textview should format time nicely
long created = cursor.getLong(columnIndex);
long now = System.currentTimeMillis() / 1000;
int minutes = (int)((now - created) / 60);
String nice = view.getContext().getString(R.string.bind_minutes, minutes);
if(minutes >= 60) {
int hours = (minutes / 60);
nice = view.getContext().getString(R.string.bind_hours, hours);
if(hours >= 24) {
int days = (hours / 24);
nice = view.getContext().getString(R.string.bind_days, days);
}
}
((TextView)view).setText(nice);
return true;
}
// otherwise fall through to other binding methods
return false;
}
And finally the code needed to connect the above ViewBinder to our SimpleCursorAdapter:
this.cross = (CrossView) this.findViewById(R.id.crossview);
this.list = (ListView) this.findViewById(android.R.id.list);
this.cross.addOnCrossListener(this);
// build adapter to show todo cursor
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.item_todo, cursor,
new String[] { db.FIELD_LIST_TITLE, db.FIELD_LIST_CREATED, db.FIELD_LIST_CROSSED },
new int[] { android.R.id.text1, android.R.id.text2, android.R.id.content });
adapter.setViewBinder(new CrossBinder());
list.setAdapter(adapter);
And it really is that simple. The SimpleCursorAdapter shows our todo list, and the ViewBinder handles showing COL_CREATED correctly, and assigning the overall crossed-off state based on COL_CROSSED.
There is some additional code in our Activity to handle onCross() events and update the database and ListView as needed. Finally I threw in a context menu to handle deleting and crossing-off items on phones without a touchscreen, and a normal menu for adding new items.
The last cool thing is the stateful drawable that I’m using to automatically change the icon based on crossed-off status and also on selection:
The ImageView automatically works its way down that list until it finds a drawable that matches all the state requirements, which makes it super easy to handle darkening icons when an item is selected. We also used ImageView.setImageState() earlier in our ViewBinder to correctly set the checked state.
For several Android projects I’ve needed a quick way of editing database rows without building an entire GUI. In the 0.9 SDK, we saw the introduction of the Preferences framework for storing simple application data, along with the PreferenceActivity family of classes for rapidly creating editable GUIs with almost zero effort. Wouldn’t it be awesome if we could edit database rows just as easily?
It is possible if you’re willing to do a little hacking. Essentially we’re providing a fake SharedPreferences up to a PreferenceActivity window. Instead of reading and saving from the application preferences, our SharedPreferences class will be using a specific database row for its storage. To make it all work, we then override the PreferenceActivity.getSharedPreferences() method to return our fake SharedPreferences instance.
Below is some example code of this approach being used in ConnectBot, an Android SSH client that I’ve been working on. First the host_prefs.xml file where we define the “preferences” that will be written to our database. Notice that I’m setting android:key to the database column names, which will make pairing the data up much easier later.
Now let’s look at the code we’ll be using for our PreferenceActivity. Nothing too special except we are overriding the getSharedPreferences(), which is how the PreferenceActivity will get its data source. In this example we’re doing a quick hack by passing the exact _id into the onCreate() as Intent.EXTRA_TITLE. Another way to handle this would be to pass around ContentProvider URIs, which would help clean up this code in several places.
With that approach, the best way to create the SharedPreferences instance would be by passing the URI as the name string to the getSharedPreferences() call. We could easily set the name string our PreferenceActivity requests by calling getPreferenceManager().setSharedPreferencesName(uri.toString()).
public class HostEditor extends PreferenceActivity
implements OnSharedPreferenceChangeListener {
protected CursorPreferenceHack pref;
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
Log.d(this.getClass().toString(), String.format("getSharedPreferences(name=%s)", name));
return this.pref;
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
HostDatabase db = new HostDatabase(this);
int id = this.getIntent().getIntExtra(Intent.EXTRA_TITLE, -1);
// TODO: we could pass through a specific ContentProvider uri here
//this.getPreferenceManager().setSharedPreferencesName(uri);
this.pref = new CursorPreferenceHack(db.getWritableDatabase(), db.TABLE_HOSTS, id);
this.pref.registerOnSharedPreferenceChangeListener(this);
this.addPreferencesFromResource(R.xml.host_prefs);
this.updateSummaries();
}
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
// update values on changed preference
this.updateSummaries();
}
protected void updateSummaries() {
// for all text preferences, set summary as current database value
for(String key : this.pref.values.keySet()) {
Preference pref = this.findPreference(key);
if(pref == null) continue;
if(pref instanceof CheckBoxPreference) continue;
pref.setSummary(this.pref.getString(key, ""));
}
}
}
And finally the CursorPreferenceHack source. It’s pretty simple, and just wraps the SharedPreferences calls as needed when converting them over into Cursor calls. The resulting GUI is easy to change, just remember to set your android:key values to database column names so they can be resolved correctly:
So there you have it, this approach allows you to make rapid GUI interfaces to safely update your database-backed information. It’s definitely a hack for now, but it makes these GUI interfaces a snap. I think the Preferences framework itself might use databases for its normal storage, so they might generalize this family of classes in the future.