Hardware Android

/*
Welcome to the source code for Android Programming Tutorials (http://commonsware.com/AndTutorials)!
Specifically, this is for Version 3.2 and above of this book.
For the source code for older versions of this book, please
visit:
https://github.com/commonsguy/cw-andtutorials
All of the source code in this archive is licensed under the
Apache 2.0 license except as noted.
The names of the top-level directories roughly correspond to a
shortened form of the chapter titles. Since chapter numbers
change with every release, and since some samples are used by
multiple chapters, I am loathe to put chapter numbers in the
actual directory names.
If you wish to use this code, bear in mind a few things:
* The projects are set up to be built by Ant, not by Eclipse.
  If you wish to use the code with Eclipse, you will need to
  create a suitable Android Eclipse project and import the
  code and other assets.
* You should delete build.xml from the project, then run
    android update project -p ...
  (where ... is the path to a project of interest)
  on those projects you wish to use, so the build files are
  updated for your Android SDK version.
*/
//
//src\apt\tutorial\AlarmActivity.java
package apt.tutorial;
import android.app.Activity;
import android.os.Bundle;
public class AlarmActivity extends Activity {
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.alarm);    
  }
}
//src\apt\tutorial\AppWidget.java
package apt.tutorial;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.widget.RemoteViews;
public class AppWidget extends AppWidgetProvider {
  @Override
  public void onUpdate(Context ctxt,
                        AppWidgetManager mgr,
                        int[] appWidgetIds) {
    if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB) {
      onHCUpdate(ctxt, mgr, appWidgetIds);
    }
    else {
      ctxt.startService(new Intent(ctxt, WidgetService.class));
    }
  }
  public void onHCUpdate(Context ctxt, AppWidgetManager appWidgetManager,
                          int[] appWidgetIds) {
    for (int i=0; i      Intent svcIntent=new Intent(ctxt, ListWidgetService.class);
      
      svcIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
      svcIntent.setData(Uri.parse(svcIntent.toUri(Intent.URI_INTENT_SCHEME)));
      
      RemoteViews widget=new RemoteViews(ctxt.getPackageName(),
                                          R.layout.widget);
      
      widget.setRemoteAdapter(appWidgetIds[i], R.id.restaurants,
                              svcIntent);
      Intent clickIntent=new Intent(ctxt, DetailForm.class);
      PendingIntent clickPI=PendingIntent
                              .getActivity(ctxt, 0,
                                            clickIntent,
                                            PendingIntent.FLAG_UPDATE_CURRENT);
      
      widget.setPendingIntentTemplate(R.id.restaurants, clickPI);
      appWidgetManager.updateAppWidget(appWidgetIds[i], widget);
    }
    
    super.onUpdate(ctxt, appWidgetManager, appWidgetIds);
  }
}
//src\apt\tutorial\DetailForm.java
package apt.tutorial;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
public class DetailForm extends FragmentActivity {
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.detail_activity);
  }
  
  @Override
  public void onResume() {
    super.onResume();
    
    String restaurantId=getIntent().getStringExtra(LunchList.ID_EXTRA);
    
    if (restaurantId!=null) {
      DetailFragment details=(DetailFragment)getSupportFragmentManager()
                                                .findFragmentById(R.id.details);
                                
      if (details!=null) {
        details.loadRestaurant(restaurantId);
      }
    }
  }
}
//src\apt\tutorial\DetailFragment.java
package apt.tutorial;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;
public class DetailFragment extends Fragment {
  private static final String ARG_REST_ID="apt.tutorial.ARG_REST_ID";
  EditText name=null;
  EditText address=null;
  EditText notes=null;
  EditText feed=null;
  RadioGroup types=null;
  RestaurantHelper helper=null;
  String restaurantId=null;
  TextView location=null;
  LocationManager locMgr=null;
  double latitude=0.0d;
  double longitude=0.0d;
  
  public static DetailFragment newInstance(long id) {
    DetailFragment result=new DetailFragment();
    Bundle args=new Bundle();
    args.putString(ARG_REST_ID, String.valueOf(id));
    result.setArguments(args);
    return(result);
  }
  
