app/src/main/java/com/radioactiveyak/location_best_practices/utils/PlatformSpecificImplementationFactory.java
public class PlatformSpecificImplementationFactory {    
	
  /**
   * Create a new LastLocationFinder instance
   * @param context Context
   * @return LastLocationFinder
   */
  public static ILastLocationFinder getLastLocationFinder(Context context) {
    return PlacesConstants.SUPPORTS_GINGERBREAD ? new GingerbreadLastLocationFinder(context) : new LegacyLastLocationFinder(context);
  }
  
  /**
   * Create a new StrictMode instance.
   * @return StrictMode
   */
  public static IStrictMode getStrictMode() {
	if (PlacesConstants.SUPPORTS_HONEYCOMB)
      return new HoneycombStrictMode();
	else if (PlacesConstants.SUPPORTS_GINGERBREAD)
      return new LegacyStrictMode(); 
	else
      return null;
  }
  
  /**
   * Create a new LocationUpdateRequester
   * @param locationManager Location Manager
   * @return LocationUpdateRequester
   */
  public static LocationUpdateRequester getLocationUpdateRequester(LocationManager locationManager) {
    return PlacesConstants.SUPPORTS_GINGERBREAD ? new GingerbreadLocationUpdateRequester(locationManager) : new FroyoLocationUpdateRequester(locationManager);    
  }
  
  /**
   * Create a new SharedPreferenceSaver
   * @param context Context
   * @return SharedPreferenceSaver
   */
  public static SharedPreferenceSaver getSharedPreferenceSaver(Context context) {
    return  PlacesConstants.SUPPORTS_GINGERBREAD ? 
       new GingerbreadSharedPreferenceSaver(context) : 
       PlacesConstants.SUPPORTS_FROYO ? 
           new FroyoSharedPreferenceSaver(context) :
           new LegacySharedPreferenceSaver(context);
  }
}
app/src/main/java/com/radioactiveyak/location_best_practices/utils/base/SharedPreferenceSaver.java
public abstract class SharedPreferenceSaver {
  
  protected Context context;
  
  protected SharedPreferenceSaver(Context context) {
    this.context = context;
  }
  
  /**
   * Save the Shared Preferences modified through the Editor object.
   * @param editor Shared Preferences Editor to commit.
   * @param backup Backup to the cloud if possible.
   */
  public void savePreferences(SharedPreferences.Editor editor, boolean backup) {}
}
app/src/main/java/com/radioactiveyak/location_best_practices/utils/base/LocationUpdateRequester.java
public abstract class LocationUpdateRequester {
  
  protected LocationManager locationManager;
  
  protected LocationUpdateRequester(LocationManager locationManager) {
    this.locationManager = locationManager;
  }
  
  /**
   * Request active location updates. 
   * These updates will be triggered by a direct request from the Location Manager.
   * @param minTime Minimum time that should elapse between location update broadcasts.
   * @param minDistance Minimum distance that should have been moved between location update broadcasts.
   * @param criteria Criteria that define the Location Provider to use to detect the Location.
   * @param pendingIntent The Pending Intent to broadcast to notify the app of active location changes.
   */
  public void requestLocationUpdates(long minTime, long minDistance, Criteria criteria, PendingIntent pendingIntent) {}
  
  /**
   * Request passive location updates.
   * These updates will be triggered by locations received by 3rd party apps that have requested location updates.
   * The miniumim time and distance for passive updates will typically be longer than for active updates. The trick
   * is to balance the difference to minimize battery drain by maximize freshness.
   * @param minTime Minimum time that should elapse between location update broadcasts.
   * @param minDistance Minimum distance that should have been moved between location update broadcasts.
   * @param pendingIntent The Pending Intent to broadcast to notify the app of passive location changes.
   */
  public void requestPassiveLocationUpdates(long minTime, long minDistance, PendingIntent pendingIntent) {}
}
app/src/main/java/com/radioactiveyak/location_best_practices/receivers/LocationChangedReceiver.java
public class LocationChangedReceiver extends BroadcastReceiver {
  
  protected static String TAG = "LocationChangedReceiver";
  
  /**
   * When a new location is received, extract it from the Intent and use
   * it to start the Service used to update the list of nearby places.
   * 
   * This is the Active receiver, used to receive Location updates when 
   * the Activity is visible. 
   */
  @Override
  public void onReceive(Context context, Intent intent) {
    String locationKey = LocationManager.KEY_LOCATION_CHANGED;
    String providerEnabledKey = LocationManager.KEY_PROVIDER_ENABLED;
    if (intent.hasExtra(providerEnabledKey)) {
      if (!intent.getBooleanExtra(providerEnabledKey, true)) {
        Intent providerDisabledIntent = new Intent(PlacesConstants.ACTIVE_LOCATION_UPDATE_PROVIDER_DISABLED);
        context.sendBroadcast(providerDisabledIntent);    
      }
    }
    if (intent.hasExtra(locationKey)) {
      Location location = (Location)intent.getExtras().get(locationKey);
      Log.d(TAG, "Actively Updating place list");
      Intent updateServiceIntent = new Intent(context, PlacesConstants.SUPPORTS_ECLAIR ? EclairPlacesUpdateService.class : PlacesUpdateService.class);
      updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_LOCATION, location);
      updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_RADIUS, PlacesConstants.DEFAULT_RADIUS);
      updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_FORCEREFRESH, true);
      context.startService(updateServiceIntent);
    }
  }
}
app/src/main/java/com/radioactiveyak/location_best_practices/receivers/NewCheckinReceiver.java
public class NewCheckinReceiver extends BroadcastReceiver {
  
  protected static String TAG = "NewCheckinReceiver";
  
