title: Update UI using IntentService with ActivityRecognition
body: <p>I am using the new APIs for locations and trying the ActivityRecognition. I used the code base that its on the <a href="http://developer.android.com/training/location/activity-recognition.html" rel="nofollow">Google Developers Training</a> and everything went just fine until I wanted to update the UI. (I'm aware that you cannot update the UI directly on a service and that's why I'm using a handler/interface/something.)
  What I tried:</p>
  
  <p>Using an interface, but this was useless since I didn't find a way to get the Activity context from the IntentService.</p>
  
  <p>Also tried passing a handler to the IntentService for it to send a message with the action but for some reason whenever it reaches the call to this:</p>
  
  <pre><code> // If the intent contains an update
      if (ActivityRecognitionResult.hasResult(intent)) {
  </code></pre>
  
  <p>this just returns false since I'm passing the messenger with the handler. If I remove the line that add the extra containing the message it works just fine but I cannot update my UI.</p>
  
  <p>I tried removing the extra before starting the service but no success. Any Ideas how should I fix this?</p>
  
  <p>These are the codes I have:</p>
  
  <p><em><strong>Activity:</em></strong></p>
  
  <pre><code>public class MainActivity extends FragmentActivity implements ConnectionCallbacks, OnConnectionFailedListener {
  
  public static final int MILLISECONDS_PER_SECOND = 1000;
  public static final int DETECTION_INTERVAL_SECONDS = 20;
  public static final int DETECTION_INTERVAL_MILLISECONDS = MILLISECONDS_PER_SECOND * DETECTION_INTERVAL_SECONDS;
  
  private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
  private PendingIntent mActivityRecognitionPendingIntent;
  // Store the current activity recognition client
  private ActivityRecognitionClient mActivityRecognitionClient;
  private Context mContext;
  // Flag that indicates if a request is underway.
  private boolean mInProgress;
  private static final String TAG = "MainActivity";
  public static final String KEY_ACTION = "user_action";
  
  public enum REQUEST_TYPE {START, STOP}
  private REQUEST_TYPE mRequestType;
  private TextView tvAction;
  
  
  //FIXME I'm aware of this potential leak
  Handler handler = new Handler() {
      @Override
      public void handleMessage(Message msg) {
          Bundle reply = msg.getData();
          final String userAction = reply.getString(KEY_ACTION);
          tvAction.setText("You are now: "+userAction);
          // do whatever with the bundle here
      }
  };
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      mContext = this;
  
      tvAction = (TextView)findViewById(R.id.tv_action);
      // Start with the request flag set to false
      mInProgress = false;
  
      mActivityRecognitionClient =
          new ActivityRecognitionClient(mContext, this, this);
  
      Intent intent = new Intent(
              mContext, ActivityRecognitionIntentService.class);
              //putting this will make return false.
      intent.putExtra("messenger", new Messenger(handler)); 
  
  
      mActivityRecognitionPendingIntent =
          PendingIntent.getService(mContext, 0, intent,
          PendingIntent.FLAG_UPDATE_CURRENT);
  }
  
  private void startUpdates() {
  
      mRequestType = REQUEST_TYPE.START;
  
      // Check for Google Play services
  
      if (!servicesConnected()) {
          return;
      }
      // If a request is not already underway
      if (!mInProgress) {
          // Indicate that a request is in progress
          mInProgress = true;
          // Request a connection to Location Services
          mActivityRecognitionClient.connect();
      //
      }
  }
  
  
  private void stopUpdates() {
      // Set the request type to STOP
      mRequestType = REQUEST_TYPE.STOP;
      /*
       * Test for Google Play services after setting the request type.
       * If Google Play services isn't present, the request can be
       * restarted.
       */
      if (!servicesConnected()) {
          return;
      }
      // If a request is not already underway
      if (!mInProgress) {
          // Indicate that a request is in progress
          mInProgress = true;
          // Request a connection to Location Services
          mActivityRecognitionClient.connect();
      //
      }
  }
  
  @Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
      // Decide what to do based on the original request code
      switch (requestCode) {
      case CONNECTION_FAILURE_RESOLUTION_REQUEST:
          /*
           * If the result code is Activity.RESULT_OK, try to connect again
           */
          switch (resultCode) {
          case Activity.RESULT_OK:
              /*
               * Try the request again
               */
              break;
          }
  
      }
  }
  
  private boolean servicesConnected() {
      // Check that Google Play services is available
      int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
      // If Google Play services is available
      if (ConnectionResult.SUCCESS == resultCode) {
          // In debug mode, log the status
          Log.d("Activity Recognition", "Google Play services is available.");
          // Continue
          return true;
          // Google Play services was not available for some reason
      } else {
          // Get the error dialog from Google Play services
          Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(resultCode, this,
                  CONNECTION_FAILURE_RESOLUTION_REQUEST);
  
          // If Google Play services can provide an error dialog
          if (errorDialog != null) {
              // Create a new DialogFragment for the error dialog
              ErrorDialogFragment errorFragment = new ErrorDialogFragment();
              // Set the dialog in the DialogFragment
              errorFragment.setDialog(errorDialog);
              // Show the error dialog in the DialogFragment
              errorFragment.show(getSupportFragmentManager(), "Activity Recognition");
          }
          return false;
      }
  }
  
  // Define a DialogFragment that displays the error dialog
  public static class ErrorDialogFragment extends DialogFragment {
      // Global field to contain the error dialog
      private Dialog mDialog;
  
      // Default constructor. Sets the dialog field to null
      public ErrorDialogFragment() {
          super();
          mDialog = null;
      }
  
      // Set the dialog to display
      public void setDialog(Dialog dialog) {
          mDialog = dialog;
      }
  
      // Return a Dialog to the DialogFragment.
      @Override
      public Dialog onCreateDialog(Bundle savedInstanceState) {
          return mDialog;
      }
  }
  
  @Override
  public void onConnectionFailed(ConnectionResult connectionResult) {
      // Turn off the request flag
      mInProgress = false;
      /*
       * If the error has a resolution, start a Google Play services
       * activity to resolve it.
       */
      if (connectionResult.hasResolution()) {
          try {
              connectionResult.startResolutionForResult(
                      this,
                      CONNECTION_FAILURE_RESOLUTION_REQUEST);
          } catch (SendIntentException e) {
              // Log the error
              e.printStackTrace();
          }
      // If no resolution is available, display an error dialog
      } else {
          // Get the error code
          int errorCode = connectionResult.getErrorCode();
          // Get the error dialog from Google Play services
          Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
                  errorCode,
                  this,
                  CONNECTION_FAILURE_RESOLUTION_REQUEST);
          // If Google Play services can provide an error dialog
          if (errorDialog != null) {
              // Create a new DialogFragment for the error dialog
              ErrorDialogFragment errorFragment =
                      new ErrorDialogFragment();
              // Set the dialog in the DialogFragment
              errorFragment.setDialog(errorDialog);
              // Show the error dialog in the DialogFragment
              errorFragment.show(
                      getSupportFragmentManager(),
                      "Activity Recognition");
          }
      }
  }
  
  @Override
  public void onConnected(Bundle arg0) {
  
  
      switch (mRequestType) {
      case START:
          /*
       * Request activity recognition updates using the preset
       * detection interval and PendingIntent. This call is
       * synchronous.
       */
      mActivityRecognitionClient.requestActivityUpdates(
              DETECTION_INTERVAL_MILLISECONDS,
              mActivityRecognitionPendingIntent);
          break;
      case STOP :
          mActivityRecognitionClient.removeActivityUpdates(
                  mActivityRecognitionPendingIntent);
          break;
  
      default :
          Log.e(TAG, "Unknown request type in onConnected().");
          break;
      }
  
  
      /*
       * Since the preceding call is synchronous, turn off the
       * in progress flag and disconnect the client
       */
      mInProgress = false;
      mActivityRecognitionClient.disconnect();
  }
  
  @Override
  public void onDisconnected() {
      // Turn off the request flag
      mInProgress = false;
      // Delete the client
      mActivityRecognitionClient = null;
  }
  
  @Override
  protected void onPause() {
      super.onPause();
      stopUpdates();
  }
  
  @Override
  protected void onResume() {
      super.onResume();
      startUpdates();
  }
  
  @Override
  protected void onStop() {
      super.onStop();
      stopUpdates();
  }
  
  
  }
  </code></pre>
  
  <p><em><strong>IntentService</em></strong></p>
  
  <pre><code>import android.support.v4.app.NotificationCompat;
  import android.util.Log;
  
  import com.google.android.gms.location.ActivityRecognitionResult;
  import com.google.android.gms.location.DetectedActivity;
  
  public class ActivityRecognitionIntentService extends IntentService {
  
      // Formats the timestamp in the log
      private static final String DATE_FORMAT_PATTERN = "yyyy-MM-dd HH:mm:ss.SSSZ";
  
      private static final String TAG = "ActivityRecognitionIntentService";
  
      private static final String SHARED_PREFERENCES = "ActivityRecognition";
  
      private static final String KEY_PREVIOUS_ACTIVITY_TYPE = "KEY_PREVIOUS_ACTIVITY_TYPE";
  
      // A date formatter
      private SimpleDateFormat mDateFormat;
  
      // Store the app's shared preferences repository
      private SharedPreferences mPrefs;
      protected Messenger messenger;
  
      public ActivityRecognitionIntentService() {
          // Set the label for the service's background thread
          super("ActivityRecognitionIntentService");
  
      }
  
      public ActivityRecognitionIntentService(String name) {
          super(name);
      }
  
      @Override
      public void onStart(Intent intent, int startId) {
          Bundle extras = intent.getExtras();
          messenger = (Messenger) extras.get("messenger");
          intent.removeExtra("messenger");
          super.onStart(intent, startId);
      }
  
      /**
       * Called when a new activity detection update is available.
       */
      @Override
      protected void onHandleIntent(Intent intent) {
  
          // Get a handle to the repository
          Context mContext = getApplicationContext();
  
  
          mPrefs = mContext.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE);
  
          // Get a date formatter, and catch errors in the returned timestamp
          try {
              mDateFormat = (SimpleDateFormat) DateFormat.getDateTimeInstance();
          } catch (Exception e) {
              Log.e(TAG, getString(R.string.date_format_error));
          }
  
          // Format the timestamp according to the pattern, then localize the
          // pattern
          mDateFormat.applyPattern(DATE_FORMAT_PATTERN);
          mDateFormat.applyLocalizedPattern(mDateFormat.toLocalizedPattern());
  
          // If the intent contains an update
          if (ActivityRecognitionResult.hasResult(intent)) {
  
              // Get the update
              ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent);
  
  
              // Get the most probable activity from the list of activities in the
              // update
              DetectedActivity mostProbableActivity = result.getMostProbableActivity();
  
              // Get the confidence percentage for the most probable activity
              int confidence = mostProbableActivity.getConfidence();
  
              // Get the type of activity
              int activityType = mostProbableActivity.getType();
  
              // Check to see if the repository contains a previous activity
              if (!mPrefs.contains(KEY_PREVIOUS_ACTIVITY_TYPE)) {
  
                  // This is the first type an activity has been detected. Store
                  // the type
                  Editor editor = mPrefs.edit();
                  editor.putInt(KEY_PREVIOUS_ACTIVITY_TYPE, activityType);
                  editor.commit();
  
                  // If the repository contains a type
              } else if (
                  // The confidence level for the current activity is &gt; 50%
                      (confidence &gt;= 50)) {
  
                  // Notify the user
                  final String userAction = getNameFromType(activityType);
                  sendNotification(userAction);
  
                  if (messenger != null) {
                      Message msg = Message.obtain();
                      Bundle data = new Bundle();
                          data.putString(MainActivity.KEY_ACTION, userAction);
                      msg.setData(data); //put the data here
                      try {
                          messenger.send(msg);
                      } catch (RemoteException e) {
                              Log.i("error", "error");
                      }
                  }
              }
          }
      }
  
      /**
       * Post a notification to the user. The notification prompts the user to
       * click it to open the device's GPS settings
       */
      private void sendNotification(String type) {
  
          // Create a notification builder that's compatible with platforms &gt;=
          // version 4
          NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext());
  
          // Set the title, text, and icon
          builder.setContentTitle(getString(R.string.app_name)).setContentText(type)// getString(R.string.turn_on_GPS))
                  .setSmallIcon(R.drawable.ic_launcher)
  
                  // Get the Intent that starts the Location settings panel
                  .setContentIntent(getContentIntent());
  
          // Get an instance of the Notification Manager
          NotificationManager notifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
  
          // Build the notification and post it
          notifyManager.notify(0, builder.build());
      }
  
      /**
       * Get a content Intent for the notification
       * 
       * @return A PendingIntent that starts the device's Location Settings panel.
       */
      private PendingIntent getContentIntent() {
  
          // Set the Intent action to open Location Settings
          Intent gpsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
  
          // Create a PendingIntent to start an Activity
          return PendingIntent.getActivity(getApplicationContext(), 0, gpsIntent, PendingIntent.FLAG_UPDATE_CURRENT);
      }
  
  
      /**
       * Map detected activity types to strings
       * 
       * @param activityType
       *            The detected activity type
       * @return A user-readable name for the type
       */
      private String getNameFromType(int activityType) {
          switch (activityType) {
          case DetectedActivity.IN_VEHICLE:
              return "in_vehicle";
          case DetectedActivity.ON_BICYCLE:
              return "on_bicycle";
          case DetectedActivity.ON_FOOT:
              return "on_foot";
          case DetectedActivity.STILL:
              return "still";
          case DetectedActivity.UNKNOWN:
              return "unknown";
          case DetectedActivity.TILTING:
              return "tilting";
          }
          return "unknown";
      }
  }
  </code></pre>