  @Override
  public void onCreate(Bundle state) {
    super.onCreate(state);
    
    setHasOptionsMenu(true);
  }
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
                            Bundle savedInstanceState) {
    return(inflater.inflate(R.layout.detail_form, container, false));
  }
  
  @Override
  public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    
    locMgr=(LocationManager)getActivity().
                              getSystemService(Context.LOCATION_SERVICE);
    
    name=(EditText)getView().findViewById(R.id.name);
    address=(EditText)getView().findViewById(R.id.addr);
    notes=(EditText)getView().findViewById(R.id.notes);
    types=(RadioGroup)getView().findViewById(R.id.types);
    feed=(EditText)getView().findViewById(R.id.feed);
    location=(TextView)getView().findViewById(R.id.location);
    
    Bundle args=getArguments();
    
    if (args!=null) {
      loadRestaurant(args.getString(ARG_REST_ID));
    }
  }
  
  @Override
  public void onPause() {
    save();
    getHelper().close();
    locMgr.removeUpdates(onLocationChange);
    
    super.onPause();
  }
  
  @Override
  public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.details_option, menu);
  }
  @Override
  public void onPrepareOptionsMenu(Menu menu) {
    if (restaurantId==null) {
      menu.findItem(R.id.location).setEnabled(false);
      menu.findItem(R.id.map).setEnabled(false);
    }
  }
  
  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    if (item.getItemId()==R.id.feed) {
      if (isNetworkAvailable()) {
        Intent i=new Intent(getActivity(), FeedActivity.class);
        
        i.putExtra(FeedActivity.FEED_URL, feed.getText().toString());
        startActivity(i);
      }
      else {
        Toast
          .makeText(getActivity(), "Sorry, the Internet is not available",
                    Toast.LENGTH_LONG)
          .show();
      }
    
      return(true);
    }
    else if (item.getItemId()==R.id.location) {
      locMgr.requestLocationUpdates(LocationManager.GPS_PROVIDER,
                                    0, 0, onLocationChange);
      
      return(true);
    }
    else if (item.getItemId()==R.id.map) {
      Intent i=new Intent(getActivity(), RestaurantMap.class);
      
      i.putExtra(RestaurantMap.EXTRA_LATITUDE, latitude);
      i.putExtra(RestaurantMap.EXTRA_LONGITUDE, longitude);
      i.putExtra(RestaurantMap.EXTRA_NAME, name.getText().toString());
      
      startActivity(i);
      
      return(true);
    }
    return(super.onOptionsItemSelected(item));
  }
  
  public void loadRestaurant(String restaurantId) {
    this.restaurantId=restaurantId;
    
    if (restaurantId!=null) {
      load();
    }
  }
  
  private boolean isNetworkAvailable() {
    ConnectivityManager cm=(ConnectivityManager)getActivity().
                                                  getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo info=cm.getActiveNetworkInfo();
    
    return(info!=null);
  }
  
  private RestaurantHelper getHelper() {
    if (helper==null) {
      helper=new RestaurantHelper(getActivity());
    }
    
    return(helper);
  }
  
  private void load() {
    Cursor c=getHelper().getById(restaurantId);
    c.moveToFirst();    
    name.setText(getHelper().getName(c));
    address.setText(getHelper().getAddress(c));
    notes.setText(getHelper().getNotes(c));
    feed.setText(getHelper().getFeed(c));
    
    if (getHelper().getType(c).equals("sit_down")) {
      types.check(R.id.sit_down);
    }
    else if (getHelper().getType(c).equals("take_out")) {
      types.check(R.id.take_out);
    }
    else {
      types.check(R.id.delivery);
    }
    
    latitude=getHelper().getLatitude(c);
    longitude=getHelper().getLongitude(c);
    
    location.setText(String.valueOf(latitude)
                      +", "
                      +String.valueOf(longitude));
    
    c.close();
  }
  
  private void save() {
    if (name.getText().toString().length()>0) {
      String type=null;
      
      switch (types.getCheckedRadioButtonId()) {
        case R.id.sit_down:
          type="sit_down";
          break;
        case R.id.take_out:
          type="take_out";
          break;
        default:
          type="delivery";
          break;
      }
  
      if (restaurantId==null) {
        getHelper().insert(name.getText().toString(),
                            address.getText().toString(), type,
                            notes.getText().toString(),
                            feed.getText().toString());
      }
      else {
        getHelper().update(restaurantId, name.getText().toString(),
                            address.getText().toString(), type,
                            notes.getText().toString(),
                            feed.getText().toString());
      }
    }
  }
  
  LocationListener onLocationChange=new LocationListener() {
    public void onLocationChanged(Location fix) {
      getHelper().updateLocation(restaurantId, fix.getLatitude(),
                                  fix.getLongitude());
      location.setText(String.valueOf(fix.getLatitude())
                      +", "
                      +String.valueOf(fix.getLongitude()));
      locMgr.removeUpdates(onLocationChange);
      
      Toast
        .makeText(getActivity(), "Location saved",
                  Toast.LENGTH_LONG)
        .show();
    }
    
    public void onProviderDisabled(String provider) {
      // required for interface, not used
    }
    
    public void onProviderEnabled(String provider) {
      // required for interface, not used
    }
    
    public void onStatusChanged(String provider, int status,
                                  Bundle extras) {
      // required for interface, not used
    }
  };
}
//src\apt\tutorial\EditPreferences.java
package apt.tutorial;
import android.app.Activity;
import android.content.ComponentName;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
public class EditPreferences extends PreferenceActivity {
  SharedPreferences prefs=null;
  
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    
    addPreferencesFromResource(R.xml.preferences);
  }
  
  @Override
  public void onResume() {
    super.onResume();
    
    prefs=PreferenceManager.getDefaultSharedPreferences(this);
    prefs.registerOnSharedPreferenceChangeListener(onChange);
  }
  
  @Override
  public void onPause() {
    prefs.unregisterOnSharedPreferenceChangeListener(onChange);
    
    super.onPause();
  }
  
  SharedPreferences.OnSharedPreferenceChangeListener onChange=
    new SharedPreferences.OnSharedPreferenceChangeListener() {
    public void onSharedPreferenceChanged(SharedPreferences prefs,
                                          String key) {
      if ("alarm".equals(key)) {
        boolean enabled=prefs.getBoolean(key, false);
        int flag=(enabled ?
                    PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
        ComponentName component=new ComponentName(EditPreferences.this,
                                                  OnBootReceiver.class);
        
        getPackageManager()
          .setComponentEnabledSetting(component,
                                      flag,
                                      PackageManager.DONT_KILL_APP);
          
        if (enabled) {
          OnBootReceiver.setAlarm(EditPreferences.this);
        }
        else {
          OnBootReceiver.cancelAlarm(EditPreferences.this);
        }
      }
      else if ("alarm_time".equals(key)) {
        OnBootReceiver.cancelAlarm(EditPreferences.this);
        OnBootReceiver.setAlarm(EditPreferences.this);
      }
    }
  };
}
//src\apt\tutorial\FeedActivity.java
package apt.tutorial;
import android.app.AlertDialog;
import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import org.mcsoxford.rss.RSSItem;
import org.mcsoxford.rss.RSSFeed;
public class FeedActivity extends ListActivity {
  public static final String FEED_URL="apt.tutorial.FEED_URL";
  private InstanceState state=null;
  
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    
    state=(InstanceState)getLastNonConfigurationInstance();
    
