Audible on Android (and other devices)
I love my new G1, but it doesn't work with Audible yet. Fortunately, I have a workaround. This guide describes how to get audiobooks onto a G1. You'll need a Mac and $40 worth of software.Download and install Tune4Mac. This app creates a virtual CD burner on your Mac, so when you tell iTunes to burn a CD, you don't actually need a blank CD-R. It's a clever hack, and the software works as promised. There's a Windows version, but I haven't tried it.
Register Tune4Mac. It's $39.95. You'll get the license key to your email immediately, paste that in the app's Registration dialog.
Configure AAC output in Tune4Mac. Tune4Mac automatically encodes audio to either MP3 or AAC. I tried MP3 encoding, and it was flaky, but AAC encoding worked just fine. Save this change by closing the dialog.
Add audiobooks to iTunes. Purchase DRM-protected books from Audible.com. If this is your first time using Audible, choose format #4, a high quality format that works with iTunes.
Create a playlist in iTunes. iTunes burns CDs using playlists, and we're going to be burning a virtual CD. I used a different playlist for each part of my two-part audiobook.
Prepare the fake burn. Click the 'Burn Disc' button in the bottom right corner of the iTunes app. Select
Tune4Mac Virtual CDRW
, Maximum Possible
, Audio CD
, none
and Include CD Text
. Click burn.Burn fake CDs. Click 'Burn' on the dialog, and then
Audio CDs
on the dialog that follows. It took an hour for my computer to burn all the virtual CDs for an entire book. During this time, Tune4Mac automatically cycles fake blank CDs so you can go grab a coffee.Use Finder to transfer the book to your device. I opened the
Documents
folder, and then the Tune4Mac
folder to find my audiobook file. I plugged my device into my Mac, causing an icon for it to show up in the Finder. I dragged the .m4a
file to this icon to transfer the book. Eject the device. In the Finder, click the device's eject icon before detaching it. Otherwise the transfer might not finish properly.
Enjoy! You can now enjoy audiobooks on your device. Unfortunately, some devices don't bookmark audiobooks, so you'll need to remember your place when you stop listening.
Disclaimer: I admit that this process is painful and expensive. Hopefully Audible follows Amazon's lead and makes DRM-free audiobooks available for purchase. That way, Audible's huge library will be available to a larger audience.
Guice punches erasure in the face!
Java 5 Type Erasure is frustrating. I'm coding along, enjoying the glorious type safety of generics. And then I try to do a little more with theT
s, and erasure shows up, "You can't do that!" I can't test an object if it really is a T
. I can't inspect T.class
using reflection. And I certainly can't new up a T
. Yuck.We're in a unique situation with Guice. Its type literals and injection points have complete generic information. It can follow long chains of type variables throughout an object model. In my app, I've got a
Robot<T>
that injects a Claw<T>
. When I request a Robot<Adamantium>
, Guice knows to find an adamantium claw.And now, this type information is available to application code. I can write a
Claw<T>
class that actually knows what T
is at runtime!Consider
GuardingList<T>
, which enforces type safety at runtime:class GuardingList<T> extends AbstractList<T> {
private final TypeLiteral<T> elementType;
private final ArrayList<T> delegate = new ArrayList<T>();
@Inject
GuardingList(TypeLiteral<T> elementType) {
this.elementType = elementType;
}
public boolean add(T t) {
checkArgument(elementType.getRawType().isInstance(t),
"Cannot add %s which is not of type %s", t, elementType);
return delegate.add(t);
}
public T get(int i) {
return delegate.get(i);
}
public int size() {
return delegate.size();
}
}
When injected, Guice applies its type knowledge to inject the correct TypeLiteral<T>
. The GuardingList
blows up when I add an invalid element: public static void main(String[] args) {
Injector injector = Guice.createInjector();
List<String> listOfString = injector.getInstance(
new Key<GuardingList<String>>() {});
listOfString.add("A");
listOfString.add("B");
/* totally unsafe, but this compiles and runs */
List<Object> rawList = (List) listOfString;
/* throws an IllegalArgumentException, since the GuardingList
knows that its elements must be Strings */
rawList.add(666);
}
Types are the natural currency of Java. Erasure is frustrating 'cause it takes them away from you at runtime. And now Guice can give 'em back.Reify today.