Android Open Source - GTFSOffline Location Fragment Helper






From Project

Back to project page GTFSOffline.

License

The source code is released under:

GNU General Public License

If you think the Android project GTFSOffline listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright 2011 Giles Malet.//from  w ww . j ava 2  s.  c  o m
 * Modified 2013 Wilson Brenna.
 *
 * This file is part of GTFSOffline.
 * 
 * GTFSOffline is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GTFSOffline is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with GTFSOffline.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.wbrenna.gtfsoffline;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.location.Location;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.text.format.Time;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ProgressBar;
import android.widget.Toast;

public class LocationFragmentHelper {
  
  private static final String TAG = "LocationFragmentHelper";
  private static String FAVSTOPS_KEY;

  private static int grid_size;
  private static int NUM_CLOSEST_STOPS;
  private static int NUM_BUSES;  //the number of next buses per stop to be shown.
    private boolean USE_ROUTE_NO;

  private Location mLocation;
  private timestopdescArrayAdapter mAdapter;
  private ArrayList<String[]> mListDetails;

  private Context mContext;
  
  private String myDBName;
  private SQLiteDatabase myDB;
  private DatabaseHelper mDatabaseHelper;

  // Need to store some stuff in an array, so we can sort by distance
  class StopLocn {
    public float dist, bearing;
    public double lat, lon;
    public String stop_id, stop_name;
  }
  private StopLocn[] mStops;
  private SharedPreferences mPrefs;
  private static boolean ampmflag;
  private static int hoursLookAhead;
  
  private ProgressBar mProgress;
  


  public LocationFragmentHelper(Context context, String aDBName, 
        SQLiteDatabase aDB, ProgressBar aProgress) {
    mContext = context;
    myDBName = aDBName;
    myDB = aDB;
  
    mDatabaseHelper = new DatabaseHelper(mContext);
    
    // Load animations used to show/hide progress bar
    //mTitle = (TextView) findViewById(R.id.listtitle);
    mProgress = aProgress;
    mListDetails = new ArrayList<String[]>(NUM_CLOSEST_STOPS*NUM_BUSES);

    //mTitle.setText(R.string.loading_stops);
    mStops = null;
    
    //set up prefs
    FAVSTOPS_KEY = new String(mContext.getString(R.string.pref_favstops_key));
    mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
    reloadPreferences();
  }
  
  public void runProcessOnLocation(Location aLocation) {
    mLocation = aLocation;
    new ProcessBusStops().execute();
  }

  private void reloadPreferences() {
    ampmflag = mPrefs.getBoolean(mContext.getString(R.string.pref_ampmtimes_key), false);
    NUM_CLOSEST_STOPS = Integer.parseInt(mPrefs.getString(
                      mContext.getString(R.string.pref_num_closest_stops), "8"));
    NUM_BUSES = Integer.parseInt(mPrefs.getString(
                mContext.getString(R.string.pref_num_buses_per_stop), "3"));
    hoursLookAhead = Integer.parseInt(mPrefs.getString(
          mContext.getString(R.string.pref_hours_look_ahead), "1"));
    grid_size = Integer.parseInt(mPrefs.getString(
        mContext.getString(R.string.pref_grid_size_key), "1"));
        USE_ROUTE_NO = mPrefs.getBoolean("useroutenos", false);
  }

  public ArrayList<String[]> retrieveNextBusList() {
    return mListDetails;
  }
  
  public void addTimeAdapter(timestopdescArrayAdapter anAdapter) {
    mAdapter = anAdapter;
  }

  /* Do the processing to load the ArrayAdapter for display. */
  public class ProcessBusStops extends AsyncTask<Void, Integer, Void> {
    // static final String TAG = "ProcessBusStops";

    
    @Override
    protected void onPreExecute() {
//      mListDetail.startAnimation(mSlideIn);
      mProgress.setVisibility(View.VISIBLE);  
    }

    // Update the progress bar.
    @Override
    protected void onProgressUpdate(Integer... parms) {
      mProgress.setProgress(parms[0]);
    }