  /**
   * When a successful checkin is announced, extract the unique ID of the place
   * that's been checked in to, and pass this value to the CheckinNotification Service
   * when you start it.
   */
  @Override
  public void onReceive(Context context, Intent intent) {
    String id = intent.getStringExtra(PlacesConstants.EXTRA_KEY_ID);
    if (id != null) {
      Intent serviceIntent = new Intent(context, CheckinNotificationService.class);
      serviceIntent.putExtra(PlacesConstants.EXTRA_KEY_ID, id);
      context.startService(serviceIntent);
    }
	}
}
app/src/main/java/com/radioactiveyak/location_best_practices/UI/fragments/PlaceDetailFragment.java
public class PlaceDetailFragment extends Fragment implements LoaderCallbacks<Cursor> {
  
  /**
   * Factory that produces a new {@link PlaceDetailFragment} populated with
   * details corresponding to the reference / ID of the venue passed in.
   * @param reference Venue Reference
   * @param id Venue Unique ID
   * @return {@link PlaceDetailFragment}
   */
  public static PlaceDetailFragment newInstance(String reference, String id) {
    PlaceDetailFragment f = new PlaceDetailFragment();

    // Supply reference and ID inputs as arguments.
    Bundle args = new Bundle();
    args.putString(PlacesConstants.ARGUMENTS_KEY_REFERENCE, reference);
    args.putString(PlacesConstants.ARGUMENTS_KEY_ID, id);
    f.setArguments(args);
    
    return f;
  }
  
  protected static String TAG = "PlaceDetailFragment";
  
  protected String placeReference = null;
  protected String placeId = null;
  
  protected Handler handler = new Handler();
  protected Activity activity;
  protected TextView nameTextView;
  protected TextView phoneTextView;
  protected TextView addressTextView;
  protected TextView ratingTextView;
  protected TextView urlTextView;
  protected Button checkinButton;
  protected TextView checkedInText;
  
  public PlaceDetailFragment() {
    super();
  }
  
  public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState); 
    activity = getActivity();
        
    // Query the PlacesDetails Content Provider using a Loader to find
    // the details for the selected venue.
    if (placeId != null)
      getLoaderManager().initLoader(0, null, this);
    
    // Query the Shared Preferences to find the ID of the last venue checked in to.
    SharedPreferences sp = activity.getSharedPreferences(PlacesConstants.SHARED_PREFERENCE_FILE, Context.MODE_PRIVATE);
    String lastCheckin = sp.getString(PlacesConstants.SP_KEY_LAST_CHECKIN_ID, null);
    if (lastCheckin != null )
      checkedIn(lastCheckin);
  }
  
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {    
    View view = inflater.inflate(R.layout.place_detail, container, false);
    nameTextView = (TextView)view.findViewById(R.id.detail_name);
    phoneTextView = (TextView)view.findViewById(R.id.detail_phone);
    addressTextView = (TextView)view.findViewById(R.id.detail_address);
    ratingTextView = (TextView)view.findViewById(R.id.detail_rating);
    urlTextView = (TextView)view.findViewById(R.id.detail_url);
    checkinButton = (Button)view.findViewById(R.id.checkin_button);
    checkedInText = (TextView)view.findViewById(R.id.detail_checkin_text);
    
    checkinButton.setOnClickListener(checkinButtonOnClickListener);
    
    if (getArguments() != null) {
      placeReference = getArguments().getString(PlacesConstants.ARGUMENTS_KEY_REFERENCE);
      placeId = getArguments().getString(PlacesConstants.ARGUMENTS_KEY_ID);
    }
    return view;
  }

  @Override
  public void onResume() {
    super.onResume();
    
    // Always refresh the details on resume, but don't force
    // a refresh to minimize the network usage. Forced updates
    // are unnecessary as we force an update when a venue
    // is selected in the Place List Activity.
    if (placeReference != null && placeId != null)
      updatePlace(placeReference, placeId, false);
  }
  
  /**
   * Start the {@link PlaceDetailsUpdateService} to refresh the details for the 
   * selected venue.
   * @param reference Reference
   * @param id Unique Identifier
   * @param forceUpdate Force an update
   */
  protected void updatePlace(String reference, String id, boolean forceUpdate) {
    if (placeReference != null && placeId != null) {
      // Start the PlaceDetailsUpdate Service to query the server for details
      // on the specified venue. A "forced update" will ignore the caching latency 
      // rules and query the server.
      Intent updateServiceIntent = new Intent(activity, PlaceDetailsUpdateService.class);
      updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_REFERENCE, reference);
      updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_ID, id);
      updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_FORCEREFRESH, forceUpdate);
      activity.startService(updateServiceIntent);
    }
  }
  
  /**
   * {@inheritDoc}
   * Query the {@link PlaceDetailsContentProvider} for the Phone, Address, Rating, Reference, and Url
   * of the selected venue. 
   * TODO Expand the projection to include any other details you are recording in the Place Detail Content Provider.
   */
  public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    String[] projection = new String[] {PlaceDetailsContentProvider.KEY_NAME, 
      PlaceDetailsContentProvider.KEY_PHONE, 
      PlaceDetailsContentProvider.KEY_ADDRESS, 
      PlaceDetailsContentProvider.KEY_RATING, 
      PlaceDetailsContentProvider.KEY_REFERENCE, 
      PlaceDetailsContentProvider.KEY_URL};
    
    String selection = PlaceDetailsContentProvider.KEY_ID + "='" + placeId + "'";
    
    return new CursorLoader(activity, PlaceDetailsContentProvider.CONTENT_URI, 
        projection, selection, null, null);
  }
  
  /**
   * {@inheritDoc}
   * When the Loader has completed, schedule an update of the Fragment UI on the main application thread.
   */
  public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    if (data.moveToFirst()) {
      final String name = data.getString(data.getColumnIndex(PlaceDetailsContentProvider.KEY_NAME));
      final String phone = data.getString(data.getColumnIndex(PlaceDetailsContentProvider.KEY_PHONE));
      final String address = data.getString(data.getColumnIndex(PlaceDetailsContentProvider.KEY_ADDRESS));
      final String rating = data.getString(data.getColumnIndex(PlaceDetailsContentProvider.KEY_RATING));
      final String url = data.getString(data.getColumnIndex(PlaceDetailsContentProvider.KEY_URL));

      // If we don't have a place reference passed in, we need to look it up and update our details
      // accordingly.
      if (placeReference == null) {
        placeReference = data.getString(data.getColumnIndex(PlaceDetailsContentProvider.KEY_REFERENCE));
        updatePlace(placeReference, placeId, true);
      }
      
      handler.post(new Runnable () {
        public void run() {
          nameTextView.setText(name);
          phoneTextView.setText(phone);
          addressTextView.setText(address);
          ratingTextView.setText(rating);
          urlTextView.setText(url);
        }        
      });
    }
  }
  
  /**
   * {@inheritDoc}
   */
  public void onLoaderReset(Loader<Cursor> loader) {
    handler.post(new Runnable () {
      public void run() {
        nameTextView.setText("");
        phoneTextView.setText("");
        addressTextView.setText("");
        ratingTextView.setText("");
        urlTextView.setText("");
      }
    });
  }
  
  /**
   * When the Checkin Button is clicked start the {@link PlaceCheckinService} to checkin.
   */
  protected OnClickListener checkinButtonOnClickListener = new OnClickListener() {
    public void onClick(View view) {
      // TODO Pass in additional parameters to your checkin / rating / review service as appropriate
      // TODO In some cases you may prefer to open a new Activity with checkin details before initiating the Service.
      Intent checkinServiceIntent = new Intent(getActivity(), PlaceCheckinService.class);
      checkinServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_REFERENCE, placeReference);
      checkinServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_ID, placeId);
      checkinServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_TIME_STAMP, System.currentTimeMillis());
      getActivity().startService(checkinServiceIntent);
    }
  }; 
  
  /**
   * Checks to see if the currently displayed venue is the last place checked in to.
   * IF it is, it disables the checkin button and update the UI accordingly.
   * @param id Checked-in place ID
   */
  public void checkedIn(String id) {
    if (placeId == null)
      Log.e(TAG, "Place ID = null");
    boolean checkedIn = id != null && placeId != null && placeId.equals(id);
    checkinButton.setEnabled(!checkedIn);
    checkedInText.setVisibility(checkedIn ? View.VISIBLE : View.INVISIBLE);
  }
}
app/src/main/java/com/radioactiveyak/location_best_practices/UI/fragments/PlaceListFragment.java
public class PlaceListFragment extends ListFragment implements LoaderCallbacks<Cursor> {
  