    if (state==null) {
      state=new InstanceState();
      state.handler=new FeedHandler(this);
      
      Intent i=new Intent(this, FeedService.class);
      
      i.putExtra(FeedService.EXTRA_URL,
                 getIntent().getStringExtra(FEED_URL));
      i.putExtra(FeedService.EXTRA_MESSENGER,
                 new Messenger(state.handler));
      
      startService(i);
    }
    else {
      if (state.handler!=null) {
        state.handler.attach(this);
      }
      
      if (state.feed!=null) {
        setFeed(state.feed);
      }
    }
  }
  
  @Override
  public Object onRetainNonConfigurationInstance() {
    if (state.handler!=null) {
      state.handler.detach();
    }
    
    return(state);
  }
  
  private void setFeed(RSSFeed feed) {
    state.feed=feed;
    setListAdapter(new FeedAdapter(feed));
  }
  
  private void goBlooey(Throwable t) {
    AlertDialog.Builder builder=new AlertDialog.Builder(this);
    
    builder
      .setTitle("Exception!")
      .setMessage(t.toString())
      .setPositiveButton("OK", null)
      .show();
  }
  
  private static class InstanceState {
    RSSFeed feed=null;
    FeedHandler handler=null;
  }
  
  private class FeedAdapter extends BaseAdapter {
    RSSFeed feed=null;
    
    FeedAdapter(RSSFeed feed) {
      super();
      
      this.feed=feed;
    }
    
    @Override
    public int getCount() {
      return(feed.getItems().size());
    }
    
    @Override
    public Object getItem(int position) {
      return(feed.getItems().get(position));
    }
    
    @Override
    public long getItemId(int position) {
      return(position);
    }
    
    @Override
    public View getView(int position, View convertView,
                        ViewGroup parent) {
      View row=convertView;
      
      if (row==null) {                          
        LayoutInflater inflater=getLayoutInflater();
        
        row=inflater.inflate(android.R.layout.simple_list_item_1,
                             parent, false);
      }
      
      RSSItem item=(RSSItem)getItem(position);
      
      ((TextView)row).setText(item.getTitle());
      
      return(row);
    }
  }
  
  private static class FeedHandler extends Handler {
    FeedActivity activity=null;
    
    FeedHandler(FeedActivity activity) {
      attach(activity);
    }
    
    void attach(FeedActivity activity) {
      this.activity=activity;
    }
    
    void detach() {
      this.activity=null;
    }
    
    @Override
    public void handleMessage(Message msg) {
      if (msg.arg1==RESULT_OK) {
        activity.setFeed((RSSFeed)msg.obj);
      }
      else {
        activity.goBlooey((Exception)msg.obj);
      }
    }
  };
}
//src\apt\tutorial\FeedService.java
package apt.tutorial;
import android.app.Activity;
import android.app.IntentService;
import android.content.Intent;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;
import org.mcsoxford.rss.RSSItem;
import org.mcsoxford.rss.RSSFeed;
import org.mcsoxford.rss.RSSReader;
public class FeedService extends IntentService {
  public static final String EXTRA_URL="apt.tutorial.EXTRA_URL";
  public static final String EXTRA_MESSENGER="apt.tutorial.EXTRA_MESSENGER";
  public FeedService() {
    super("FeedService");
  }
  