    @Override
    protected Void doInBackground(Void... foo) {
      // Log.v(TAG, "doInBackground()");

      if( myDB == null ) {
        myDB = mDatabaseHelper.ReadableDB(myDBName, myDB);
      }
      if (myDB == null) {
        //we tried!
        return null;
      }
      if (mLocation == null) {
        //uh oh, no location yet!
        //we'll trust the TOAST in LocationHelper to keep people aware of this.
        return null;
      }
      double myLongitude = mLocation.getLongitude();
      double myLatitude = mLocation.getLatitude();
      double myTop = myLatitude + (180.0/Math.PI)*(grid_size/6378.1370);
      double myBottom = myLatitude - (180.0/Math.PI)*(grid_size/6378.1370);
      double myLeft = myLongitude - (180.0/Math.PI)*(grid_size/6378.1370)/Math.cos(Math.PI/180.0*myLatitude);
      double myRight = myLongitude + (180.0/Math.PI)*(grid_size/6378.1370)/Math.cos(Math.PI/180.0*myLatitude);

      final String qry;
      final String[] selectargs;
      if ( grid_size == 0 ) {
        //we want to look as far as possible away.
        qry = "select stop_id as _id, stop_lat, stop_lon, stop_name from stops";
        selectargs = new String[] { };
      } else {
        qry = "select stop_id as _id, stop_lat, stop_lon, stop_name from stops " +
            "where stop_lat < ? and stop_lat > ? and stop_lon < ? and stop_lon > ?";
        selectargs = new String[] { Double.toString(myTop), Double.toString(myBottom), 
          Double.toString(myLeft), Double.toString(myRight) };
      }
      int maxcount;

      
      // Load the stops from the database the first time through
      if (mStops == null) {
        final Cursor csr = mDatabaseHelper.ReadableDB(myDBName, myDB).rawQuery(qry, selectargs);
        maxcount = csr.getCount();
        mStops = new StopLocn[maxcount];
        boolean more = csr.moveToPosition(0);
        int locidx = 0;

        while (more) {
          // stash in array
          mStops[locidx] = new StopLocn();
          mStops[locidx].stop_id = csr.getString(0);
          mStops[locidx].lat = csr.getDouble(1);
          mStops[locidx].lon = csr.getDouble(2);
          mStops[locidx].stop_name = csr.getString(3);

          more = csr.moveToNext();
          ++locidx;
          //publishProgress(((int) ((locidx / (float) maxcount) * 100)));
        }
        csr.close();
      }
      
      // Calculate the distance to each point in the array
      final float[] results = new float[2];
      
      if(mLocation == null) {
        return null;
      }
      for (final StopLocn s : mStops) {

        Location.distanceBetween(mLocation.getLatitude(), mLocation.getLongitude(), s.lat, s.lon, results);
        s.dist = results[0];
        s.bearing = results[1];
      }

      // Sort by distance from our current location
      Arrays.sort(mStops, new Comparator<StopLocn>() {
        @Override
        public int compare(StopLocn entry1, StopLocn entry2) {
          return entry1.dist < entry2.dist ? -1 : entry1.dist == entry2.dist ? 0 : 1;
        }
      });

      // Transfer everything to an array list to load in display
      // Bearing is from -180 to +180, so use as index into here
      mListDetails.clear();
      final String[] DIRS = { "S", "SW", "W", "NW", "N", "NE", "E", "SE", };
      Integer stop_limit;
      if (mStops.length < NUM_CLOSEST_STOPS) {
        stop_limit = mStops.length;
      } else {
        stop_limit = NUM_CLOSEST_STOPS;
      }
      
      final Time t = new Time();
      t.setToNow();
      
      for (int i = 0; i < stop_limit; i++) {
        final StopLocn s = mStops[i];

        final String dir = DIRS[(int) (s.bearing + 180 + 22.5) % 360 / 45];
        String dist;
        if (s.dist < 1000) {
          dist = String.format("%3.0fm %s", s.dist, dir);
        } else {
          dist = String.format("%3.1fkm %s", s.dist / 1000.0, dir);
        }
        //So, we have the heading of the nearest stop. Now, we need to query to find
        //the next NUM_BUSES.
        ServiceCalendar myBusService = new ServiceCalendar(myDBName, myDB, ampmflag);
        myBusService.setDB(mDatabaseHelper);
        final ArrayList<String[]> fullResultsA = myBusService.getNextDepartureTimes(t, s.stop_id, 
            NUM_BUSES, hoursLookAhead, true);
        //the format of this:
        // departuretime  runstoday  trip_id    route_short_name  trip_headsign
        //  140300        1    34867    13          Route 13 Laurelwood
        
        ArrayList<String[]> fullResults = myBusService.getNextDepartureTimes(t, s.stop_id, 
            NUM_BUSES, hoursLookAhead, false);

        if ((fullResults == null) && (fullResultsA == null))
        {
          if (stop_limit < mStops.length - 1) {
            stop_limit++;
          }
          continue;
        } else if (fullResultsA == null) {
          //do nothing
        } else if (fullResults == null) {
          fullResults = fullResultsA;
        }
        else {
                    if (t.hour <= hoursLookAhead) {
                        fullResults.addAll(fullResultsA);
                    }
                    else {
                        ArrayList<String[]> tmpResults = fullResultsA;
                        tmpResults.addAll(fullResults);
                        fullResults = tmpResults;
                    }
        }
        
        for (String[] str: fullResults) {
          //process the result string to get the right departure time
          final String hours = str[0].substring(0,2);
          final String minutes = str[0].substring(2,4);
          //String departsIn;
                    final String routeNo;
                    if (str[3].equals("") || (!USE_ROUTE_NO)) {
                        routeNo = s.stop_id;
                    }
                    else {
                        routeNo = str[3];
                    }

          mListDetails.add(new String[] { dist, s.stop_id, s.stop_name, 
              str[4], myBusService.formattedDepartureTime(t, hours, minutes),
              str[2], myDBName, routeNo});
          
        }
        publishProgress(((int) ((i / (float) stop_limit) * 100)));
      }
      
      return null;
    }