  protected Cursor cursor = null;
  protected SimpleCursorAdapter adapter;
  protected PlaceActivity activity;
  
  @Override
  public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
        
    activity = (PlaceActivity)getActivity();
    
    // Create a new SimpleCursorAdapter that displays the name of each nearby
    // venue and the current distance to it.
    adapter = new SimpleCursorAdapter(
            activity,
            android.R.layout.two_line_list_item,
            cursor,                                              
            new String[] {PlacesContentProvider.KEY_NAME, PlacesContentProvider.KEY_DISTANCE},           
            new int[] {android.R.id.text1, android.R.id.text2},
            0);
    // Allocate the adapter to the List displayed within this fragment.
    setListAdapter(adapter);
    
    // Populate the adapter / list using a Cursor Loader. 
    getLoaderManager().initLoader(0, null, this);
  }
  
  /**
   * {@inheritDoc}
   * When a venue is clicked, fetch the details from your server and display the detail page.
   */
  @Override
  public void onListItemClick(ListView l, View v, int position, long theid) {
    super.onListItemClick(l, v, position, theid);
    
    // Find the ID and Reference of the selected venue.
    // These are needed to perform a lookup in our cache and the Google Places API server respectively.
    Cursor c = adapter.getCursor();
    c.moveToPosition(position);
    String reference = c.getString(c.getColumnIndex(PlacesContentProvider.KEY_REFERENCE));
    String id = c.getString(c.getColumnIndex(PlacesContentProvider.KEY_ID));
    
    // Initiate a lookup of the venue details usign the PlacesDetailsUpdateService.
    // Because this is a user initiated action (rather than a prefetch) we request
    // that the Service force a refresh.
    Intent serviceIntent = new Intent(activity, PlaceDetailsUpdateService.class);
    serviceIntent.putExtra(PlacesConstants.EXTRA_KEY_REFERENCE, reference);
    serviceIntent.putExtra(PlacesConstants.EXTRA_KEY_ID, id);
    serviceIntent.putExtra(PlacesConstants.EXTRA_KEY_FORCEREFRESH, true);        
    activity.startService(serviceIntent);
    
    // Request the parent Activity display the venue detail UI.
    activity.selectDetail(reference, id);
  }
     
  /**
   * {@inheritDoc}
   * This loader will return the ID, Reference, Name, and Distance of all the venues
   * currently stored in the {@link PlacesContentProvider}.
   */
  public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    String[] projection = new String[] {PlacesContentProvider.KEY_ID,PlacesContentProvider.KEY_NAME, PlacesContentProvider.KEY_DISTANCE, PlacesContentProvider.KEY_REFERENCE};
    
    return new CursorLoader(activity, PlacesContentProvider.CONTENT_URI, 
        projection, null, null, null);
  }

  /**
   * {@inheritDoc}
   * When the loading has completed, assign the cursor to the adapter / list.
   */
  public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    adapter.swapCursor(data);
  }

  /**
   * {@inheritDoc}
   */
  public void onLoaderReset(Loader<Cursor> loader) {
    adapter.swapCursor(null);
  }
}
app/src/main/java/com/radioactiveyak/location_best_practices/UI/fragments/CheckinFragment.java
public class CheckinFragment extends Fragment implements LoaderCallbacks<Cursor> {
  