  @Override
  public void onHandleIntent(Intent i) {
    RSSReader reader=new RSSReader();
    Messenger messenger=(Messenger)i.getExtras().get(EXTRA_MESSENGER);
    Message msg=Message.obtain();
    
    try {
      RSSFeed result=reader.load(i.getStringExtra(EXTRA_URL));
      
      msg.arg1=Activity.RESULT_OK;
      msg.obj=result;
    }
    catch (Exception e) {
      Log.e("LunchList", "Exception parsing feed", e);
      msg.arg1=Activity.RESULT_CANCELED;
      msg.obj=e;
    }
    
    try {
      messenger.send(msg);
    }
    catch (Exception e) {
      Log.w("LunchList", "Exception sending results to activity", e);
    }
  }
}
//src\apt\tutorial\ListViewsFactory.java
package apt.tutorial;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;
public class ListViewsFactory
  implements RemoteViewsService.RemoteViewsFactory {
  private Context ctxt=null;
  private RestaurantHelper helper=null;
  private Cursor restaurants=null;
  public ListViewsFactory(Context ctxt, Intent intent) {
    this.ctxt=ctxt;
  }
  
  @Override
  public void onCreate() {
    helper=new RestaurantHelper(ctxt);
    restaurants=helper
                .getReadableDatabase()
                .rawQuery("SELECT _ID, name FROM restaurants", null);
  }
  
  @Override
  public void onDestroy() {
    restaurants.close();
    helper.close();
  }
  @Override
  public int getCount() {
    return(restaurants.getCount());
  }
  @Override
  public RemoteViews getViewAt(int position) {
    RemoteViews row=new RemoteViews(ctxt.getPackageName(),
                                     R.layout.widget_row);
    
    restaurants.moveToPosition(position);
    row.setTextViewText(android.R.id.text1,
                        restaurants.getString(1));
    Intent i=new Intent();
    Bundle extras=new Bundle();
    
    extras.putString(LunchList.ID_EXTRA,
                     String.valueOf(restaurants.getInt(0)));
    i.putExtras(extras);
    row.setOnClickFillInIntent(android.R.id.text1, i);
    return(row);
  }
  @Override
  public RemoteViews getLoadingView() {
    return(null);
  }
  
  @Override
  public int getViewTypeCount() {
    return(1);
  }
  @Override
  public long getItemId(int position) {
    restaurants.moveToPosition(position);
    
    return(restaurants.getInt(0));
  }
  @Override
  public boolean hasStableIds() {
    return(true);
  }
  @Override
  public void onDataSetChanged() {
    // no-op
  }
}
//src\apt\tutorial\ListWidgetService.java
package apt.tutorial;
import android.content.Intent;
import android.widget.RemoteViewsService;
public class ListWidgetService extends RemoteViewsService {
  @Override
  public RemoteViewsFactory onGetViewFactory(Intent intent) {
    return(new ListViewsFactory(this.getApplicationContext(),
                                 intent));
  }
}
//src\apt\tutorial\LunchFragment.java
package apt.tutorial;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.ListFragment;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.LayoutInflater;
import android.widget.AdapterView;
import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
public class LunchFragment extends ListFragment {
  public final static String ID_EXTRA="apt.tutorial._ID";
  Cursor model=null;
  RestaurantAdapter adapter=null;
  RestaurantHelper helper=null;
  SharedPreferences prefs=null;
  OnRestaurantListener listener=null;
  
  @Override
  public void onCreate(Bundle state) {
    super.onCreate(state);
    
    setHasOptionsMenu(true);
  }
  
  @Override
  public void onResume() {
    super.onResume();
    
    helper=new RestaurantHelper(getActivity());
    prefs=PreferenceManager.getDefaultSharedPreferences(getActivity());
    initList();
    prefs.registerOnSharedPreferenceChangeListener(prefListener);
  }
  
  @Override
  public void onPause() {
    helper.close();
    super.onPause();
  }
  
  @Override
  public void onListItemClick(ListView list, View view,
                              int position, long id) {
    if (listener!=null) {
      listener.onRestaurantSelected(id);
    }
  }
  
