package net.cbaines.suma; import java.io.IOException; import java.sql.SQLException; 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; protected Timetable timetable; private Timetable visibleTimetable; private ListView timetableView; private Context instance; // BusStops and if they are being updated by the handler List busStops; 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"); busIDTextView.setText(bus.id + " " + bus.getName()); } 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.getRouteSection(instance, bus.direction); // Log.i(TAG, "Got " + busStops.size() + " bus stops for this bus"); if (bus.destination != null) { Log.i(TAG, "Bus destination is " + bus.destination); int index = busStops.indexOf(bus.destination); if (index != -1) { Log.v(TAG, "Found destination stop in busStops"); for (int i = index + 1; i < busStops.size(); i++) { Log.v(TAG, "Removing " + busStops.get(i)); busStops.remove(i); } } } else { Log.i(TAG, "Bus destination is null"); } 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) { GetTimetableStopTask task = tasks.get(busStops.get(num)); 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(num, new StopLoading(bus, busStops.get(num), null, new Date( System.currentTimeMillis() - 21000), false)); displayTimetable(timetable); } task = new GetTimetableStopTask(); BusStop[] str = { stop.busStop }; task.execute(str); tasks.put(stop.busStop, task); } } else { // Log.v(TAG, "Not updating " + busStops.get(num)); } } handler.postDelayed(refreshData, 50000); } }; } catch (SQLException e) { e.printStackTrace(); } } @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(timetable); 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++) { final Stop stop = timetable.get(num); // Log.v(TAG, "Updating " + busStops.get(num)); // Remove the old time from the timetable synchronized (timetable) { timetable.set(num, new StopLoading(bus, busStops.get(num), null, new Date( System.currentTimeMillis() - 21000), false)); displayTimetable(timetable); } GetTimetableStopTask task = new GetTimetableStopTask(); BusStop[] str = { stop.busStop }; task.execute(str); tasks.put(stop.busStop, task); } 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 position; protected void onPreExecute() { // progBar.setVisibility(View.VISIBLE); } protected Stop doInBackground(BusStop... busStopArray) { busStop = busStopArray[0]; position = busStops.indexOf(busStop); Stop stop = null; try { // Log.i(TAG, "Fetching stop for busStop " + position); stop = DataManager.getStop(instance, bus, busStop); if (stop == null) { stop = new Stop(bus, busStop, null, new Date(System.currentTimeMillis()), false); } // Log.i(TAG, "Finished fetching stop for busStop " + position); } 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(position, stop); displayTimetable(timetable); } } } @Override public Object onRetainNonConfigurationInstance() { return timetable; } private void displayTimetable(Timetable timetable) { visibleTimetable = (Timetable) timetable.clone(); if (timetable.size() == 0) { busContentMessage.setText("No Busses"); busContentMessage.setVisibility(View.VISIBLE); busActivityContentLayout.setGravity(Gravity.CENTER); } else { if (visibleTimetable.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(visibleTimetable); } else { adapter = new BusSpecificTimetableAdapter(this, visibleTimetable); timetableView.setAdapter(adapter); } busActivityContentLayout.setGravity(Gravity.TOP); } } } }