  /**
   * Factory that return a new instance of the {@link CheckinFragment} 
   * populated with the details corresponding to the passed in venue
   * identifier.
   * @param id Identifier of the venue checked in to
   * @return A new CheckinFragment
   */
  public static CheckinFragment newInstance(String id) {
    CheckinFragment f = new CheckinFragment();

    // Supply id input as an argument.
    Bundle args = new Bundle();
    args.putString(PlacesConstants.ARGUMENTS_KEY_ID, id);
    f.setArguments(args);
    
    return f;
  }
  
  protected String placeId = null;
  protected Handler handler = new Handler();
  protected Activity activity;
  protected TextView checkPlaceNameTextView;
  
  public CheckinFragment() {
    super();
  }
  
  /**
   * Change the venue checked in to.
   * @param id Identifier of the venue checked in to
   */
  public void setPlaceId(String id) {
    // Update the place ID and restart the loader to update the UI.
    placeId = id; 
    if (placeId != null)
      getLoaderManager().restartLoader(0, null, this);
  }
  
  public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState); 
    activity = getActivity();
  
    // Populate the UI by initiating the loader to retrieve the 
    // details of the venue from the underlying Place Content Provider.
    if (placeId != null)
      getLoaderManager().initLoader(0, null, this);
  }
  
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {    
    View view = inflater.inflate(R.layout.checkin_box, container, false);
    checkPlaceNameTextView = (TextView)view.findViewById(R.id.checkin_place_name);
    
    if (getArguments() != null)
      placeId = getArguments().getString(PlacesConstants.ARGUMENTS_KEY_ID);
    
    return view;
  }

  @Override
  public void onResume() {
    super.onResume();
  }
  
  /**
   * {@inheritDoc}
   * This loader queries the {@link PlaceContentProvider} to extract the name of
   * the venue that has been checked in to.
   */
  public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    String[] projection = new String[] {PlaceDetailsContentProvider.KEY_NAME};
    
    String selection = PlaceDetailsContentProvider.KEY_ID + "='" + placeId + "'";
    
    return new CursorLoader(activity, PlaceDetailsContentProvider.CONTENT_URI, 
        projection, selection, null, null);
  }
  
  /**
   * {@inheritDoc}
   * When this load has finished, update the UI with the name of the venue.
   */
  public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    if (data.moveToFirst()) {
      final String venueName = data.getString(data.getColumnIndex(PlaceDetailsContentProvider.KEY_NAME));
      handler.post(new Runnable () {
        public void run() {
            checkPlaceNameTextView.setText(venueName);
        }        
      });
    }
  }
  
  /**
   * {@inheritDoc}
   */
  public void onLoaderReset(Loader<Cursor> loader) {
    handler.post(new Runnable () {
      public void run() {
        checkPlaceNameTextView.setText("");
      }
    });
  }
}
app/src/main/java/com/radioactiveyak/location_best_practices/services/EclairPlacesUpdateService.java
public class EclairPlacesUpdateService extends PlacesUpdateService {
  @Override
  protected void setIntentRedeliveryMode(boolean enable) {
    setIntentRedelivery(true);
  }
}
app/src/main/java/com/radioactiveyak/location_best_practices/receivers/PassiveLocationChangedReceiver.java
public class PassiveLocationChangedReceiver extends BroadcastReceiver {
  
  protected static String TAG = "PassiveLocationChangedReceiver";
  
  /**
   * When a new location is received, extract it from the Intent and use
   * it to start the Service used to update the list of nearby places.
   * 
   * This is the Passive receiver, used to receive Location updates from 
   * third party apps when the Activity is not visible. 
   */
  @Override
  public void onReceive(Context context, Intent intent) {
    String key = LocationManager.KEY_LOCATION_CHANGED;
    Location location = null;
    
    if (intent.hasExtra(key)) {
      // This update came from Passive provider, so we can extract the location
      // directly.
      location = (Location)intent.getExtras().get(key);      
    }
    else {
      // This update came from a recurring alarm. We need to determine if there
      // has been a more recent Location received than the last location we used.
      
      // Get the best last location detected from the providers.
      LegacyLastLocationFinder lastLocationFinder = new LegacyLastLocationFinder(context);
      location = lastLocationFinder.getLastBestLocation(PlacesConstants.MAX_DISTANCE, System.currentTimeMillis()-PlacesConstants.MAX_TIME);
      SharedPreferences prefs = context.getSharedPreferences(PlacesConstants.SHARED_PREFERENCE_FILE, Context.MODE_PRIVATE);
      
      // Get the last location we used to get a listing.
      long lastTime = prefs.getLong(PlacesConstants.SP_KEY_LAST_LIST_UPDATE_TIME, Long.MIN_VALUE);
      long lastLat = prefs.getLong(PlacesConstants.SP_KEY_LAST_LIST_UPDATE_LAT, Long.MIN_VALUE);
      long lastLng = prefs.getLong(PlacesConstants.SP_KEY_LAST_LIST_UPDATE_LNG, Long.MIN_VALUE);
      Location lastLocation = new Location(PlacesConstants.CONSTRUCTED_LOCATION_PROVIDER);
      lastLocation.setLatitude(lastLat);
      lastLocation.setLongitude(lastLng);

      // Check if the last location detected from the providers is either too soon, or too close to the last
      // value we used. If it is within those thresholds we set the location to null to prevent the update
      // Service being run unnecessarily (and spending battery on data transfers).
      if ((lastTime > System.currentTimeMillis()-PlacesConstants.MAX_TIME) ||
         (lastLocation.distanceTo(location) < PlacesConstants.MAX_DISTANCE))
        location = null;
    }
    
    // Start the Service used to find nearby points of interest based on the last detected location.
    if (location != null) {
      Log.d(TAG, "Passivly updating place list.");
      Intent updateServiceIntent = new Intent(context, PlacesConstants.SUPPORTS_ECLAIR ? EclairPlacesUpdateService.class : PlacesUpdateService.class);
      updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_LOCATION, location);
      updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_RADIUS, PlacesConstants.DEFAULT_RADIUS);
      updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_FORCEREFRESH, false);
      context.startService(updateServiceIntent);   
    }
  }
}
app/src/main/java/com/radioactiveyak/location_best_practices/services/PlacesUpdateService.java
public class PlacesUpdateService extends IntentService {  

