Monday, December 19, 2011

Rails 3 Routes.rb - Helpers for Legacy Routes?

During our Rails 3 upgrade we ran into a non-obvious routing issue. Pre-upgrade we had a person route defined as follows:

match '/person/email' => 'person/emails#index'
map.resources :person do |person|
  person.resources :emails
end

Unfortunately we have to include the 'match' rule for backwards compatibility for older clients. This worked great until we upgraded to Rails 3. When we ran rake routes we got the following output:

person_emails     GET    /person/emails(.:format)          {:action=>"index", :controller=>"person/emails"}
                  POST   /person/emails(.:format)          {:action=>"create", :controller=>"person/emails"}
new_person_email  GET    /person/emails/new(.:format)      {:action=>"new", :controller=>"person/emails"}
edit_person_email GET    /person/emails/:id/edit(.:format) {:action=>"edit", :controller=>"person/emails"}
person_email      GET    /person/emails/:id(.:format)      {:action=>"show", :controller=>"person/emails"}
                  PUT    /person/emails/:id(.:format)      {:action=>"update", :controller=>"person/emails"}
                  DELETE /person/emails/:id(.:format)      {:action=>"destroy", :controller=>"person/emails"}
                         /person/email(.:format)           {:action=>"index", :controller=>"person/emails"}

Everything looks like it's supposed to. Notice that the last line in the 'rake routes' output is the 'match' rule. From within out app we were using the person_email_path( @email ) helper and would produce the URL /person/emails/34dga3-fg7899-aa645c. Everything was working perfectly until we upgraded to Rails 3. During the upgrade we avoided making drastic changes to routes.rb but we had to adapted it to Rails' new way of defining resources, but other than that our routes stayed the same. Our Rails 3 routes.rb file is below:

match '/person/email' => 'person/emails#index'
namespace :person do
  resources :emails
end

Now unexpectedly, the output of the person_email_path( @email ) helper produced the following /person/emails.34dga3-fg7899-aa645c. Additionally, after running rake routes, we observed the following output:

person_email             /person/email(.:format)           {:action=>"index", :controller=>"person/emails"}
person_emails     GET    /person/emails(.:format)          {:action=>"index", :controller=>"person/emails"}
                  POST   /person/emails(.:format)          {:action=>"create", :controller=>"person/emails"}
new_person_email  GET    /person/emails/new(.:format)      {:action=>"new", :controller=>"person/emails"}
edit_person_email GET    /person/emails/:id/edit(.:format) {:action=>"edit", :controller=>"person/emails"}
                  GET    /person/emails/:id(.:format)      {:action=>"show", :controller=>"person/emails"}
                  PUT    /person/emails/:id(.:format)      {:action=>"update", :controller=>"person/emails"}
                  DELETE /person/emails/:id(.:format)      {:action=>"destroy", :controller=>"person/emails"}

If you compare the two rake routes output closely you'll notice that after upgrading to Rails 3 the
match '/person/email' => 'person/emails#index'
rule hijacked the person_email_path helper. Previously this helper was used to generate the URL to the #show action, now after the upgrade it's being used to match the rule supporting backwards compatibility, which leads clients to the #index action.

Fortunately the fix for this was really simple, the hard put was tracking it down. All we had to do was move the rule supporting backwards compatibility below the resource definitions, unfortunately we have a lot of legacy clients and weren't able to remove rule. Below is the fixed routes.rb:

namespace :person do
  resources :emails
end
match '/person/email' => 'person/emails#index'

I don't know if this is a defect in Rails, or if Rails is just trying to be cute. I was ( and still am ) under the impression that the path and URL helpers were only applicable to resources. In any case, I hope this post helps someone.

Friday, December 02, 2011

Rails 3 Migration - Paperclip Processors

For those of you knee deep in a Rails 3 migration and have upgraded Paperclip, beware that the directory Paperclip loads custom processors from has changed. The problem with not knowing this before hand is you won't know things are busted until your app attempts to use the processor. In other words, this is a runtime exception. In our case this was a big deal, here's what was going on...

Children using our app have the ability to create drawings similar to MS Paint. When children save drawings they get uploaded to our servers, persisted locally, and subsequently uploaded to S3. After our Rails 3 upgrade, drawings were no longer getting persisted to our local storage nor S3. Root causing this was tricky because the persistence work is accomplished via DelayedJobs, so the exception wasn't in plain sight. After instrumenting our asynchronous task, we found out that Paperclip was failing to load our custom processor from the app/lib/paperclip_processors directory, which makes sense since our processor was located in the app/lib/paperclip directory. After checking the Paperclip release notes, sure enough they changed the processor directory.

