Friday, October 16, 2009

Checking for Internet Availability on Android

I've been writing a good handful of net-enabled apps for Android and wanted a simple class to make sure the internet is available. If there is connectivity, the application should continue to run. Otherwise, the user should be prompted that the internet is not available, with the ability to "retry" or "cancel". If they choose to cancel, a different branch of code is executed. The behavior is seen in many of the built-in Android apps. My solution uses callbacks to get the job done. Hopefully the example speaks for itself, and the two required classes follow. You'll need an additional permission for the package to work- ACCESS_NETWORK_STATE. Also, Blogger is basically the worst possible way I could imagine posting code to the internet. If you have a better suggestion, I'd be happy to hear it.

Example

This snippet can be placed inside of an Activity, often in onCreate or similar lifecycle method.
WaitForInternetCallback callback =
 new WaitForInternetCallback(this) {
 public void onConnectionSuccess() {
  Log.d("test","We have internet!");
 }
 
 public void onConnectionFailure() {
  Log.d("test","failed to get internet connectivity...");
 }
 };
   
try {
 WaitForInternet.setCallback(callback);
} catch (SecurityException e) {
 Log.w("test","Could not check network state.", e);
 callback.onConnectionSuccess();
}
Here are the associated classes:

WaitForInternetCallback

package edu.stanford.prpl.junction.impl;

import android.app.Activity;

public abstract class WaitForInternetCallback {
 protected Activity mActivity;
 
 public WaitForInternetCallback(Activity activity) {
  mActivity=activity;
 }
 
 public abstract void onConnectionSuccess();
 public abstract void onConnectionFailure();
}

WaitForInternet

package edu.stanford.prpl.junction.impl;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.net.ConnectivityManager;
import android.util.Log;

public class WaitForInternet {
 
 /**
  * Check for internet connectivity.
  * The calling context must have permission to
  * access the device's network state.
  *
  * If the calling context does not have permission, an exception is thrown.
  *
  * @param WaitForInternetCallback
  * @return
  */
 public static void setCallback(final WaitForInternetCallback callback) {  
  final ConnectivityManager connMan = (ConnectivityManager) callback.mActivity
    .getSystemService(Context.CONNECTIVITY_SERVICE);

  final MutableBoolean isConnected = new MutableBoolean(connMan
    .getActiveNetworkInfo() != null
    && connMan.getActiveNetworkInfo().isConnected());
  if (isConnected.value) {
   callback.onConnectionSuccess();
   return;
  }
  
  final MutableBoolean isRetrying = new MutableBoolean(true);

  /* dialog */
  final AlertDialog.Builder connDialog = new AlertDialog.Builder(callback.mActivity);
  connDialog.setTitle("Network not available");
  connDialog
    .setMessage("Your phone cannot currently access the internet.");
  connDialog.setPositiveButton("Retry",
    new DialogInterface.OnClickListener() {
     public void onClick(DialogInterface dialogInterface, int i) {
      synchronized (isRetrying) {
       isRetrying.notify();
      }
     }
    });
  connDialog.setNegativeButton("Cancel",
    new DialogInterface.OnClickListener() {
     public void onClick(DialogInterface dialogInterface, int i) {
      synchronized (isRetrying) {
       isRetrying.value = false;
       isRetrying.notify();
      }
     }
    });

  new Thread() {
   public void run() {
    while (!isConnected.value && isRetrying.value) {
     callback.mActivity.runOnUiThread(new Thread() {
      @Override
      public void run() {
       connDialog.show();
      }
     });

     synchronized (isRetrying) {
      try {
       isRetrying.wait();
      } catch (InterruptedException e) {
       Log.w("junction", "Error waiting for retry lock", e);
      }
     }

     isConnected.value = (connMan.getActiveNetworkInfo() != null && connMan
       .getActiveNetworkInfo().isConnected());
    }
    
    if (isConnected.value) {
     callback.onConnectionSuccess();
    } else {
     callback.onConnectionFailure();
    }
   }
  }.start();
 }
}

class MutableBoolean {
 public boolean value = true;

 public MutableBoolean(boolean v) {
  value = v;
 }
}