package net.cbaines.suma; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import org.apache.http.client.ClientProtocolException; import org.json.JSONException; import android.content.Context; import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; import android.util.Log; import android.view.Gravity; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; public class BusActivity extends ToastHelperActivity implements Preferences { final static String TAG = "BusActivity"; private TextView busIDTextView; private TextView busDestTextView; private TextView busContentMessage; private LinearLayout busActivityContentLayout; /** * The bus this activity is focused on */ private Bus bus; /** * The bus stop this activity is working from */ private BusStop busStop; Runnable refreshData; private Timetable timetable; private ListView timetableView; private Context instance; // BusStops and if they are being updated by the handler List busStops; ArrayList busStopRoutePositions; private HashMap tasks = new HashMap(); Handler handler; long timeOfLastRefresh = 0; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.bus_activity); instance = this; String busID; if (getIntent().getDataString().startsWith("http://data")) { String[] uriParts = getIntent().getDataString().split("/"); busID = uriParts[uriParts.length - 1].replace(".html", ""); } else { String[] uriParts = getIntent().getDataString().split("/"); busID = uriParts[uriParts.length - 1]; } String busStopID = getIntent().getExtras().getString("busStopID"); try { final DatabaseHelper helper = getHelper(); List buses = helper.getBusDao().queryForEq(Bus.ID_FIELD_NAME, busID); bus = null; if (buses.size() == 0) { Log.e(TAG, "Bus " + busID + " not found!"); } else if (buses.size() == 1) { bus = buses.get(0); } else if (buses.size() > 1) { Log.e(TAG, "Found more than one bus? " + busID); } helper.getBusRouteDao().refresh(bus.route); busStop = null; if (busStopID != null) { List busStops = helper.getBusStopDao().queryForEq(BusStop.ID_FIELD_NAME, busStopID); if (busStops.size() == 0) { Log.e(TAG, "BusStop " + busStopID + " not found!"); } else if (busStops.size() == 1) { busStop = busStops.get(0); } else if (busStops.size() > 1) { Log.e(TAG, "Found more than one busStop? " + busStopID); } } busIDTextView = (TextView) findViewById(R.id.busActivityBusID); busDestTextView = (TextView) findViewById(R.id.busActivityBusDestination); busContentMessage = (TextView) findViewById(R.id.busActivityMessage); busActivityContentLayout = (LinearLayout) findViewById(R.id.busActivityContentLayout); timetableView = (ListView) findViewById(R.id.busActivityTimes); if (bus.id != null) { // Log.i(TAG, "Bus id is not null (" + bus.id + // ") setting busIDTextView"); getHelper().getBusRouteDao().refresh(bus.route); busIDTextView.setText(bus.id + " " + bus.route.label); } else { Log.w(TAG, "Bus id is null?"); // Might not ever happen busIDTextView.setText("Unidentified"); } if (bus.destinationString != null) { // Log.i(TAG, "Bus destination string is " + // bus.destinationString); busDestTextView .setText(getResources().getString(R.string.bus_activity_destination_label) + bus.destinationString); busDestTextView.setVisibility(View.VISIBLE); } else { // Log.i(TAG, "Bus destination string is null"); busDestTextView.setText(getResources().getString(R.string.bus_activity_no_destination_message)); busDestTextView.setVisibility(View.VISIBLE); } busStops = bus.route.getRouteBusStops(this); busStopRoutePositions = new ArrayList(); for (int i = 0; i < busStops.size(); i++) { busStopRoutePositions.add(i); } // Log.i(TAG, "Got " + busStops.size() + " bus stops for this bus"); if (bus.route.id == 326 && !bus.direction.equals("E")) { Log.i(TAG, "Removing extra U1 stops"); for (int i = 35; i < 50; i++) { Log.i(TAG, "Removing " + busStops.get(35).description); busStops.remove(35); busStopRoutePositions.remove(35); } } else { Log.i(TAG, "Not removing extra U1 stops"); } refreshData = new Runnable() { public void run() { Log.v(TAG, "Refreshing data " + (System.currentTimeMillis() - timeOfLastRefresh)); timeOfLastRefresh = System.currentTimeMillis(); for (int num = timetableView.getFirstVisiblePosition(); num <= timetableView.getLastVisiblePosition(); num++) { final Stop stop = timetable.get(num); if (System.currentTimeMillis() - stop.timeOfFetch.getTime() > 20000) { refreshBusStop(num); } else { // Log.v(TAG, "Not updating " + busStops.get(num)); } } handler.postDelayed(refreshData, 50000); } }; } catch (SQLException e) { e.printStackTrace(); } } private void refreshBusStop(int index) { GetTimetableStopTask task = tasks.get(index); if (task != null) { // If there is a taks if (task.getStatus() == AsyncTask.Status.FINISHED) { // If // its // finished task = null; // Delete it } } if (task == null) { // If there is now no task // Log.v(TAG, "Updating " + busStops.get(num)); // Remove the old time from the timetable synchronized (timetable) { timetable.set( index, new StopLoading(bus, busStops.get(index), timetable.get(index).arivalTime, new Date(System .currentTimeMillis() - 21000), false)); displayTimetable(); } task = new GetTimetableStopTask(); task.execute(index); tasks.put(index, task); } } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.bus_menu, menu); return true; } public void onResume() { super.onResume(); SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); if (sharedPrefs.getBoolean(UNI_LINK_BUS_TIMES, UNI_LINK_BUS_TIMES_ENABLED_BY_DEFAULT) || sharedPrefs.getBoolean(NON_UNI_LINK_BUS_TIMES, NON_UNI_LINK_BUS_TIMES_ENABLED_BY_DEFAULT)) { // Log.i(TAG, "Live Times enabled"); timetable = (Timetable) getLastNonConfigurationInstance(); handler = new Handler(); if (timetable == null) { Log.i(TAG, "No Previous timetable"); timetable = new Timetable(); for (int i = 0; i < busStops.size(); i++) { // Add a loading stop, with a fetch time such that it will // be fetched timetable .add(new StopLoading(bus, busStops.get(i), null, new Date(System.currentTimeMillis() - 21000), false)); } // Log.v(TAG, "Finished adding placeholder stops"); } else { Log.i(TAG, "Displaying previous timetable"); } displayTimetable(); if (busStop != null) { Log.i(TAG, "Moving to position of " + busStop.description + " which is " + busStops.indexOf(busStop)); timetableView.setSelection(busStops.indexOf(busStop)); } handler.postDelayed(refreshData, 500); } else { Log.i(TAG, "Live Times Disabled"); busContentMessage.setText("Live bus times disabled"); busContentMessage.setVisibility(View.VISIBLE); } } public void onPause() { if (handler != null) { // BusTimes are enabled handler.removeCallbacks(refreshData); for (GetTimetableStopTask task : tasks.values()) { if (task != null) { task.cancel(true); } } Log.i(TAG, "Stoping refreshing timetable data"); } super.onPause(); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.menu_refresh_bus) { if (handler != null) { // BusTimes are enabled handler.removeCallbacks(refreshData); for (GetTimetableStopTask task : tasks.values()) { task.cancel(true); } for (int num = timetableView.getFirstVisiblePosition(); num <= timetableView.getLastVisiblePosition(); num++) { refreshBusStop(num); } handler.postDelayed(refreshData, 50000); } else { // TODO: Toast here... } } else { Log.e(TAG, "No known menu option selected"); return super.onOptionsItemSelected(item); } return true; } private class GetTimetableStopTask extends AsyncTask { // private String errorMessage; private BusStop busStop; private int busStopIndex; protected void onPreExecute() { // progBar.setVisibility(View.VISIBLE); } protected Stop doInBackground(Integer... busStopIndexs) { busStop = busStops.get(busStopIndexs[0]); busStopIndex = busStopIndexs[0]; Stop stop = null; try { // Log.i(TAG, "Fetching stop for busStop " + busStop.description); stop = DataManager.getStop(instance, bus, busStopRoutePositions.get(busStopIndex)); if (stop == null) { stop = new Stop(bus, busStop, null, new Date(System.currentTimeMillis()), false); } // Log.i(TAG, "Finished fetching stop for busStop " + stop.busStop.description + " " + busStop.description); } catch (SQLException e) { // errorMessage = "Error message regarding SQL?"; e.printStackTrace(); } catch (ClientProtocolException e) { // errorMessage = "ClientProtocolException!?!"; e.printStackTrace(); } catch (IOException e) { // errorMessage = // "Error fetching bus times from server, are you connected to the internet?"; e.printStackTrace(); } catch (JSONException e) { // errorMessage = "Error parsing bus times"; e.printStackTrace(); } return stop; } protected void onPostExecute(Stop stop) { // Log.i(TAG, "Got timetable"); synchronized (timetable) { timetable.set(busStopIndex, stop); displayTimetable(); } } } @Override public Object onRetainNonConfigurationInstance() { return timetable; } private void displayTimetable() { if (timetable.size() == 0) { busContentMessage.setText("No Busses"); busContentMessage.setVisibility(View.VISIBLE); busActivityContentLayout.setGravity(Gravity.CENTER); } else { if (timetable.size() == 0) { busActivityContentLayout.setGravity(Gravity.CENTER); busContentMessage.setText("No Busses (With the current enabled routes)"); busContentMessage.setVisibility(View.VISIBLE); timetableView.setVisibility(View.GONE); } else { timetableView.setVisibility(View.VISIBLE); busContentMessage.setVisibility(View.GONE); BusSpecificTimetableAdapter adapter; if ((adapter = (BusSpecificTimetableAdapter) timetableView.getAdapter()) != null) { adapter.updateTimetable(timetable); } else { adapter = new BusSpecificTimetableAdapter(this, timetable); timetableView.setAdapter(adapter); } busActivityContentLayout.setGravity(Gravity.TOP); } } } }