Problem found, simple solution? You would think that the solution would be as simple as moving the process into a new directory, it wasn't. Our processor looked like this...

module Paperclip
  class Zeemark < Processor

    def initialize file, options = {}, attachment = nil
       // blah, blah...
    end

    def make
       // blah, blah...
    end
    
  end
end

The earlier version of Paperclip that we were using required custom processors to be part of the Paperclip module. This changed when we upgraded Paperclip, so simply copying our processor to app/lib/paperclip_processors introduced a different error. Specifically, Ruby complained that it cannot find the Zeemark class in file app/lib/paperlcip_processors/zeemark.rb, and rightly so. To fix this I stripped out the Paperclip module such that our processor looked like this...

class Zeemark < Paperclip::Processor

  def initialize file, options = {}, attachment = nil
     // blah, blah...
  end

  def make
     // blah, blah...
  end
    
end

Notice that I had to qualify parent Processor class with Paperclip::Processor. I hope someone out there finds this useful, deleting code is usually a good thing :-)

Monday, March 14, 2011

Cursor Adapters

If your Android app needs to present a large amount of data to your users, reading it from the database, being strict mode compliant, and maintaining a snappy GUI is a challenge. There are a lot of places where strict mode violations can creep in and produce undesirable side effects such as ANRs and a sluggish GUI. A good technique to help combat this problem is the use of Database Cursors and Android's CursorAdapter class.

Unfortunately, the pre-Honeycomb CursorAdapter class needs a little TLC before you can achieve strict mode compliance and a responsive GUI. The issue with the legacy implementation is that upon instantiation, it will execute the SQLite query. This is means, if you create your CursorAdapter on the Main ( or GUI ) thread, you are performing a database read on this thread. This is a big no-no, among other things, all DB reads and writes must be executed in a background thread.

But don't worry, it's not entirely your fault, this is how Google designed the API. The good news is you they've addressed it in Honeycomb and in their Honeycomb compatibility library. If you're interested in using this new API to construct CursorAdapters, checkout this link. Also, I will be doing a detailed blog post demonstrating how to use this API for pre-Honeycomb apps.

If you don't want to introduce yet-another-library into your Android project, here's a technique to create CursorAdatpers in the background. At a high-level you'll do the following

1.) In your Activity's onCreate( ) method, instantiate your CursorAdapter and pass in null for the Cursor argument

2.) In your Activity's onStart( ) method, kick off an AsyncTask that executes the desired DB query and returns a Cursor object

3.) In your AsyncTask's onPostExecute( ) method, call CursorAdapter.changeCursor(...) supplying the newly created Cursor.

4.) In your Activity's onDestroy( ) method, call CursorAdapter.changeCursor( null ). This will close the Cursor currently in use by the CursorAdapter.

I prefer this technique over using the Activity's built-in startManagingCursor( ) method because startManagingCursor( ) will automatically call Cursor.requery( ) for you. When it comes to UI threads and DB queries I prefer to be have control of what's going on. And to support my paranoia, Google is advising developers not to use Cursor.requery( ), which implies you shouldn't be using Activity.startManagerCursor( ) either. An official description can be found here.

Now, the code. Forgive the crudeness of the code, this is just a skeleton example used to provide an outline of the process. In the real-world, while your AsyncTask is running in the background, you will likely have a snazzy slash screen or animation entertaining your users while your app works diligently in the background. Enjoy.


public class MainActivity extends Activity { 
 /**
  * Flag used to discover if the activity has been destroyed while
  * CursorTask querying in the background
  */
 private boolean destroyed;
 
 /**
  * The CursorAdapter we'll be manipulating
  */
 private CursorAdapter cursorAdapter;
 