  protected static String TAG = "PlacesUpdateService";
  
  protected ContentResolver contentResolver;
  protected SharedPreferences prefs;
  protected Editor prefsEditor;
  protected ConnectivityManager cm;
  protected boolean lowBattery = false;
  protected boolean mobileData = false;
  protected int prefetchCount = 0;
 
  public PlacesUpdateService() {
    super(TAG);
    setIntentRedeliveryMode(false);
  }
  
  /**
   * Set the Intent Redelivery mode to true to ensure the Service starts "Sticky"
   * Defaults to "true" on legacy devices.
   */
  protected void setIntentRedeliveryMode(boolean enable) {}

  /**
   * Returns battery status. True if less than 10% remaining.
   * @param battery Battery Intent
   * @return Battery is low
   */
  protected boolean getIsLowBattery(Intent battery) {
    float pctLevel = (float)battery.getIntExtra(BatteryManager.EXTRA_LEVEL, 1) / 
    battery.getIntExtra(BatteryManager.EXTRA_SCALE, 1);
    return pctLevel < 0.15;
  }
  
  @Override
  public void onCreate() {
    super.onCreate();
    cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
    contentResolver = getContentResolver();
    prefs = getSharedPreferences(PlacesConstants.SHARED_PREFERENCE_FILE, Context.MODE_PRIVATE);
    prefsEditor = prefs.edit();
  }

  /**
   * {@inheritDoc}
   * Checks the battery and connectivity state before removing stale venues
   * and initiating a server poll for new venues around the specified 
   * location within the given radius.
   */
  @Override
  protected void onHandleIntent(Intent intent) {
  	// Check if we're running in the foreground, if not, check if
  	// we have permission to do background updates.
  	boolean backgroundAllowed = cm.getBackgroundDataSetting();
  	boolean inBackground = prefs.getBoolean(PlacesConstants.EXTRA_KEY_IN_BACKGROUND, true);
  	
  	if (!backgroundAllowed && inBackground) return;
	
    // Extract the location and radius around which to conduct our search.
    Location location = new Location(PlacesConstants.CONSTRUCTED_LOCATION_PROVIDER);
    int radius = PlacesConstants.DEFAULT_RADIUS;
    
    Bundle extras = intent.getExtras();
    if (intent.hasExtra(PlacesConstants.EXTRA_KEY_LOCATION)) {
      location = (Location)(extras.get(PlacesConstants.EXTRA_KEY_LOCATION));
      radius = extras.getInt(PlacesConstants.EXTRA_KEY_RADIUS, PlacesConstants.DEFAULT_RADIUS);
    }
    
    // Check if we're in a low battery situation.
    IntentFilter batIntentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
    Intent battery = registerReceiver(null, batIntentFilter);
    lowBattery = getIsLowBattery(battery);
    
    // Check if we're connected to a data network, and if so - if it's a mobile network.
    NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
    boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting();
    mobileData = activeNetwork != null && activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE;

    // If we're not connected, enable the connectivity receiver and disable the location receiver.
    // There's no point trying to poll the server for updates if we're not connected, and the 
    // connectivity receiver will turn the location-based updates back on once we have a connection.
    if (!isConnected) {
      PackageManager pm = getPackageManager();
      
      ComponentName connectivityReceiver = new ComponentName(this, ConnectivityChangedReceiver.class);
      ComponentName locationReceiver = new ComponentName(this, LocationChangedReceiver.class);
      ComponentName passiveLocationReceiver = new ComponentName(this, PassiveLocationChangedReceiver.class);

      pm.setComponentEnabledSetting(connectivityReceiver,
        PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 
        PackageManager.DONT_KILL_APP);
            
      pm.setComponentEnabledSetting(locationReceiver,
        PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 
        PackageManager.DONT_KILL_APP);
      
      pm.setComponentEnabledSetting(passiveLocationReceiver,
        PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 
        PackageManager.DONT_KILL_APP);
    }
    else {
      // If we are connected check to see if this is a forced update (typically triggered
      // when the location has changed).
      boolean doUpdate = intent.getBooleanExtra(PlacesConstants.EXTRA_KEY_FORCEREFRESH, false);
      
      // If it's not a forced update (for example from the Activity being restarted) then
      // check to see if we've moved far enough, or there's been a long enough delay since
      // the last update and if so, enforce a new update.
      if (!doUpdate) {
        // Retrieve the last update time and place.
        long lastTime = prefs.getLong(PlacesConstants.SP_KEY_LAST_LIST_UPDATE_TIME, Long.MIN_VALUE);
        long lastLat = prefs.getLong(PlacesConstants.SP_KEY_LAST_LIST_UPDATE_LAT, Long.MIN_VALUE);
        long lastLng = prefs.getLong(PlacesConstants.SP_KEY_LAST_LIST_UPDATE_LNG, Long.MIN_VALUE);
        Location lastLocation = new Location(PlacesConstants.CONSTRUCTED_LOCATION_PROVIDER);
        lastLocation.setLatitude(lastLat);
        lastLocation.setLongitude(lastLng);
        
        // If update time and distance bounds have been passed, do an update.
        if ((lastTime < System.currentTimeMillis()-PlacesConstants.MAX_TIME) ||
           (lastLocation.distanceTo(location) > PlacesConstants.MAX_DISTANCE))
          doUpdate = true;
      }
      
      if (doUpdate) {
        // Refresh the prefetch count for each new location.
        prefetchCount = 0;
        // Remove the old locations
        removeOldLocations(location, radius);
        // Hit the server for new venues for the current location.
        refreshPlaces(location, radius);
      }
      else
        Log.d(TAG, "Place List is fresh: Not refreshing");
      
      // Retry any queued checkins.
      Intent checkinServiceIntent = new Intent(this, PlaceCheckinService.class);
      startService(checkinServiceIntent);
    }
    Log.d(TAG, "Place List Download Service Complete");
  }
  
