package com.njtransit; import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.net.ConnectivityManager; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.Window; import android.widget.ArrayAdapter; import android.widget.LinearLayout; import android.widget.TextView; import com.admob.android.ads.AdView; import com.admob.android.ads.SimpleAdListener; import com.njtransit.departurevision.DepartureVision; import com.njtransit.departurevision.DepartureVision.TrainStatusListener; import com.njtransit.domain.IService; import com.njtransit.domain.Station; import com.njtransit.domain.Stop; import com.njtransit.domain.TrainStatus; import com.njtransit.model.StopsQueryResult; import com.njtransit.rail.R; import com.njtransit.ui.adapter.StopAdapter; public class StopActivity extends SchedulerActivity implements TrainStatusListener { private StopListView stopTimes; private TimerTask updaterThread = null; private boolean needsReschedule = false; private TextView departure; private TextView arrival; private TextView errors; private Timer timer; ProgressDialog progress = null; private boolean needProgress; private int refreshCount = 0; private List<Stop> stops = new ArrayList<Stop>(); private DepartureVision departureVision; public static final String DEPARTURE_ID = "departure-id"; public static final String ARRIVAL_ID = "arrival-id"; private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { StopAdapter adapter = (StopAdapter)stopTimes.getAdapter(); switch(msg.what) { case MINUTES_AWAY: for (Map.Entry<TextView, Integer> e : minutesAway .entrySet()) { e.getKey().setVisibility( e.getValue() == null ? View.GONE : View.VISIBLE); if (e.getValue() != null) { e.getKey().setText( String.format("departs in %s minutes", e.getValue())); } } adapter.notifyDataSetChanged(); stopTimes.invalidateViews(); stopTimes.invalidate(); break; case TRAIN_STATUS: Stop stop = adapter.getItem(msg.arg1); TrainStatus status = (TrainStatus)msg.obj; for(int i = 0; i < stopTimes.getChildCount(); i++) { LinearLayout row = (LinearLayout)stopTimes.getChildAt(i); Stop stopB = (Stop)stopTimes.getItemAtPosition(row.getId()); if(stop.equals(stopB)) { adapter.getStatuses().put(stop, status); adapter.notifyDataSetChanged(); stopTimes.invalidateViews(); stopTimes.invalidate(); break; } } } } }; private Map<TextView, Integer> minutesAway = new HashMap<TextView, Integer>(); private AdView ad; private ConnectivityManager mConnectivity; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mConnectivity = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.stop_list_home); Station departure = getSchedulerContext().getDepartureStation(); Station arrival = getSchedulerContext().getArrivalStation(); this.departure = (TextView) findViewById(R.id.departureText); this.arrival = (TextView) findViewById(R.id.arrivalText); stopTimes = (StopListView) findViewById(R.id.list); errors = (TextView) findViewById(R.id.errors); ad = (AdView) findViewById(R.id.ad); if (savedInstanceState == null) { findAndShowStops(departure, arrival); ad.setAdListener(new SimpleAdListener() { @Override public void onReceiveAd(AdView arg0) { super.onReceiveAd(arg0); ad.setVisibility(View.VISIBLE); } @Override public void onFailedToReceiveAd(AdView arg0) { // TODO Auto-generated method stub super.onFailedToReceiveAd(arg0); } }); } } Comparator<Stop> comparator = new Comparator<Stop>() { @Override public int compare(Stop o1, Stop o2) { return o1.getDepart().compareTo(o2.getDepart()); } }; private void populateStationsHeader(Station departure, Station arrival) { this.departure.setText(departure.getName()); this.arrival.setText(arrival.getName()); } private void findAndShowStops(Station depart, Station arrive) { final Station departure; final Station arrival; // if depart is null or arrive is null then use testing values. final boolean useMockData; if (depart != null) { departure = depart; useMockData = false; } else { departure = new Station(148, "Trenton Transit Center", 72.5, 72.5, "TR"); useMockData = true; getSchedulerContext().setDepartureStation(departure); } if (arrive != null) { arrival = arrive; } else { arrival = new Station(105, "New York Penn Station", 72.5, 72.5, "NY"); getSchedulerContext().setArrivalStation(arrival); } if (useMockData) { populateStationsHeader(departure, arrival); } new AsyncTask<Void, Void, StopResult>() { @Override protected StopResult doInBackground(Void... params) { Map<Integer, Set<Integer>> data = getSchedulerContext() .getAdapter().getRoutesStationIsOn(departure, arrival); Set<Integer> routes = new HashSet<Integer>(); for (Set<Integer> mRoutes : data.values()) { routes.addAll(mRoutes); } Set<Integer> routeIdToStopIds = getSchedulerContext() .getAdapter().getStopsOnEachRoute(routes); try { Root .saveLastArrivalStation(StopActivity.this, getSchedulerContext().getDepartureStation() .getId()); Root.saveLastDepartureStation(StopActivity.this, getSchedulerContext().getArrivalStation().getId()); } finally { } final StopsQueryResult sqr; if (getSchedulerContext().getDepartureDate() != null) { sqr = getSchedulerContext().getAdapter() .getStopTimesAlternate(departure, arrival, useMockData, getSchedulerContext().getDepartureDate()); } else { sqr = getSchedulerContext().getAdapter() .getStopTimesAlternate(departure, arrival, useMockData); } final ArrayList<Stop> today = new ArrayList<Stop>(); final ArrayList<Stop> tomorrow = new ArrayList<Stop>(); final Long now = System.currentTimeMillis(); final Calendar day = Calendar.getInstance(); Stop closest = null; Long closestDiff = Long.MAX_VALUE; if (!(day.get(Calendar.YEAR) == sqr.getDepartureDate().get( Calendar.YEAR) && day.get(Calendar.DAY_OF_YEAR) == sqr .getDepartureDate().get(Calendar.DAY_OF_YEAR))) { for (Stop stop : sqr.getStops()) { IService service = sqr.getTripToService().get( stop.getTripId()); if (service.isDate(sqr.getDepartureDate())) { Calendar newDepart = Calendar.getInstance(); newDepart.setTimeInMillis(stop.getDepart() .getTimeInMillis()); Calendar newArrive = Calendar.getInstance(); newArrive.setTimeInMillis(stop.getArrive() .getTimeInMillis()); newDepart.set(Calendar.YEAR, sqr.getDepartureDate() .get(Calendar.YEAR)); newDepart.set(Calendar.DAY_OF_YEAR, sqr .getDepartureDate().get( Calendar.DAY_OF_YEAR)); newArrive.set(Calendar.YEAR, sqr.getDepartureDate() .get(Calendar.YEAR)); newArrive.set(Calendar.DAY_OF_YEAR, sqr .getDepartureDate().get( Calendar.DAY_OF_YEAR)); if (stop.getDepart().get(Calendar.DAY_OF_YEAR) < stop .getArrive().get(Calendar.DAY_OF_YEAR)) { newArrive.add(Calendar.DAY_OF_YEAR, 1); } Stop stop2 = new Stop(stop.getTripId(), newDepart, newArrive,stop.getBlockId()); stops.add(stop2); } } Collections.sort(stops, comparator); } else { Calendar relativeTime = Calendar.getInstance(); Calendar tomorrowDate = Calendar.getInstance(); tomorrowDate.add(Calendar.DAY_OF_YEAR, 1); for (Stop stop : sqr.getStops()) { IService service = sqr.getTripToService().get( stop.getTripId()); // relativeTime.set(Calendar.HOUR_OF_DAY,stop.getDepart().get(Calendar.HOUR_OF_DAY)); // relativeTime.set(Calendar.MINUTE, // stop.getDepart().get(Calendar.MINUTE)); if (service.isToday()) { Calendar newDepart = Calendar.getInstance(); newDepart.setTimeInMillis(stop.getDepart() .getTimeInMillis()); Calendar newArrive = Calendar.getInstance(); newArrive.setTimeInMillis(stop.getArrive() .getTimeInMillis()); newDepart.set(Calendar.YEAR, relativeTime .get(Calendar.YEAR)); newDepart.set(Calendar.DAY_OF_YEAR, relativeTime .get(Calendar.DAY_OF_YEAR)); newArrive.set(Calendar.YEAR, relativeTime .get(Calendar.YEAR)); newArrive.set(Calendar.DAY_OF_YEAR, relativeTime .get(Calendar.DAY_OF_YEAR)); if (stop.getDepart().get(Calendar.DAY_OF_YEAR) < stop .getArrive().get(Calendar.DAY_OF_YEAR)) { newArrive.add(Calendar.DAY_OF_YEAR, 1); } Long diff = newDepart.getTimeInMillis() - relativeTime.getTimeInMillis(); if (diff > -5400001) { Stop newStop = new Stop(stop.getTripId(), newDepart, newArrive,stop.getBlockId()); if (diff > 0 && diff < closestDiff) { closest = newStop; closestDiff = diff; } today.add(newStop); } } if (service.isTomorrow()) { if (stop.getDepart().get(Calendar.HOUR_OF_DAY) < 6) { Calendar tom = Calendar.getInstance(); tom.setTimeInMillis(tomorrowDate .getTimeInMillis()); Calendar newDepart = Calendar.getInstance(); newDepart.setTimeInMillis(stop.getDepart() .getTimeInMillis()); Calendar newArrive = Calendar.getInstance(); newArrive.setTimeInMillis(stop.getArrive() .getTimeInMillis()); newDepart.set(Calendar.YEAR, tom .get(Calendar.YEAR)); newDepart.set(Calendar.DAY_OF_YEAR, tom .get(Calendar.DAY_OF_YEAR)); newArrive.set(Calendar.YEAR, tom .get(Calendar.YEAR)); newArrive.set(Calendar.DAY_OF_YEAR, tom .get(Calendar.DAY_OF_YEAR)); if (stop.getDepart().get(Calendar.DAY_OF_YEAR) < stop .getArrive().get(Calendar.DAY_OF_YEAR)) { newArrive.add(Calendar.DAY_OF_YEAR, 1); } Stop newStop = new Stop(stop.getTripId(), newDepart, newArrive,stop.getBlockId()); Long diff = newDepart.getTimeInMillis() - relativeTime.getTimeInMillis(); if (diff > 0 && diff < closestDiff) { closest = stop; closestDiff = diff; } tomorrow.add(newStop); } } } Collections.sort(today, comparator); Collections.sort(tomorrow, comparator); today.addAll(tomorrow); stops.addAll(today); stops.isEmpty(); } return new StopResult(sqr, closest); } @Override protected void onPreExecute() { trackPageView(getClass().getSimpleName() + "/" + departure.getName() + "_to_" + arrival.getName()); populateStationsHeader(departure, arrival); progress = ProgressDialog.show(StopActivity.this, "Please wait", "Loading schedule ...", true); needProgress = true; } @Override protected void onCancelled() { progress.cancel(); } @SuppressWarnings("unchecked") @Override protected void onPostExecute(StopResult result) { if (progress.isShowing()) { needProgress = false; progress.dismiss(); } Stop closest = result.getClosest(); if (!stops.isEmpty()) { StopAdapter stopAdapter = new StopAdapter( StopActivity.this, result.getStopQueryResult() .getDepartureDate(), result .getStopQueryResult().getTripToService(), stops); stopTimes.setAdapter(stopAdapter); if (closest != null) { stopTimes.setSelectionFromTop( ((ArrayAdapter<Stop>) stopTimes.getAdapter()) .getPosition(closest), 10); } timer = new Timer(false); Calendar c = Calendar.getInstance(); c.clear(Calendar.MILLISECOND); c.clear(Calendar.SECOND); c.set(Calendar.MINUTE, c.get(Calendar.MINUTE) + 1); timer.scheduleAtFixedRate(newUpdaterThread(), c.getTime(), 60000); new Thread() { @Override public void run() { try { if (getSchedulerContext().getDepartureStation() != null && getSchedulerContext() .getArrivalStation() != null) { getSchedulerContext() .getAdapter() .saveHistory( getSchedulerContext() .getDepartureStation() .getId(), getSchedulerContext() .getArrivalStation() .getId(), System.currentTimeMillis()); } } catch (Exception e) { Log.e(getClass().getSimpleName(), "could not log saveHistory", e); } } }.start(); } else { String address = String .format("feedback_%s_%s_%s", getSchedulerContext() .getDepartureStation().getId(), getSchedulerContext().getArrivalStation() .getId(), getString(R.string.email_address)); String appName = getString(R.string.app_name_full); String question = String .format( "We are unable to find results for your search criteria. Please note that %s does not support connections. To provide additional feedback, please email %s.", appName, address); stopTimes.setVisibility(View.GONE); errors.setVisibility(View.VISIBLE); errors.setText(question); } trackEvent("stop_times", "query", stops.size() + " stops for " + departure.getName() + " to " + arrival.getName() + " in " + result.getStopQueryResult().getQueryDuration(), 0); if (result.getStopQueryResult().getDepart() .getAlternateId() != null) { departureVision = new DepartureVision(); final String alternateId = result.getStopQueryResult() .getDepart().getAlternateId(); new Thread() { @Override public void run() { try { departureVision.addListener(StopActivity.this); departureVision.startDepartures(alternateId); } catch (IOException e) { e.printStackTrace(); } } }.start(); } } }.execute(); } public void onTrainStatus(TrainStatus status) { StopAdapter adapter = (StopAdapter)this.stopTimes.getAdapter(); try { for(int i = 0; i < adapter.getCount(); i++) { Stop stop = adapter.getItem(i); if(status.getTrain()!=null && stop.getBlockId()!=null) { if(stop.getBlockId().equals(status.getTrain())) { Message m = Message.obtain(); m.arg1 = i; m.what = TRAIN_STATUS; m.obj = status; mHandler.sendMessage(m); } } } } catch (Exception e) { } } private static final int TRAIN_STATUS=2; private TimerTask newUpdaterThread() { updaterThread = new TimerTask() { @Override public void run() { minutesAway.clear(); for (int i = 0; i < stopTimes.getChildCount(); i++) { final Integer away; LinearLayout row = (LinearLayout) stopTimes.getChildAt(i); TextView minutesAway = (TextView) row .findViewById(R.id.away); Stop stop = (Stop) stopTimes.getItemAtPosition(row.getId()); long awayTimeInMinutes = StopAdapter .awayTimeInMinutes(stop); Calendar tomorrow = Calendar.getInstance(); int hourOfDay = tomorrow.get(Calendar.HOUR_OF_DAY); if (awayTimeInMinutes >= 0 && (awayTimeInMinutes <= 100 || ((hourOfDay < 4 || hourOfDay > 18) && awayTimeInMinutes <= 200))) { // minutesAway.setVisibility(View.VISIBLE); // minutesAway.setText(String.format("departs in %s minutes",awayTimeInMinutes)); away = (int) awayTimeInMinutes; } else { away = null; // minutesAway.setVisibility(View.GONE); } StopActivity.this.minutesAway.put(minutesAway, away); } if (minutesAway.isEmpty()) { return; } trackEvent("stops", "refresh", new Date().toString(), ++refreshCount); Message m = Message.obtain(); m.what = MINUTES_AWAY; mHandler.sendMessage(m); } }; return updaterThread; } private static final int MINUTES_AWAY = 1; protected void onPause() { super.onPause(); if (stopTimes != null) { try { if (timer != null) { needsReschedule = true; timer.cancel(); } } catch (Exception e) { Log.w("error", "onPause", e); } } if (progress != null) { if (progress.isShowing()) { progress.dismiss(); } } } protected void onResume() { super.onResume(); if (stopTimes != null) { try { if (timer != null && needsReschedule) { timer = new Timer(false); timer.schedule(newUpdaterThread(), 300); Calendar c = Calendar.getInstance(); c.set(Calendar.SECOND, 0); c.set(Calendar.MILLISECOND, 0); c.add(Calendar.MINUTE, 1); timer.schedule(newUpdaterThread(), c.getTime()); } } catch (Exception e) { Log.e(getClass().getSimpleName(), "onResume", e); } } if (needProgress) { progress.show(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); if (stops.size() > 0) { MenuItem reverse = menu.add(Menu.NONE, 1, Menu.FIRST, getString(R.string.reverse)); reverse.setIcon(R.drawable.signpost); } return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == 1) { trackEvent("menu-click", "MenuButton", item.getTitle().toString(), item.getItemId()); getSchedulerContext().reverseTrip(); Intent intent = new Intent(this, StopActivity.class); startActivity(intent); } return super.onOptionsItemSelected(item); } }