 @Override
 public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   destroyed = false;
   cursorAdapter = new ExampleCursorAdapter( this );
   // other initialization goes here...
 }
    
 @Override
 protected void onStart() {
   super.onStart();
   new CursorTask().execute();
   // other destruction goes here...
 }
    
 @Override
 protected void onDestroy() {
   destroyed = true;
   super.onDestroy();
   // calling changeCursor() will close the previous cursor
   if( cursorAdapter != null ) { cursorAdapter.changeCursor( null ); }
 }
    
 /**
  * Called when the CursorTask returns from the background
  * @param cur
  */
 private void onCursorLoaded( Cursor cur ) {
   // This is the tricky part. Since the activity could've been
   // destroyed during the time CursorTask spent in the background
   // we have to make sure we haven't been destoyed and that
   // this.cursorAdapter is stil valid. We should be extra 
   // careful here because  we don't want to leak DB Cursors.
   if( !destroyed ) {
      cursorAdapter.changeCursor( cur );
   }
   else {
      cur.close();
   }
 }
    
 /**
  * Utility class used to load a cursor in the background and then
  * notify the foreground that it has a new Cursor 
  *
  */
 private class CursorTask extends AsyncTask  {
   /**
    * Run the query in the background so we don't cause ANRs
    */
  @Override
  protected Cursor doInBackground( Void... params ) {
  // do your database query here and return a cursor, intentionally
  // omitted
    return null;
  }

  /**
   * After doInBackground() returns, call onCursorLoaded() to
   * change cursorAdapter's Cursor
   */
  @Override
  protected void onPostExecute( Cursor result ) {
    onCursorLoaded( result );
  }
 }
    
 /**
  * A simple CursorAdapter implementation used to illustrate loading a Cursor in
  * the background 
  *
  */
 private static class ExampleCursorAdapter extends CursorAdapter {

 private static final int NAME_COL = 0;
     
   public ExampleCursorAdapter( Context context ) {
     super( context, null, false );
   }

   @Override
   public void bindView(View view, Context context, Cursor cursor) {
     // this is where you'll take the data from the cursor and
     // bind to view.  view is what is returned from the newView(...)
     // method
     ( ( TextView ) view ).setText( cursor.getString( NAME_COL ) );
   }

   @Override
   public View newView( Context context, Cursor cursor, ViewGroup parent ) {
     // here you'll create the View you want to be displayed, I typically
     // don't touch the cursor in this method because bindView(...) gets
     // called immediately after this.
     return new TextView( context );
   }
 }
}

Thursday, March 10, 2011

Android's Application Object as a "Singleton"

When writing your Android app you may find it necessary to share data and services across multiple Activities. For example, if your app has session data, such as the currently logged in user, you will likely want to expose this information. When developing on the Android platform, the pattern for solving this problem is to have your android.app.Application instance own all global data, and then treat your Application instance as a Singleton.

When writing an Android app, you're guaranteed to only have one instance of the android.app.Application class so it's safe ( and recommended by the Google Android team ) to treat it as a Singleton. That is, you can safely add a static getInstance() method to your Application implementation. Below is an example.

public class AndroidApplication extends Application {

    private static AndroidApplication sInstance;

    public static AndroidApplication getInstance() {
      return sInstance;
    }

    @Override
    public void onCreate() {
      super.onCreate();  
      sInstance = this;
      sInstance.initializeInstance();
    }

    protected void initializeInstance() {
      // do all you initialization here
    }
}

This isn't the classical Singleton implementation but given the constraints of the Android framework, this is the closest thing we have, and it works.

Using this technique in our app has simplified and cleaned up our implementation. Also, it has made developing tests much easier. Using this technique in conjunction with the Robolectric testing framework, mock out our entire execution environment in a straight forward fashion.

Saturday, March 05, 2011

Installing Android's Compatibility Library

As I'm sure you all know, Google released a compatibility library for Android so pre-honeycomb applications can support the new Fragments API.  If you have a pre-Honeycomb application and want to take advantage of Fragments ( and a few other cool classes ), you can download the compatibility library using the Eclipse ADT plugin.  Specifically, using the Eclipse ADT plugin, you must download Android Compatibility Package, revision 1. After you download the library you need to include it in your project. The easiest way to do this is to copy it to your "lib" directory, assuming you have one, and then adjust your project's build path. The file you'll need to copy is /android-compatibility/v4/android-support-v4.jar. Copy this file to your Eclipse project, adjust your build path, and you'll be good to go. More to come later.

Friday, March 04, 2011

Android's Haptic Feedback

Android has some stock haptic controls, but if these don't satisfy your needs you can gain control of the device's vibrator for custom feedback.  Custom control of the device's vibrator requires permission, this is something you'll have to explicitly list in your AndroidManifest.xml (example below).  If you're paranoid about asking for permission or if you already have a long list of permissions, you may want to use the stock Android haptic feedback options.

Please note, the Motorola Xoom doesn't have a vibrator, therefore the examples below will compile and run, but you will not receive haptic feedback.