  /**
   * Polls the underlying service to return a list of places within the specified
   * radius of the specified Location. 
   * @param location Location
   * @param radius Radius
   */
  protected void refreshPlaces(Location location, int radius) {   
    // Log to see if we'll be prefetching the details page of each new place.
    if (mobileData) {
      Log.d(TAG, "Not prefetching due to being on mobile");
    } else if (lowBattery) {
      Log.d(TAG, "Not prefetching due to low battery");
    }

    long currentTime = System.currentTimeMillis();
    URL url;
    
    try {
      // TODO Replace this with a URI to your own service.
      String locationStr = location.getLatitude() + "," + location.getLongitude();
      String baseURI = PlacesConstants.PLACES_LIST_BASE_URI;
      String placesFeed = baseURI + "&location=" + locationStr + "&radius=" + radius + PlacesConstants.PLACES_API_KEY;
      url = new URL(placesFeed);
           
      // Open the connection
      URLConnection connection = url.openConnection();
      HttpsURLConnection httpConnection = (HttpsURLConnection)connection; 
      int responseCode = httpConnection.getResponseCode(); 

      if (responseCode == HttpURLConnection.HTTP_OK) { 
        // Use the XML Pull Parser to extract each nearby location.
        // TODO Replace the XML parsing to extract your own place list.
        InputStream in = httpConnection.getInputStream();     
        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
        factory.setNamespaceAware(true);
        XmlPullParser xpp = factory.newPullParser();

        xpp.setInput(in, null);
        int eventType = xpp.getEventType();
        while (eventType != XmlPullParser.END_DOCUMENT) {
         if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("result")) {
           eventType = xpp.next();
           String id = "";
           String name = "";
           String vicinity = "";
           String types = "";
           String locationLat = "";
           String locationLng = "";
           String viewport = "";
           String icon = "";
           String reference = "";
           while (!(eventType == XmlPullParser.END_TAG && xpp.getName().equals("result"))) {
             if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("name"))
               name = xpp.nextText();
             else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("vicinity"))
               vicinity = xpp.nextText();
             else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("type"))
               types = types == "" ? xpp.nextText() : types + " " + xpp.nextText();
             else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("lat"))
               locationLat = xpp.nextText();
             else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("lng"))
               locationLng = xpp.nextText();
             else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("icon"))
               icon = xpp.nextText();
             else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("reference"))
               reference = xpp.nextText();
             else if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("id"))
               id = xpp.nextText();
             eventType = xpp.next();
           }
           Location placeLocation = new Location(PlacesConstants.CONSTRUCTED_LOCATION_PROVIDER);
           placeLocation.setLatitude(Double.valueOf(locationLat));
           placeLocation.setLongitude(Double.valueOf(locationLng));
           
           // Add each new place to the Places Content Provider
           addPlace(location, id, name, vicinity, types, placeLocation, viewport, icon, reference, currentTime);
         }
         eventType = xpp.next();
        }
        
        // Remove places from the PlacesContentProviderlist that aren't from this updte.
        String where = PlaceDetailsContentProvider.KEY_LAST_UPDATE_TIME + " < " + currentTime; 
        contentResolver.delete(PlacesContentProvider.CONTENT_URI, where, null);
       
        // Save the last update time and place to the Shared Preferences.
        prefsEditor.putLong(PlacesConstants.SP_KEY_LAST_LIST_UPDATE_LAT, (long) location.getLatitude());
        prefsEditor.putLong(PlacesConstants.SP_KEY_LAST_LIST_UPDATE_LNG, (long) location.getLongitude());
        prefsEditor.putLong(PlacesConstants.SP_KEY_LAST_LIST_UPDATE_TIME, System.currentTimeMillis());      
        prefsEditor.commit();
      }
      else
        Log.e(TAG, responseCode + ": " + httpConnection.getResponseMessage());
      
    } catch (MalformedURLException e) {
      Log.e(TAG, e.getMessage());
    } catch (IOException e) {
      Log.e(TAG, e.getMessage());
    } catch (XmlPullParserException e) {
      Log.e(TAG, e.getMessage());
    }
    finally {
    }
  }
  
  /**
   * Adds the new place to the {@link PlacesContentProvider} using the values passed in.
   * TODO Update this method to accept and persist the place information your service provides.
   * @param currentLocation Current location
   * @param id Unique identifier
   * @param name Name
   * @param vicinity Vicinity
   * @param types Types
   * @param location Location
   * @param viewport Viewport
   * @param icon Icon
   * @param reference Reference
   * @param currentTime Current time
   * @return Successfully added
   */
  protected boolean addPlace(Location currentLocation, String id, String name, String vicinity, String types, Location location, String viewport, String icon, String reference, long currentTime) {
    // Contruct the Content Values
    ContentValues values = new ContentValues();
    values.put(PlacesContentProvider.KEY_ID, id);  
    values.put(PlacesContentProvider.KEY_NAME, name);
    double lat = location.getLatitude();
    double lng = location.getLongitude();
    values.put(PlacesContentProvider.KEY_LOCATION_LAT, lat);
    values.put(PlacesContentProvider.KEY_LOCATION_LNG, lng);
    values.put(PlacesContentProvider.KEY_VICINITY, vicinity);
    values.put(PlacesContentProvider.KEY_TYPES, types);
    values.put(PlacesContentProvider.KEY_VIEWPORT, viewport);
    values.put(PlacesContentProvider.KEY_ICON, icon);
    values.put(PlacesContentProvider.KEY_REFERENCE, reference);
    values.put(PlacesContentProvider.KEY_LAST_UPDATE_TIME, currentTime);

    // Calculate the distance between the current location and the venue's location
    float distance = 0;
    if (currentLocation != null && location != null)
      distance = currentLocation.distanceTo(location);
    values.put(PlacesContentProvider.KEY_DISTANCE, distance);  
    
    // Update or add the new place to the PlacesContentProvider
    String where = PlacesContentProvider.KEY_ID + " = '" + id + "'";
    boolean result = false;
    try {
      if (contentResolver.update(PlacesContentProvider.CONTENT_URI, values, where, null) == 0) {
        if (contentResolver.insert(PlacesContentProvider.CONTENT_URI, values) != null)
          result = true;
      }
      else 
        result = true;
    }
    catch (Exception ex) { 
      Log.e("PLACES", "Adding " + name + " failed.");
    }
    
    // If we haven't yet reached our prefetching limit, and we're either
    // on WiFi or don't have a WiFi-only prefetching restriction, and we
    // either don't have low batter or don't have a low battery prefetching 
    // restriction, then prefetch the details for this newly added place.
    if ((prefetchCount < PlacesConstants.PREFETCH_LIMIT) &&
        (!PlacesConstants.PREFETCH_ON_WIFI_ONLY || !mobileData) &&
        (!PlacesConstants.DISABLE_PREFETCH_ON_LOW_BATTERY || !lowBattery)) {
      prefetchCount++;
      
      // Start the PlaceDetailsUpdateService to prefetch the details for this place.
      // As we're prefetching, don't force the refresh if we already have data.
      Intent updateServiceIntent = new Intent(this, PlaceDetailsUpdateService.class);
      updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_REFERENCE, reference);
      updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_ID, id);
      updateServiceIntent.putExtra(PlacesConstants.EXTRA_KEY_FORCEREFRESH, false);
      startService(updateServiceIntent);
    }
    
    return result;
  }
  
  /**
   * Remove stale place detail records unless we've set the persistent cache flag to true.
   * This is typically the case where a place has actually been viewed rather than prefetched. 
   * @param location Location
   * @param radius Radius
   */
  protected void removeOldLocations(Location location, int radius) {
    // Stale Detail Pages
    long minTime = System.currentTimeMillis()-PlacesConstants.MAX_DETAILS_UPDATE_LATENCY;
    String where = PlaceDetailsContentProvider.KEY_LAST_UPDATE_TIME + " < " + minTime + " AND " +
                   PlaceDetailsContentProvider.KEY_FORCE_CACHE + " = 0";
    contentResolver.delete(PlaceDetailsContentProvider.CONTENT_URI, where, null);
  }
}
app/src/main/java/com/radioactiveyak/location_best_practices/PlacesConstants.java
public class PlacesConstants {
  