  @Override
  public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.option, menu);
  }
  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    if (item.getItemId()==R.id.add) {
      startActivity(new Intent(getActivity(), DetailForm.class));
      
      return(true);
    }
    else if (item.getItemId()==R.id.prefs) {
      startActivity(new Intent(getActivity(), EditPreferences.class));
    
      return(true);
    }
    return(super.onOptionsItemSelected(item));
  }
  
  public void setOnRestaurantListener(OnRestaurantListener listener) {
    this.listener=listener;
  }
  
  private void initList() {
    if (model!=null) {
      model.close();
    }
    
    model=helper.getAll(prefs.getString("sort_order", "name"));
    adapter=new RestaurantAdapter(model);
    setListAdapter(adapter);
  }
  
  private SharedPreferences.OnSharedPreferenceChangeListener prefListener=
   new SharedPreferences.OnSharedPreferenceChangeListener() {
    public void onSharedPreferenceChanged(SharedPreferences sharedPrefs,
                                          String key) {
      if (key.equals("sort_order")) {
        initList();
      }
    }
  };
    
  public interface OnRestaurantListener {
    void onRestaurantSelected(long id);
  }
  
  class RestaurantAdapter extends CursorAdapter {
    RestaurantAdapter(Cursor c) {
      super(getActivity(), c);
    }
    
    @Override
    public void bindView(View row, Context ctxt,
                         Cursor c) {
      RestaurantHolder holder=(RestaurantHolder)row.getTag();
      
      holder.populateFrom(c, helper);
    }
    
    @Override
    public View newView(Context ctxt, Cursor c,
                         ViewGroup parent) {
      LayoutInflater inflater=getActivity().getLayoutInflater();
      View row=inflater.inflate(R.layout.row, parent, false);
      RestaurantHolder holder=new RestaurantHolder(row);
      
      row.setTag(holder);
      
      return(row);
    }
  }
  
  static class RestaurantHolder {
    private TextView name=null;
    private TextView address=null;
    private ImageView icon=null;
    
    RestaurantHolder(View row) {
      name=(TextView)row.findViewById(R.id.title);
      address=(TextView)row.findViewById(R.id.address);
      icon=(ImageView)row.findViewById(R.id.icon);
    }
    
    void populateFrom(Cursor c, RestaurantHelper helper) {
      name.setText(helper.getName(c));
      address.setText(helper.getAddress(c));
  
      if (helper.getType(c).equals("sit_down")) {
        icon.setImageResource(R.drawable.ball_red);
      }
      else if (helper.getType(c).equals("take_out")) {
        icon.setImageResource(R.drawable.ball_yellow);
      }
      else {
        icon.setImageResource(R.drawable.ball_green);
      }
    }
  }
}
//src\apt\tutorial\LunchList.java
package apt.tutorial;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
public class LunchList extends FragmentActivity
    implements LunchFragment.OnRestaurantListener {
  public final static String ID_EXTRA="apt.tutorial._ID";
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    
    LunchFragment lunch
      =(LunchFragment)getSupportFragmentManager()
                            .findFragmentById(R.id.lunch);
    
    lunch.setOnRestaurantListener(this);
  }
  
  public void onRestaurantSelected(long id) {
    if (findViewById(R.id.details)==null) {
      Intent i=new Intent(this, DetailForm.class);
  
      i.putExtra(ID_EXTRA, String.valueOf(id));
      startActivity(i);
    }
    else {
      FragmentManager fragMgr=getSupportFragmentManager();
      DetailFragment details=(DetailFragment)fragMgr.findFragmentById(R.id.details);
      
      if (details==null) {
        details=DetailFragment.newInstance(id);
        FragmentTransaction xaction=fragMgr.beginTransaction();
        
        xaction
          .add(R.id.details, details)
          .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
          .addToBackStack(null)
          .commit();
      }
      else {
        details.loadRestaurant(String.valueOf(id));
      }
    }
  }
}
//src\apt\tutorial\OnAlarmReceiver.java
package apt.tutorial;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
public class OnAlarmReceiver extends BroadcastReceiver {
  private static final int NOTIFY_ME_ID=1337;
  @Override
  public void onReceive(Context ctxt, Intent intent) {
    SharedPreferences prefs=PreferenceManager.getDefaultSharedPreferences(ctxt);
    boolean useNotification=prefs.getBoolean("use_notification",
                                             true);
    if (useNotification) {
      NotificationManager mgr=
        (NotificationManager)ctxt.getSystemService(Context.NOTIFICATION_SERVICE);
      Notification note=new Notification(R.drawable.stat_notify_chat,
                                          "It's time for lunch!",
                                          System.currentTimeMillis());
      PendingIntent i=PendingIntent.getActivity(ctxt, 0,
                              new Intent(ctxt, AlarmActivity.class),
                                                0);
      
      note.setLatestEventInfo(ctxt, "LunchList",
                              "It's time for lunch! Aren't you hungry?",
                              i);
      note.flags|=Notification.FLAG_AUTO_CANCEL;
      
      mgr.notify(NOTIFY_ME_ID, note);
    }
    else {
      Intent i=new Intent(ctxt, AlarmActivity.class);
      
      i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      
      ctxt.startActivity(i);
    }
  }
}
//src\apt\tutorial\OnBootReceiver.java
package apt.tutorial;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import java.util.Calendar;
public class OnBootReceiver extends BroadcastReceiver {
  public static void setAlarm(Context ctxt) {
    AlarmManager mgr=(AlarmManager)ctxt.getSystemService(Context.ALARM_SERVICE);
    Calendar cal=Calendar.getInstance();
    SharedPreferences prefs=PreferenceManager.getDefaultSharedPreferences(ctxt);
    String time=prefs.getString("alarm_time", "12:00");
    
    cal.set(Calendar.HOUR_OF_DAY, TimePreference.getHour(time));
    cal.set(Calendar.MINUTE, TimePreference.getMinute(time));
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);
    
    if (cal.getTimeInMillis()      cal.add(Calendar.DAY_OF_YEAR, 1);
    }
    
android.util.Log.e("***OnBootReceiver", android.text.format.DateFormat.format("MM/dd/yy h:mmaa", cal).toString());
    
    mgr.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(),
                      AlarmManager.INTERVAL_DAY,
                      getPendingIntent(ctxt));
  }
  
  public static void cancelAlarm(Context ctxt) {
    AlarmManager mgr=(AlarmManager)ctxt.getSystemService(Context.ALARM_SERVICE);
    mgr.cancel(getPendingIntent(ctxt));
  }
  
  private static PendingIntent getPendingIntent(Context ctxt) {
    Intent i=new Intent(ctxt, OnAlarmReceiver.class);
    
    return(PendingIntent.getBroadcast(ctxt, 0, i, 0));
  }
  
  @Override
  public void onReceive(Context ctxt, Intent intent) {
android.util.Log.e("****OnBootReceiver", "got here");    
    setAlarm(ctxt);
  }
}
//src\apt\tutorial\RestaurantHelper.java
package apt.tutorial;
import android.content.Context;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
class RestaurantHelper extends SQLiteOpenHelper {
  private static final String DATABASE_NAME="lunchlist.db";
  private static final int SCHEMA_VERSION=3;
  
