June 5, 2012

Android drag and drop; it's broken...


(Disclaimer: All opinions expressed herein are my own and do not necessarily reflect that of my employer)

Drag and drop seems like a great thing to have in a platform. Since Honeycomb, Android has had one... Unfortunately, it's severely lacking, and in some places just flat out broken.

For the past several months I worked on a customized Launcher app for a rather large phone manufacturer. We used the official google Launcher from the recently released ICS as a starting point.

Now, Android is a great platform; they've done a lot right. Some things though, like their Launcher code, isn't as quality as I would have expected.

We spent the first month refactoring several parts of the code. Pretty early on I had the bright idea to get rid of the monolithic Launcher.java and separate out the home and menu into two activities. I ended up just having a HomeFragment and a MenuFragment in the same activity, which worked much better, but the process resulted in me refactoring the drag and and drop to use what's provided by the platform.

Before this point, the Launcher used a transparent view called DragLayer that covered the window, intercepted events, drew the drag preview etc.

This method doesn't work when you want a drag to be started in one activity and end in another because the drag is tied to a view. (Long pressing an icon in the menu and dropping on home.)

The platform's drag and drop is much more robust in that it's handled completely separate from your activity. Your activity more or less hands off a surface to the window manager which takes it from there.

This has the great benefit of not having to manage dragging yourself. Unfortunately we also lost a lot of flexibility.

The first thing I ran into was you can't change the preview of what you're dragging after the drag has started. We needed to turn a dragged app icon red when hovering over a trash icon. It's simply not possible, at least not with the public API. Using a lot of reflection and manually exposing various functionality I was able to get around this limitation; but most people don't have the luxury of knowing the platform isn't going to change from underneath them.

Next up, views receive every event EXCEPT for start and end if they have a child that returns true on a start event. You can see more details on that bug here: http://code.google.com/p/android/issues/detail?id=25073
Or maybe I'm just using it wrong?

The next problem I ran up against was a lot trickier to figure out. Basically, it's possible to receive two drop events in a single drag. And before android 4.0.3 it would cause a crash that looks like this:

    java.lang.IllegalStateException: reportDropResult() by non-recipient
        at com.android.server.wm.Session.reportDropResult(Session.java:311)
        at android.view.IWindowSession$Stub.onTransact(IWindowSession.java:375)
        at com.android.server.wm.Session.onTransact(Session.java:111)
        at android.os.Binder.execTransact(Binder.java:338)
        at dalvik.system.NativeStart.run(Native Method)

More details on that can be found here: http://code.google.com/p/android/issues/detail?id=25061

There are several more bugs with androids platform drag and drop that other members of the team found. And then there are problems inherit with it operating in a separate process. (Such as if you want to animate your drag shadow once it's been dropped, but that's a rant for another day.)

Long story short; life is fun.

No comments:

Post a Comment