I'll start by showing the more complicated example first, custom haptic feedback.

Custom haptic feedback using the device's vibrator

1.) First Step, request the permission. Add the following line to you AndroidManifest.xml.

<uses-permission android:name="android.permission.VIBRATE" />

2.) Now define a listener to respond to touch events. It's not shown here but the CustomHapticListener class is actually a private non-static inner class of my Activity. This is because it needs access to the Context.getSystemService(...) method.

private class CustomHapticListener implements OnTouchListener {

  // Duration in milliseconds to vibrate
  private final int durationMs;
  
  public CustomHapticListener( int ms ) { 
    durationMs = ms;
  }   

  @Override
  public boolean onTouch( View v, MotionEvent event ) {
    if( event.getAction() == MotionEvent.ACTION_DOWN ){
      Vibrator vibe = ( Vibrator ) getSystemService( VIBRATOR_SERVICE );
      vibe.vibrate( durationMs );
    }
    return true;
  }
}

Lines 13 and 14 are the important ones. Line 13 gets a reference to the Vibrator service and line 14 vibrates the device. If you have requested the vibrate permission, line 14 will throw an exception.

3.) Lastly, register the listener. In your Activity's onCreate(...) method. You'll need to get a reference to the GUI element you want to attach haptic feedback to and then register the OnTouchListener we defined above.

@Override
public void onCreate( Bundle savedInstance ) {
  Button customBtn = ( Button ) findViewById( R.id.btn_custom );
  customBtn.setOnTouchListener( new CustomHapticListener( 100 ) );
}

That's it you're now in control of the haptic feedback, now onto using stock Android haptic feedback.

Stock Haptic Feedback Events

First thing's first, to use stock Android haptic feedback events you must enable it on View-by-View basis. That is, you must explicitly enable haptic feedback for each View. Enabling haptic feedback can be done declaratively in your layout file or programmatically in Java. To enable haptic feedback in your layout, simply add the android:hapticFeedbackEnabled="true" attribute to your View(s). Here's an abbreviated example:


Here's how you do the same thing in code:

Button keyboardTapBtn = ( Button ) findViewById( btnId );
keyboardTapBtn.setHapticFeedbackEnabled( true );

Now that haptic feedback has been enabled, the next step is to register an OnTouchListener and then perform the actual feedback. Below is an example of registering an OnTouchListener and performing haptic feedback when a user touches the View.

// Initialize some buttons with the stock Android haptic feedback values
private void initializeButtons() {
    // intialize the buttons with the standard Haptic feedback options
    initializeButton( R.id.btn_keyboardTap, HapticFeedbackConstants.KEYBOARD_TAP );
    initializeButton( R.id.btn_longPress,   HapticFeedbackConstants.LONG_PRESS );
    initializeButton( R.id.btn_virtualKey,  HapticFeedbackConstants.VIRTUAL_KEY );
}

// helper method to initialize single buttons and register an OnTouchListener
// to perform the haptic feedback
private void initializeButton( int btnId, int hapticId ) {
    Button btn = ( Button ) findViewById( btnId );
    btn.setOnTouchListener( new HapticTouchListener( hapticId ) );     
}    

// Class to handle touch events and respond with haptic feedback
private class HapticTouchListener implements OnTouchListener {

    private final int feedbackType;
     
    public HapticTouchListener( int type ) { feedbackType = type; }
     
    public int feedbackType() { return feedbackType; }
     
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        // only perform feedback when the user touches the view, as opposed
        // to lifting a finger off the view
        if( event.getAction() == MotionEvent.ACTION_DOWN ){
            // perform the feedback
            v.performHapticFeedback( feedbackType() );
        }
        return true;
    }
}

You'll notice on lines 3 - 5 I'm initializing 3 different buttons with three different haptic feedback constants. These are Android's stock values, 2 of the 3 seem to provide exactly the same feedback. The example code above is part of a test app I wrote to demonstrate haptic feedback and I could not tell the difference between HapticFeedbackConstants.LONG_PRESS and HapticFeedbackConstants.KeyboardTap. Also, HapticFeedbackConstants.VIRTUAL_KEY didn't provide any feedback, maybe I did something wrong...maybe not.

Line 31 is where the haptic feedback is performed. All in all, providing haptic feedback is pretty simple, if you want control of the device's vibrator make sure you request permission in your AndroidManifest.xml. If you choose to use the stock Android haptic feedback options, make sure you enable haptic feedback for your Views either in the layout or programmatically.