  public RestaurantHelper(Context context) {
    super(context, DATABASE_NAME, null, SCHEMA_VERSION);
  }
  
  @Override
  public void onCreate(SQLiteDatabase db) {
    db.execSQL("CREATE TABLE restaurants (_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, address TEXT, type TEXT, notes TEXT, feed TEXT, lat REAL, lon REAL);");
  }
  @Override
  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    if (newVersion<2) {
      db.execSQL("ALTER TABLE restaurants ADD COLUMN feed TEXT");
    }
    
    if (newVersion<3) {
      db.execSQL("ALTER TABLE restaurants ADD COLUMN lat REAL");
      db.execSQL("ALTER TABLE restaurants ADD COLUMN lon REAL");
    }
  }
  public Cursor getAll(String orderBy) {
    return(getReadableDatabase()
            .rawQuery("SELECT _id, name, address, type, notes, lat, lon FROM restaurants ORDER BY "+orderBy,
                      null));
  }
  
  public Cursor getById(String id) {
    String[] args={id};
    return(getReadableDatabase()
            .rawQuery("SELECT _id, name, address, type, notes, feed, lat, lon FROM restaurants WHERE _ID=?",
                      args));
  }
  
  public void insert(String name, String address,
                     String type, String notes,
                     String feed) {
    ContentValues cv=new ContentValues();
          
    cv.put("name", name);
    cv.put("address", address);
    cv.put("type", type);
    cv.put("notes", notes);
    cv.put("feed", feed);
    
    getWritableDatabase().insert("restaurants", "name", cv);
  }
  
  public void update(String id, String name, String address,
                     String type, String notes, String feed) {
    ContentValues cv=new ContentValues();
    String[] args={id};
    
    cv.put("name", name);
    cv.put("address", address);
    cv.put("type", type);
    cv.put("notes", notes);
    cv.put("feed", feed);
    
    getWritableDatabase().update("restaurants", cv, "_ID=?",
                                 args);
  }
  
  public void updateLocation(String id, double lat, double lon) {
    ContentValues cv=new ContentValues();
    String[] args={id};
    
    cv.put("lat", lat);
    cv.put("lon", lon);
    
    getWritableDatabase().update("restaurants", cv, "_ID=?",
                                 args);
  }
  
  public String getName(Cursor c) {
    return(c.getString(1));
  }
  
  public String getAddress(Cursor c) {
    return(c.getString(2));
  }
  
  public String getType(Cursor c) {
    return(c.getString(3));
  }
  
  public String getNotes(Cursor c) {
    return(c.getString(4));
  }
  
  public String getFeed(Cursor c) {
    return(c.getString(5));
  }
  
  public double getLatitude(Cursor c) {
    return(c.getDouble(6));
  }
  
  public double getLongitude(Cursor c) {
    return(c.getDouble(7));
  }
}
//src\apt\tutorial\RestaurantMap.java
package apt.tutorial;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.Toast;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.OverlayItem;
public class RestaurantMap extends MapActivity {
  public static final String EXTRA_LATITUDE="apt.tutorial.EXTRA_LATITUDE";
  public static final String EXTRA_LONGITUDE="apt.tutorial.EXTRA_LONGITUDE";
  public static final String EXTRA_NAME="apt.tutorial.EXTRA_NAME";
  private MapView map=null;
  
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.map);
    
    double lat=getIntent().getDoubleExtra(EXTRA_LATITUDE, 0);
    double lon=getIntent().getDoubleExtra(EXTRA_LONGITUDE, 0);
    
    map=(MapView)findViewById(R.id.map);
    
    map.getController().setZoom(17);
    
    GeoPoint status=new GeoPoint((int)(lat*1000000.0),
                                  (int)(lon*1000000.0));
    
    map.getController().setCenter(status);
    map.setBuiltInZoomControls(true);
    
    Drawable marker=getResources().getDrawable(R.drawable.marker);
    
    marker.setBounds(0, 0, marker.getIntrinsicWidth(),
                      marker.getIntrinsicHeight());
    
    map
      .getOverlays()
      .add(new RestaurantOverlay(marker, status,
                                  getIntent().getStringExtra(EXTRA_NAME)));
  }
  
   @Override
  protected boolean isRouteDisplayed() {
    return(false);
  }
    
  private class RestaurantOverlay extends ItemizedOverlay {
    private OverlayItem item=null;
    
    public RestaurantOverlay(Drawable marker, GeoPoint point,
                              String name) {
      super(marker);
      
      boundCenterBottom(marker);
      
      item=new OverlayItem(point, name, name);
      populate();
    }
    
    @Override
    protected OverlayItem createItem(int i) {
      return(item);
    }
     
    @Override
    protected boolean onTap(int i) {
      Toast.makeText(RestaurantMap.this,
                      item.getSnippet(),
                      Toast.LENGTH_SHORT).show();
      
      return(true);
    }
    
    @Override
    public int size() {
      return(1);
    }
  }
}
//src\apt\tutorial\TimePreference.java
package apt.tutorial;
import android.content.Context;
import android.content.res.TypedArray;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TimePicker;
public class TimePreference extends DialogPreference {
  private int lastHour=0;
  private int lastMinute=0;
  private TimePicker picker=null;
  