  /**
   * TODO **P1**  You must put your Google Places API key here.
   * You can get your API key from: 
   * {@link http://code.google.com/apis/maps/documentation/places/#Limits}
   */
  private static String MY_API_KEY = "";
  
  public static String PLACES_API_KEY = "&key=" + MY_API_KEY;
  
  /**
   * You'll need to modify these values to suit your own app.
   */
  // TODO Turn off when deploying your app.
  public static boolean DEVELOPER_MODE = true;
  
  // TODO Point these at your data sources.
  public static String PLACES_LIST_BASE_URI = "https://maps.googleapis.com/maps/api/place/search/xml?sensor=true";
  public static String PLACES_DETAIL_BASE_URI = "https://maps.googleapis.com/maps/api/place/details/xml?sensor=true&reference=";
  public static String PLACES_CHECKIN_URI = "https://maps.googleapis.com/maps/api/place/check-in/xml?sensor=true";
  public static String PLACES_CHECKIN_OK_STATUS = "OK";
  
  /**
   * These values control the user experience of your app. You should
   * modify them to provide the best experience based on how your
   * app will actually be used.
   * TODO Update these values for your app.
   */
  // The default search radius when searching for places nearby.
  public static int DEFAULT_RADIUS = 150;
  // The maximum distance the user should travel between location updates. 
  public static int MAX_DISTANCE = DEFAULT_RADIUS/2;
  // The maximum time that should pass before the user gets a location update.
  public static long MAX_TIME = AlarmManager.INTERVAL_FIFTEEN_MINUTES;
  
  // You will generally want passive location updates to occur less frequently
  // than active updates. You need to balance location freshness with battery life.
  // The location update distance for passive updates.
  public static int PASSIVE_MAX_DISTANCE = MAX_DISTANCE;
  // The location update time for passive updates
  public static long PASSIVE_MAX_TIME = MAX_TIME;
  // Use the GPS (fine location provider) when the Activity is visible?
  public static boolean USE_GPS_WHEN_ACTIVITY_VISIBLE = true;
  //When the user exits via the back button, do you want to disable
  // passive background updates.
  public static boolean DISABLE_PASSIVE_LOCATION_WHEN_USER_EXIT = false;
  
  // Maximum latency before you force a cached detail page to be updated.
  public static long MAX_DETAILS_UPDATE_LATENCY = AlarmManager.INTERVAL_DAY;
  
