/*
* Copyright 2011 Giles Malet.
* 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 android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
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 FavFragmentHelper {
private static final String TAG = "FavFragmentHelper";
private static String FAVSTOPS_KEY;
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 DatabaseHelper mDatabaseHelper;
private SQLiteDatabase myDB;
private String curDB;
// 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 boolean ampmflag;
private String[] mActiveDB;
private int hoursLookAhead;
private ProgressBar mProgress;
public FavFragmentHelper(Context context, String[] activeDBs, ProgressBar aProgress) {
mContext = context;
mDatabaseHelper = new DatabaseHelper(mContext);
mActiveDB = activeDBs;
// 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;
//mLocation = null;
//set up prefs
FAVSTOPS_KEY = new String(mContext.getString(R.string.pref_favstops_key));
mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
reloadPreferences();
}
public void runProcess() {
new ProcessBusStops().execute();
}
public 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"));
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.
// - do nothing for now
@Override
protected void onProgressUpdate(Integer... parms) {
mProgress.setProgress(parms[0]);
}
@Override
protected Void doInBackground(Void... foo) {
// Log.v(TAG, "doInBackground()");
//really inefficient...at the moment we just search all databases
mListDetails.clear();
// Load the stops from preferences
ArrayList<String[]> tmpStops = GetBusstopFavourites();
mStops = new StopLocn[tmpStops.size()];
//for (final StopLocn s : mStops) {
if (tmpStops.size() == 0) {
Log.v(TAG, "Empty favourites");
return null;
}
final TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter('#');
Time t = new Time();
t.setToNow();
if(mActiveDB == null)
{
return null;
}
for (String myDBName : mActiveDB) {
//Log.e(TAG, "Running on database: " + myDBName);
int stopsCounter = 0;
for (int i = 0; i<tmpStops.size(); i++) {
//Log.e(TAG, "Stops are: " + Arrays.toString(tmpStops.get(i)));
splitter.setString(tmpStops.get(i)[1]);
if(splitter.hasNext())
{
String tmpSplitterString = splitter.next();
if(splitter.hasNext())
{
//if DB string doesn't match, leave!
String tmpDbNameSplitter = splitter.next();
if(!tmpDbNameSplitter.equals(myDBName))
{
continue;
}
}
//if there is no DB string leave in for compatibility
mStops[stopsCounter] = new StopLocn();
mStops[stopsCounter].stop_id = tmpStops.get(i)[0];
mStops[stopsCounter].stop_name = tmpSplitterString;
stopsCounter++;
}
//mStops[i].stop_name = tmpStops.get(i)[1];
}
if(stopsCounter == 0)
{
continue;
}
myDB = mDatabaseHelper.ReadableDB(myDBName, null);
curDB = myDBName;
//String[] mStopIdArray = new String[mStops.length];
String[] mStopIdArray = new String[stopsCounter];
for (int i = 0; i < stopsCounter; i++) {
mStopIdArray[i] = mStops[i].stop_id;
//Log.w(TAG, "Running on stop: " + s.stop_id);
}
//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.getNextDepartureTimesGen(
t, mStopIdArray, 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.getNextDepartureTimesGen(
t, mStopIdArray, NUM_BUSES, hoursLookAhead, false);
if ((fullResults == null) && (fullResultsA == null))
{
continue;
} else if (fullResultsA == null) {
//do nothing
} else if (fullResults == null) {
fullResults = fullResultsA;
} else {
fullResults.addAll(fullResultsA);
}
final int favcounter = fullResults.size();
int loopcounter = 0;
for (String[] str: fullResults) {
//process str[0] 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;
//Log.e(TAG, "Adding to list: " + s.stop_name);
int myIndex = Arrays.asList(mStopIdArray).indexOf(str[5]);
if (str[3].equals("") || (!USE_ROUTE_NO)) {
routeNo = mStops[myIndex].stop_id;
}
else {
routeNo = str[3];
}
mListDetails.add(new String[] { "",
str[5],
mStops[myIndex].stop_name, str[4],
myBusService.formattedDepartureTime(t, hours, minutes),
str[2], myDBName, routeNo });
publishProgress(((int) ((++loopcounter / (float) favcounter) * 100)));
}
//close the database
mDatabaseHelper.CloseDB(myDB);
}
return null;
}
@Override
protected void onCancelled() {
mDatabaseHelper.CloseDB(mDatabaseHelper.ReadableDB(curDB, myDB));
}
@Override
protected void onPostExecute(Void foo) {
// Log.v(TAG, "onPostExecute()");
mProgress.setVisibility(View.INVISIBLE);
//mListDetail.startAnimation(mSlideOut);
//mTitle.setText(R.string.title_activity_closest_stops);
if(mAdapter != null) {
mAdapter.notifyDataSetChanged();
}
}
}
// 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:
RemoveBusstopFavourite(stop_id);
break;
}
dialog.cancel();
}
};
final AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setTitle("Stop " + stop_id + ", " + stop_name);
builder.setMessage(R.string.favs_remove_from_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 RemoveBusstopFavourite(String busstop) {
final String favs = mPrefs.getString(FAVSTOPS_KEY, "");
String newfavs = "";
final TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(';');
splitter.setString(favs);
for (final String s : splitter) {
if (!s.equals(busstop)) {
newfavs += s + ";";
}
}
mPrefs.edit().putString(FAVSTOPS_KEY, newfavs).remove(FAVSTOPS_KEY + "-" + busstop).commit();
Toast.makeText(mContext, "Stop " + busstop + " was removed from your favourites.", Toast.LENGTH_LONG).show();
}
}