  public static int getHour(String time) {
    String[] pieces=time.split(":");
    
    return(Integer.parseInt(pieces[0]));
  }
  public static int getMinute(String time) {
    String[] pieces=time.split(":");
    
    return(Integer.parseInt(pieces[1]));
  }
  public TimePreference(Context ctxt, AttributeSet attrs) {
    super(ctxt, attrs);
    
    setPositiveButtonText("Set");
    setNegativeButtonText("Cancel");
  }
  @Override
  protected View onCreateDialogView() {
    picker=new TimePicker(getContext());
    
    return(picker);
  }
  
  @Override
  protected void onBindDialogView(View v) {
    super.onBindDialogView(v);
    
    picker.setCurrentHour(lastHour);
    picker.setCurrentMinute(lastMinute);
  }
  
  @Override
  protected void onDialogClosed(boolean positiveResult) {
    super.onDialogClosed(positiveResult);
    if (positiveResult) {
      lastHour=picker.getCurrentHour();
      lastMinute=picker.getCurrentMinute();
      
      String time=String.valueOf(lastHour)+":"+String.valueOf(lastMinute);
      
      if (callChangeListener(time)) {
        persistString(time);
      }
    }
  }
  @Override
  protected Object onGetDefaultValue(TypedArray a, int index) {
    return(a.getString(index));
  }
  @Override
  protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
    String time=null;
    
    if (restoreValue) {
      if (defaultValue==null) {
        time=getPersistedString("00:00");
      }
      else {
        time=getPersistedString(defaultValue.toString());
      }
    }
    else {
      time=defaultValue.toString();
    }
    
    lastHour=getHour(time);
    lastMinute=getMinute(time);
  }
}
//src\apt\tutorial\WidgetService.java
package apt.tutorial;
import android.app.IntentService;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.widget.RemoteViews;
public class WidgetService extends IntentService {
  public WidgetService() {
    super("WidgetService");
  }
    
  @Override
  public void onHandleIntent(Intent intent) {
    ComponentName me=new ComponentName(this, AppWidget.class);
    RemoteViews updateViews=new RemoteViews("apt.tutorial",
                                            R.layout.widget);
    RestaurantHelper helper=new RestaurantHelper(this);
    AppWidgetManager mgr=AppWidgetManager.getInstance(this);
    
    try {
      Cursor c=helper
                .getReadableDatabase()
                .rawQuery("SELECT COUNT(*) FROM restaurants", null);
      
      c.moveToFirst();
      
      int count=c.getInt(0);
      
      c.close();
      
      if (count>0) {
        int offset=(int)(count*Math.random());
        String args[]={String.valueOf(offset)};
        
        c=helper
            .getReadableDatabase()
            .rawQuery("SELECT _ID, name FROM restaurants LIMIT 1 OFFSET ?", args);
        c.moveToFirst();
        updateViews.setTextViewText(R.id.name, c.getString(1));
        
        Intent i=new Intent(this, DetailForm.class);
        
        i.putExtra(LunchList.ID_EXTRA, c.getString(0));
        
        PendingIntent pi=PendingIntent.getActivity(this, 0, i,
                                                    PendingIntent.FLAG_UPDATE_CURRENT);
        
        updateViews.setOnClickPendingIntent(R.id.name, pi);
      }
      else {
        updateViews.setTextViewText(R.id.title,
                                    this.getString(R.string.empty));
      }
    }
    finally {
      helper.close();
    }
    
    Intent i=new Intent(this, WidgetService.class);
    PendingIntent pi=PendingIntent.getService(this, 0, i, 0);
      
    updateViews.setOnClickPendingIntent(R.id.next, pi);
    mgr.updateAppWidget(me, updateViews);
  }
}
//
//res\xml-v11\widget_provider.xml

  android:minWidth="220dip"
  android:minHeight="220dip"
  android:updatePeriodMillis="1800000"
  android:initialLayout="@layout/widget"
  android:previewImage="@drawable/hc_widget_preview"
/>
//
//res\xml\preferences.xml
  xmlns:android="http://schemas.android.com/apk/res/android">
      android:key="sort_order"
    android:title="Sort Order"
    android:summary="Choose the order the list uses"
    android:entries="@array/sort_names"
    android:entryValues="@array/sort_clauses"
    android:dialogTitle="Choose a sort order" />
      android:key="alarm"
    android:title="Sound a Lunch Alarm"
    android:summary="Check if you want to know when it is time for lunch" />
      android:key="alarm_time"
    android:title="Lunch Alarm Time"
    android:defaultValue="12:00"
    android:summary="Set your desired time for the lunch alarm"
    android:dependency="alarm" />
      android:key="use_notification"
    android:title="Use a Notification"
    android:defaultValue="true"
    android:summary="Check if you want a status bar icon at lunchtime, or uncheck for a full-screen notice"
    android:dependency="alarm" />