  // Prefetching place details is useful but potentially expensive. The following
  // values lets you disable prefetching when on mobile data or low battery conditions.
  // Only prefetch on WIFI?
  public static boolean PREFETCH_ON_WIFI_ONLY = false;
  // Disable prefetching when battery is low?
  public static boolean DISABLE_PREFETCH_ON_LOW_BATTERY = true;
  
  // How long to wait before retrying failed checkins.
  public static long CHECKIN_RETRY_INTERVAL = AlarmManager.INTERVAL_FIFTEEN_MINUTES;
  
  // The maximum number of locations to prefetch for each update.
  public static int PREFETCH_LIMIT = 5;
 
  
  /**
   * These values are constants used for intents, exteas, and shared preferences.
   * You shouldn't need to modify them.
   */
  public static String SHARED_PREFERENCE_FILE = "SHARED_PREFERENCE_FILE";
  public static String SP_KEY_FOLLOW_LOCATION_CHANGES = "SP_KEY_FOLLOW_LOCATION_CHANGES";
  public static String SP_KEY_LAST_LIST_UPDATE_TIME = "SP_KEY_LAST_LIST_UPDATE_TIME";
  public static String SP_KEY_LAST_LIST_UPDATE_LAT = "SP_KEY_LAST_LIST_UPDATE_LAT";
  public static String SP_KEY_LAST_LIST_UPDATE_LNG = "SP_KEY_LAST_LIST_UPDATE_LNG";
  public static String SP_KEY_LAST_CHECKIN_ID = "SP_KEY_LAST_CHECKIN_ID";
  public static String SP_KEY_LAST_CHECKIN_TIMESTAMP = "SP_KEY_LAST_CHECKIN_TIMESTAMP";
  public static String SP_KEY_RUN_ONCE = "SP_KEY_RUN_ONCE";
  
  public static String EXTRA_KEY_REFERENCE = "reference";
  public static String EXTRA_KEY_ID = "id";
  public static String EXTRA_KEY_LOCATION = "location";
  public static String EXTRA_KEY_RADIUS = "radius";
  public static String EXTRA_KEY_TIME_STAMP = "time_stamp";
  public static String EXTRA_KEY_FORCEREFRESH = "force_refresh";
  public static String EXTRA_KEY_IN_BACKGROUND = "EXTRA_KEY_IN_BACKGROUND";
  
  public static String ARGUMENTS_KEY_REFERENCE = "reference";
  public static String ARGUMENTS_KEY_ID = "id";
  
  public static String NEW_CHECKIN_ACTION = "com.radioactiveyak.places.NEW_CHECKIN_ACTION";
  public static String RETRY_QUEUED_CHECKINS_ACTION = "com.radioactiveyak.places.retry_queued_checkins";
  public static String ACTIVE_LOCATION_UPDATE_PROVIDER_DISABLED = "com.radioactiveyak.places.active_location_update_provider_disabled";
  
  public static boolean SUPPORTS_GINGERBREAD = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD;
  public static boolean SUPPORTS_HONEYCOMB = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB;
  public static boolean SUPPORTS_FROYO = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.FROYO;
  public static boolean SUPPORTS_ECLAIR = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ECLAIR;
  
  public static String CONSTRUCTED_LOCATION_PROVIDER = "CONSTRUCTED_LOCATION_PROVIDER";
  
  public static int CHECKIN_NOTIFICATION = 0;
}


Question: import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.radioactiveyak.location_best_practices.PlacesConstants;
import com.radioactiveyak.location_best_practices.R;
import com.radioactiveyak.location_best_practices.UI.fragments.CheckinFragment;
import com.radioactiveyak.location_best_practices.UI.fragments.PlaceDetailFragment;
import com.radioactiveyak.location_best_practices.UI.fragments.PlaceListFragment;
import com.radioactiveyak.location_best_practices.receivers.LocationChangedReceiver;
import com.radioactiveyak.location_best_practices.receivers.NewCheckinReceiver;
import com.radioactiveyak.location_best_practices.receivers.PassiveLocationChangedReceiver;
import com.radioactiveyak.location_best_practices.services.EclairPlacesUpdateService;
import com.radioactiveyak.location_best_practices.services.PlacesUpdateService;
import com.radioactiveyak.location_best_practices.utils.PlatformSpecificImplementationFactory;
import com.radioactiveyak.location_best_practices.utils.base.ILastLocationFinder;
import com.radioactiveyak.location_best_practices.utils.base.LocationUpdateRequester;
import com.radioactiveyak.location_best_practices.utils.base.SharedPreferenceSaver;
/*
 * Copyright 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.radioactiveyak.location_best_practices.UI;



/**
 * Main application Activity. Used for all application UI, including pre/post Honeycomb 
 * and tablet / phone variations using different layouts. The {@link PlaceDetailFragment},
 * {@link PlaceListFragment}, and {@link CheckinFragment} are all managed from within this Activity.
 * 
 * Because the list, detail, and checkin functionality is encapsulated within their respective
 * Fragments, this Activity serves as a container for each fragment, and the coordinator of their
 * interaction. This Activity also manages the menu, listens for checkins, and manages location updates when the 
 * Application is active.
 * 
 * TODO Update the menu and functionality to support your enhanced UI
 * TODO and service-specific features and requirements
 */
public class PlaceActivity extends FragmentActivity {

  protected static String TAG = "PlaceActivity";

  // TODO (RETO) Add "refreshing" icons when stuff is blank or refreshing.

  protected PackageManager packageManager;
  protected NotificationManager notificationManager;
  protected LocationManager locationManager;
  
//  protected boolean followLocationChanges = true;
  protected SharedPreferences prefs;
  protected Editor prefsEditor;
  protected SharedPreferenceSaver sharedPreferenceSaver;

  protected Criteria criteria;
  protected ILastLocationFinder lastLocationFinder;
Answer:   protected LocationUpdateRequester locationUpdateRequester;