    @Override
    protected void onCancelled() {
      mDatabaseHelper.CloseDB(mDatabaseHelper.ReadableDB(myDBName, myDB));
    }
    
    @Override
    protected void onPostExecute(Void foo) {
       //Log.v(TAG, "onPostExecute(), closing " + myDBName );

      mProgress.setVisibility(View.INVISIBLE);
      //mListDetail.startAnimation(mSlideOut);

      //mTitle.setText(R.string.title_activity_closest_stops);
      if(mAdapter != null) {
        mAdapter.notifyDataSetChanged();
      }
      //close the database
      mDatabaseHelper.CloseDB(myDB);
      myDB = null;
    }
  }
  
  // Called for a long click
  public void onListItemLongClick(AdapterView<?> parent, View v, int position, long id) {
    Log.v(TAG, "long clicked position " + position);

    final String[] strs = (String[]) parent.getItemAtPosition(position);
    if (strs == null) {
      return;
    }
    final String stop_id = strs[1];
    final String stop_name = strs[2];

    final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
      @Override
      public void onClick(DialogInterface dialog, int id) {
        switch (id) {
        case DialogInterface.BUTTON_POSITIVE:
          AddBusstopFavourite(stop_id, stop_name + "#" + myDBName);
          break;
        }
        dialog.cancel();
      }
    };

    final AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
    builder.setTitle("Stop " + stop_id + ", " + stop_name);
    builder.setMessage(R.string.favs_add_to_list).setPositiveButton(R.string.yes, listener)
    .setNegativeButton(R.string.no, listener).create().show();
  }
  
  public ArrayList<String[]> GetBusstopFavourites() {
    final String favs = mPrefs.getString(FAVSTOPS_KEY, "");

    // Load the array for the list
    final ArrayList<String[]> details = new ArrayList<String[]>();

    // favs is a semi-colon separated string of stops, with a trailing semi-colon.
    // Then each stop has a description stored as KEY-stop.
    if (!favs.equals("")) {
      final TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(';');
      splitter.setString(favs);
      for (final String s : splitter) {
        final String[] strs = { s, mPrefs.getString(FAVSTOPS_KEY + "-" + s, "") };
        details.add(strs);
      }
    }
    return details;
  }


  public void AddBusstopFavourite(String busstop, String stopname) {
    String favs = mPrefs.getString(FAVSTOPS_KEY, "");

    final TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(';');
    splitter.setString(favs);
    boolean already = false;
    for (final String s : splitter) {
      if (s.equals(busstop)) {
        already = true;
        break;
      }
    }

    if (already) {
      Toast.makeText(mContext, "Stop " + busstop + " is already a favourite!", Toast.LENGTH_LONG).show();
    } else {
      favs += busstop + ";";
      mPrefs.edit().putString(FAVSTOPS_KEY, favs).putString(FAVSTOPS_KEY + "-" + busstop, stopname).commit();
      Toast.makeText(mContext, "Stop " + busstop + " was added to your favourites.", Toast.LENGTH_LONG).show();
    }
  }
  
  
}




Java Source Code List

com.wbrenna.gtfsoffline.DatabaseHelper.java
com.wbrenna.gtfsoffline.FavFragmentHelper.java
com.wbrenna.gtfsoffline.ListArrayAdapter.java
com.wbrenna.gtfsoffline.LocationFragmentHelper.java
com.wbrenna.gtfsoffline.LocationHelper.java
com.wbrenna.gtfsoffline.MainActivity.java
com.wbrenna.gtfsoffline.PrefsActivity.java
com.wbrenna.gtfsoffline.ServiceCalendar.java
com.wbrenna.gtfsoffline.TimesActivity.java
com.wbrenna.gtfsoffline.TimesArrayAdapter.java
com.wbrenna.gtfsoffline.UpdateActivity.java
com.wbrenna.gtfsoffline.timestopdescArrayAdapter.java