//res\xml\widget_provider.xml

   android:minWidth="300dip"
   android:minHeight="79dip"
   android:updatePeriodMillis="1800000"
   android:initialLayout="@layout/widget"
/>
//
//res\values\arrays.xml


  
    By Name, Ascending
    By Name, Descending
    By Type
    By Address, Ascending
    By Address, Descending
  
  
    name ASC
    name DESC
    type, name ASC
    address ASC
    address DESC
  

//res\values\strings.xml


    LunchList
    No restaurants!

//
//res\menu\details_option.xml


      android:title="RSS Feed"
    android:icon="@drawable/ic_menu_friendslist"
  />
      android:title="Save Location"
    android:icon="@drawable/ic_menu_compass"
  />
      android:title="Show on Map"
    android:icon="@drawable/ic_menu_mapmode"
  />

//res\menu\option.xml


      android:title="Add"
    android:icon="@drawable/ic_menu_add"
    android:showAsAction="ifRoom|withText"
  />
      android:title="Settings"
    android:icon="@drawable/ic_menu_preferences"
  />

//
//res\layout-v11\widget.xml

  android:id="@+id/restaurants"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:layout_marginTop="3dp"
  android:layout_marginLeft="3dp"
  android:background="@drawable/widget_frame"
/>
//
//res\layout-large-land\main.xml

  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:orientation="horizontal"
>
      class="apt.tutorial.LunchFragment"
    android:id="@+id/lunch"
    android:layout_width="0dip"
    android:layout_height="fill_parent"
    android:layout_weight="40"
  />
      android:id="@+id/details"
    android:layout_width="0dip"
    android:layout_height="fill_parent"
    android:layout_weight="60"
  />

//
//res\layout-land\detail_form.xml
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:stretchColumns="2"
  >
  
    
          android:layout_span="2"
    />
  

  
    
          android:layout_span="2"
    />
  

  
    
    
              android:text="Take-Out"
      />
              android:text="Sit-Down"
      />
              android:text="Delivery"
      />
    
    
              android:singleLine="false"
        android:gravity="top"
        android:lines="2"
        android:scrollHorizontally="false"
        android:maxLines="2"
        android:maxWidth="140sp"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:hint="Notes"
      />
              android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:hint="Feed URL"
      />
              android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        >
                  android:layout_width="wrap_content"
          android:layout_height="wrap_content"
        />
                  android:text="(not set)"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
        />
      
    
  


//
//res\layout\alarm.xml

  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="It's time for lunch!"
  android:textSize="30sp"
  android:textStyle="bold"
/>
//res\layout\detail_activity.xml

  class="apt.tutorial.DetailFragment"
  android:id="@+id/details"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
/>
//res\layout\detail_form.xml
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:stretchColumns="1"
  >
  
    
    
  

  
    
    
  

  
    
    
              android:text="Take-Out"
      />
              android:text="Sit-Down"
      />
              android:text="Delivery"
      />
    
  

  
    
    
  

      android:singleLine="false"
    android:gravity="top"
    android:lines="2"
    android:scrollHorizontally="false"
    android:maxLines="2"
    android:maxWidth="200sp"
    android:layout_span="2"
    android:hint="Notes"
    android:layout_marginTop="4dip"
  />
      android:layout_span="2"
    android:hint="Feed URL"
  />

//res\layout\main.xml

  class="apt.tutorial.LunchFragment"
  android:id="@+id/lunch"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
/>
//res\layout\map.xml

  xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/map"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:apiKey="00yHj0k7_7vxbuQ9zwyXI4bNMJrAjYrJ9KKHgbQ"
  android:clickable="true" />
//res\layout\row.xml
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:orientation="horizontal"
  android:padding="4dip"
  >
      android:layout_width="wrap_content"
    android:layout_height="fill_parent"
    android:layout_alignParentTop="true"
    android:layout_alignParentBottom="true"
    android:layout_marginRight="4dip"
  />
      android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    >  
          android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:layout_weight="1"
      android:gravity="center_vertical"
      android:textStyle="bold"
      android:singleLine="true"
      android:ellipsize="end"
    />
          android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:layout_weight="1"
      android:gravity="center_vertical"
      android:singleLine="true"
      android:ellipsize="end"
    />
  

//res\layout\widget.xml

  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:background="@drawable/widget_frame"
>
      android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_alignParentLeft="true"
    android:layout_toLeftOf="@+id/next"
    android:textSize="10pt"
    android:textColor="#FFFFFFFF"
  />
      android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_alignParentRight="true"
    android:src="@drawable/ff"
  />

//res\layout\widget_row.xml


    android:id="@android:id/text1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAppearance="?android:attr/textAppearanceLarge"
    android:gravity="center_vertical"
    android:paddingLeft="6dip"
    android:minHeight="?android:attr/listPreferredItemHeight"
/>