Back to project page GTFSOffline.
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.
/* * 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(); } } }