Changed arrivals to departures.

Added handling for WHITE and SPCL

--HG--
rename : res/layout/arrival_listing.xml => res/layout/departure_listing.xml
rename : src/com/dougkeen/bart/ArrivalArrayAdapter.java => src/com/dougkeen/bart/DepartureArrayAdapter.java
rename : src/com/dougkeen/bart/GetRealTimeArrivalsTask.java => src/com/dougkeen/bart/GetRealTimeDeparturesTask.java
rename : src/com/dougkeen/bart/ViewArrivalsActivity.java => src/com/dougkeen/bart/ViewDeparturesActivity.java
rename : src/com/dougkeen/bart/data/Arrival.java => src/com/dougkeen/bart/data/Departure.java
rename : src/com/dougkeen/bart/data/RealTimeArrivals.java => src/com/dougkeen/bart/data/RealTimeDepartures.java
This commit is contained in:
dkeen 2011-06-05 11:13:21 -07:00
parent 67e6f5347d
commit ca8f07b30a
13 changed files with 271 additions and 188 deletions

View File

@ -35,7 +35,7 @@
<data android:mimeType="vnd.android.cursor.dir/com.dougkeen.bart.favorite" />
</intent-filter>
</activity>
<activity android:name=".ViewArrivalsActivity"
<activity android:name="ViewDeparturesActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.VIEW" />

View File

@ -8,20 +8,20 @@
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:layout_toRightOf="@id/destinationColorBar"
android:layout_alignParentTop="true">
<TextView android:id="@+id/destinationText" style="@style/ArrivalDestinationText"
<TextView android:id="@+id/destinationText" style="@style/DepartureDestinationText"
android:layout_weight="1" android:singleLine="true"
android:ellipsize="marquee" />
<ImageView android:id="@+id/bikeIcon" android:src="@drawable/bike"
style="@style/BikeIcon" />
<TextView android:id="@+id/countdown" style="@style/ArrivalCountdownText"
<TextView android:id="@+id/countdown" style="@style/DepartureCountdownText"
android:gravity="right" />
</LinearLayout>
<ImageView android:id="@+id/xferIcon" android:src="@drawable/xfer"
android:layout_below="@id/topRow" android:layout_alignParentRight="true"
style="@style/XferIcon" />
<TextView android:id="@+id/trainLengthText" style="@style/ArrivalTrainLengthText"
<TextView android:id="@+id/trainLengthText" style="@style/DepartureTrainLengthText"
android:layout_toRightOf="@id/destinationColorBar"
android:layout_below="@id/topRow" />
<TextView android:layout_alignParentRight="true" android:id="@+id/uncertainty"
android:layout_below="@id/topRow" style="@style/ArrivalUncertaintyText" />
android:layout_below="@id/topRow" style="@style/DepartureUncertaintyText" />
</RelativeLayout>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="@string/view_arrivals" android:id="@+id/view"></item>
<item android:title="@string/view_departures" android:id="@+id/view"></item>
<item android:title="@string/delete" android:id="@+id/delete"></item>
</menu>

View File

@ -2,7 +2,8 @@
<resources>
<string name="app_name">BART Catcher</string>
<string name="favorite_routes">Favorite Routes</string>
<string name="empty_favorites_list_message">Press the menu button and select \"Add route\" to add
<string name="empty_favorites_list_message">Press the menu button and select \"Add route\" to
add
a route</string>
<string name="add_route">Add a route</string>
<string name="origin">Origin</string>
@ -12,15 +13,17 @@
different</string>
<string name="error_null_destination">You must select a destination station</string>
<string name="error_null_origin">You must select an origin station</string>
<string name="arrival_wait_message">Please wait while real time arrival data is
<string name="departure_wait_message">Please wait while real time departure data is
loaded...</string>
<string name="no_data_message">No arrival data is currently available for this
route</string>
<string name="no_data_message">No departure data is currently available for this
route. Note that there may be service advisories posted at
http://m.bart.gov/schedules/advisories/</string>
<string name="view">View</string>
<string name="view_arrivals">View arrivals</string>
<string name="view_departures">View departures</string>
<string name="delete">Delete</string>
<string name="yes">Yes</string>
<string name="cancel">Cancel</string>
<string name="view_on_bart_site">View details on BART site</string>
<string name="could_not_connect">Could not connect to BART services. Please try again later.</string>
<string name="could_not_connect">Could not connect to BART services. Please try
again later.</string>
</resources>

View File

@ -18,7 +18,7 @@
<item name="android:singleLine">true</item>
</style>
<style name="ArrivalDestinationText">
<style name="DepartureDestinationText">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textSize">22dp</item>
@ -27,7 +27,7 @@
<item name="android:layout_marginLeft">3dp</item>
</style>
<style name="ArrivalTrainLengthText">
<style name="DepartureTrainLengthText">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textSize">18dp</item>
@ -35,14 +35,14 @@
<item name="android:layout_marginLeft">3dp</item>
</style>
<style name="ArrivalCountdownText">
<style name="DepartureCountdownText">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textSize">20dp</item>
<item name="android:singleLine">true</item>
<item name="android:width">90dp</item>
</style>
<style name="ArrivalUncertaintyText">
<style name="DepartureUncertaintyText">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textSize">16dp</item>

View File

@ -13,36 +13,36 @@ import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.dougkeen.bart.data.Arrival;
import com.dougkeen.bart.data.Departure;
public class ArrivalArrayAdapter extends ArrayAdapter<Arrival> {
public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
public ArrivalArrayAdapter(Context context, int textViewResourceId,
Arrival[] objects) {
public DepartureArrayAdapter(Context context, int textViewResourceId,
Departure[] objects) {
super(context, textViewResourceId, objects);
}
public ArrivalArrayAdapter(Context context, int resource,
int textViewResourceId, Arrival[] objects) {
public DepartureArrayAdapter(Context context, int resource,
int textViewResourceId, Departure[] objects) {
super(context, resource, textViewResourceId, objects);
}
public ArrivalArrayAdapter(Context context, int resource,
int textViewResourceId, List<Arrival> objects) {
public DepartureArrayAdapter(Context context, int resource,
int textViewResourceId, List<Departure> objects) {
super(context, resource, textViewResourceId, objects);
}
public ArrivalArrayAdapter(Context context, int resource,
public DepartureArrayAdapter(Context context, int resource,
int textViewResourceId) {
super(context, resource, textViewResourceId);
}
public ArrivalArrayAdapter(Context context, int textViewResourceId,
List<Arrival> objects) {
public DepartureArrayAdapter(Context context, int textViewResourceId,
List<Departure> objects) {
super(context, textViewResourceId, objects);
}
public ArrivalArrayAdapter(Context context, int textViewResourceId) {
public DepartureArrayAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
@ -53,30 +53,30 @@ public class ArrivalArrayAdapter extends ArrayAdapter<Arrival> {
view = convertView;
} else {
LayoutInflater inflater = LayoutInflater.from(getContext());
view = inflater.inflate(R.layout.arrival_listing, parent, false);
view = inflater.inflate(R.layout.departure_listing, parent, false);
}
Arrival arrival = getItem(position);
((TextView) view.findViewById(R.id.destinationText)).setText(arrival
Departure departure = getItem(position);
((TextView) view.findViewById(R.id.destinationText)).setText(departure
.getDestination().toString());
((TextView) view.findViewById(R.id.trainLengthText)).setText(arrival
((TextView) view.findViewById(R.id.trainLengthText)).setText(departure
.getTrainLengthText());
ImageView colorBar = (ImageView) view
.findViewById(R.id.destinationColorBar);
((GradientDrawable) colorBar.getDrawable()).setColor(Color
.parseColor(arrival.getDestinationColor()));
((TextView) view.findViewById(R.id.countdown)).setText(arrival
.parseColor(departure.getDestinationColor()));
((TextView) view.findViewById(R.id.countdown)).setText(departure
.getCountdownText());
((TextView) view.findViewById(R.id.uncertainty)).setText(arrival
((TextView) view.findViewById(R.id.uncertainty)).setText(departure
.getUncertaintyText());
if (arrival.isBikeAllowed()) {
if (departure.isBikeAllowed()) {
((ImageView) view.findViewById(R.id.bikeIcon))
.setVisibility(View.VISIBLE);
} else {
((ImageView) view.findViewById(R.id.bikeIcon))
.setVisibility(View.INVISIBLE);
}
if (arrival.getRequiresTransfer()) {
if (departure.getRequiresTransfer()) {
((ImageView) view.findViewById(R.id.xferIcon))
.setVisibility(View.VISIBLE);
} else {

View File

@ -10,32 +10,34 @@ import org.xml.sax.helpers.DefaultHandler;
import android.util.Log;
import com.dougkeen.bart.data.Arrival;
import com.dougkeen.bart.data.RealTimeArrivals;
import com.dougkeen.bart.data.Departure;
import com.dougkeen.bart.data.RealTimeDepartures;
public class EtdContentHandler extends DefaultHandler {
public EtdContentHandler(Station origin, Station destination,
List<Route> routes) {
super();
realTimeArrivals = new RealTimeArrivals(origin, destination, routes);
realTimeDepartures = new RealTimeDepartures(origin, destination, routes);
}
private final static List<String> TAGS = Arrays.asList("date", "time",
"abbreviation", "minutes", "platform", "direction", "length",
"color", "hexcolor", "bikeflag");
private RealTimeArrivals realTimeArrivals;
private RealTimeDepartures realTimeDepartures;
public RealTimeArrivals getRealTimeArrivals() {
return realTimeArrivals;
public RealTimeDepartures getRealTimeDepartures() {
return realTimeDepartures;
}
private String date;
private String currentDestination;
private String currentValue;
private Arrival currentArrival;
private Departure currentDeparture;
private boolean isParsingTag;
private boolean getDestinationFromLine;
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
@ -51,9 +53,13 @@ public class EtdContentHandler extends DefaultHandler {
isParsingTag = true;
}
if (localName.equals("estimate")) {
currentArrival = new Arrival();
currentArrival.setDestination(Station
.getByAbbreviation(currentDestination));
currentDeparture = new Departure();
if (currentDestination.equalsIgnoreCase("SPCL")) {
getDestinationFromLine = true;
} else {
currentDeparture.setDestination(Station
.getByAbbreviation(currentDestination));
}
}
}
@ -63,40 +69,60 @@ public class EtdContentHandler extends DefaultHandler {
if (localName.equals("date")) {
date = currentValue;
} else if (localName.equals("time")) {
realTimeArrivals.setTime(Date.parse(date + " " + currentValue));
realTimeDepartures.setTime(Date.parse(date + " " + currentValue));
} else if (localName.equals("abbreviation")) {
currentDestination = currentValue;
} else if (localName.equals("minutes")) {
if (currentValue.equalsIgnoreCase("arrived")) {
currentArrival.setMinutes(0);
currentDeparture.setMinutes(0);
} else {
currentArrival.setMinutes(Integer.parseInt(currentValue));
currentDeparture.setMinutes(Integer.parseInt(currentValue));
}
} else if (localName.equals("platform")) {
currentArrival.setPlatform(currentValue);
currentDeparture.setPlatform(currentValue);
} else if (localName.equals("direction")) {
currentArrival.setDirection(currentValue);
currentDeparture.setDirection(currentValue);
} else if (localName.equals("length")) {
currentArrival.setTrainLength(Integer.parseInt(currentValue));
currentDeparture.setTrainLength(Integer.parseInt(currentValue));
} else if (localName.equals("color")) {
try {
currentArrival.setLine(Line.valueOf(currentValue));
if (getDestinationFromLine) {
final Line line = Line.valueOf(currentValue);
currentDeparture.setLine(line);
currentDeparture.setDestination(line
.getUsualTerminusForDirectionAndOrigin(
currentDeparture.getDirection(),
realTimeDepartures.getOrigin()));
} else if (currentValue.equalsIgnoreCase("WHITE")) {
for (Line line : Line.values()) {
if (line.stations.indexOf(currentDeparture
.getDestination()) >= 0
&& line.stations.indexOf(realTimeDepartures
.getDestination()) >= 0) {
currentDeparture.setLine(line);
break;
}
}
} else {
currentDeparture.setLine(Line.valueOf(currentValue));
}
} catch (IllegalArgumentException e) {
Log.w("BartApp", "There is no line called '" + currentValue
Log.w(Constants.TAG, "There is no line called '" + currentValue
+ "'");
}
} else if (localName.equals("hexcolor")) {
currentArrival.setDestinationColor("#ff"
currentDeparture.setDestinationColor("#ff"
+ currentValue.substring(1));
} else if (localName.equals("bikeflag")) {
currentArrival.setBikeAllowed(currentValue.equalsIgnoreCase("1"));
currentDeparture.setBikeAllowed(currentValue.equalsIgnoreCase("1"));
} else if (localName.equals("estimate")) {
realTimeArrivals.addArrival(currentArrival);
currentArrival = null;
realTimeDepartures.addDeparture(currentDeparture);
currentDeparture = null;
getDestinationFromLine = false;
} else if (localName.equals("etd")) {
currentDestination = null;
} else if (localName.equals("station")) {
realTimeArrivals.sortArrivals();
realTimeDepartures.sortDepartures();
}
isParsingTag = false;
currentValue = null;

View File

@ -9,13 +9,13 @@ import java.util.List;
import org.xml.sax.SAXException;
import com.dougkeen.bart.data.RealTimeArrivals;
import com.dougkeen.bart.data.RealTimeDepartures;
import android.os.AsyncTask;
import android.util.Xml;
public abstract class GetRealTimeArrivalsTask extends
AsyncTask<GetRealTimeArrivalsTask.Params, Integer, RealTimeArrivals> {
public abstract class GetRealTimeDeparturesTask extends
AsyncTask<GetRealTimeDeparturesTask.Params, Integer, RealTimeDepartures> {
private static final int CONNECTION_TIMEOUT_MILLIS = 10000;
private final static String API_KEY = "5LD9-IAYI-TRAT-MHHW";
@ -28,20 +28,20 @@ public abstract class GetRealTimeArrivalsTask extends
private List<Route> mRoutes;
@Override
protected RealTimeArrivals doInBackground(Params... paramsArray) {
protected RealTimeDepartures doInBackground(Params... paramsArray) {
// Always expect one param
Params params = paramsArray[0];
mRoutes = params.origin.getRoutesForDestination(params.destination);
if (!isCancelled()) {
return getArrivalsFromNetwork(params, 0);
return getDeparturesFromNetwork(params, 0);
} else {
return null;
}
}
private RealTimeArrivals getArrivalsFromNetwork(Params params,
private RealTimeDepartures getDeparturesFromNetwork(Params params,
int attemptNumber) {
try {
URL sourceUrl = new URL(String.format(API_URL,
@ -57,9 +57,9 @@ public abstract class GetRealTimeArrivalsTask extends
Xml.parse(connection.getInputStream(),
Xml.findEncodingByName("UTF-8"),
handler);
final RealTimeArrivals realTimeArrivals = handler
.getRealTimeArrivals();
return realTimeArrivals;
final RealTimeDepartures realTimeDepartures = handler
.getRealTimeDepartures();
return realTimeDepartures;
} catch (MalformedURLException e) {
throw new RuntimeException(e);
} catch (UnsupportedEncodingException e) {
@ -71,7 +71,7 @@ public abstract class GetRealTimeArrivalsTask extends
} catch (InterruptedException interrupt) {
// Ignore... just go on to next attempt
}
return getArrivalsFromNetwork(params, attemptNumber + 1);
return getDeparturesFromNetwork(params, attemptNumber + 1);
} else {
mIOException = e;
return null;
@ -101,7 +101,7 @@ public abstract class GetRealTimeArrivalsTask extends
}
@Override
protected void onPostExecute(RealTimeArrivals result) {
protected void onPostExecute(RealTimeDepartures result) {
if (result != null) {
onResult(result);
} else {
@ -109,7 +109,7 @@ public abstract class GetRealTimeArrivalsTask extends
}
}
public abstract void onResult(RealTimeArrivals result);
public abstract void onResult(RealTimeDepartures result);
public abstract void onNetworkError(IOException e);
}

View File

@ -92,4 +92,22 @@ public enum Line {
return destinations;
}
public Station getUsualTerminusForDirectionAndOrigin(String direction,
Station origin) {
boolean isNorth = false;
if (direction.toLowerCase().startsWith("s") && directionMayInvert
&& origin.invertDirection) {
isNorth = true;
} else if (direction.toLowerCase().startsWith("n")
&& !(directionMayInvert && origin.invertDirection)) {
isNorth = true;
}
if (isNorth) {
return stations.get(stations.size() - 1);
} else {
return stations.get(0);
}
}
}

View File

@ -47,7 +47,8 @@ public enum Station {
UCTY("ucty", "Union City", true, "bayf"),
WCRK("wcrk", "Walnut Creek", false, "mcar"),
WDUB("wdub", "West Dublin/Pleasanton", false, "bayf"),
WOAK("woak", "West Oakland", false);
WOAK("woak", "West Oakland", false),
SPCL("spcl", "Special", false);
public final String abbreviation;
public final String name;

View File

@ -13,6 +13,7 @@ import android.os.Bundle;
import android.os.Parcelable;
import android.os.PowerManager;
import android.text.format.DateFormat;
import android.text.util.Linkify;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
@ -21,12 +22,12 @@ import android.widget.ArrayAdapter;
import android.widget.TextView;
import android.widget.Toast;
import com.dougkeen.bart.GetRealTimeArrivalsTask.Params;
import com.dougkeen.bart.data.Arrival;
import com.dougkeen.bart.data.RealTimeArrivals;
import com.dougkeen.bart.GetRealTimeDeparturesTask.Params;
import com.dougkeen.bart.data.Departure;
import com.dougkeen.bart.data.RealTimeDepartures;
import com.dougkeen.bart.data.RoutesColumns;
public class ViewArrivalsActivity extends ListActivity {
public class ViewDeparturesActivity extends ListActivity {
private static final int UNCERTAINTY_THRESHOLD = 17;
@ -35,11 +36,11 @@ public class ViewArrivalsActivity extends ListActivity {
private Station mOrigin;
private Station mDestination;
private ArrayAdapter<Arrival> mArrivalsAdapter;
private ArrayAdapter<Departure> mDeparturesAdapter;
private TextView mListTitleView;
private AsyncTask<Params, Integer, RealTimeArrivals> mGetArrivalsTask;
private AsyncTask<Params, Integer, RealTimeDepartures> mGetDeparturesTask;
private boolean mIsAutoUpdating = false;
@ -52,7 +53,7 @@ public class ViewArrivalsActivity extends ListActivity {
private PowerManager.WakeLock mWakeLock;
private boolean mFetchArrivalsOnNextFocus;
private boolean mFetchDeparturesOnNextFocus;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -77,31 +78,32 @@ public class ViewArrivalsActivity extends ListActivity {
mOrigin = Station.getByAbbreviation(cursor.getString(0));
mDestination = Station.getByAbbreviation(cursor.getString(1));
String header = mOrigin.name + " to " + mDestination.name;
String header = "Departures:\n" + mOrigin.name + " to "
+ mDestination.name;
mListTitleView = (TextView) findViewById(R.id.listTitle);
mListTitleView.setText(header);
((TextView) findViewById(android.R.id.empty))
.setText(R.string.arrival_wait_message);
.setText(R.string.departure_wait_message);
mArrivalsAdapter = new ArrivalArrayAdapter(this,
R.layout.arrival_listing);
mDeparturesAdapter = new DepartureArrayAdapter(this,
R.layout.departure_listing);
if (savedInstanceState != null
&& savedInstanceState.containsKey("arrivals")) {
for (Parcelable arrival : savedInstanceState
.getParcelableArray("arrivals")) {
mArrivalsAdapter.add((Arrival) arrival);
&& savedInstanceState.containsKey("departures")) {
for (Parcelable departure : savedInstanceState
.getParcelableArray("departures")) {
mDeparturesAdapter.add((Departure) departure);
}
}
setListAdapter(mArrivalsAdapter);
setListAdapter(mDeparturesAdapter);
mFetchArrivalsOnNextFocus = true;
mFetchDeparturesOnNextFocus = true;
}
@Override
protected void onDestroy() {
if (mGetArrivalsTask != null) {
mGetArrivalsTask.cancel(true);
if (mGetDeparturesTask != null) {
mGetDeparturesTask.cancel(true);
}
super.onDestroy();
}
@ -109,26 +111,28 @@ public class ViewArrivalsActivity extends ListActivity {
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Arrival[] arrivals = new Arrival[mArrivalsAdapter.getCount()];
for (int i = mArrivalsAdapter.getCount() - 1; i >= 0; i--) {
arrivals[i] = mArrivalsAdapter.getItem(i);
Departure[] departures = new Departure[mDeparturesAdapter.getCount()];
for (int i = mDeparturesAdapter.getCount() - 1; i >= 0; i--) {
departures[i] = mDeparturesAdapter.getItem(i);
}
outState.putParcelableArray("arrivals", arrivals);
outState.putParcelableArray("departures", departures);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
if (mFetchArrivalsOnNextFocus) {
fetchLatestArrivals();
mFetchArrivalsOnNextFocus = false;
if (mFetchDeparturesOnNextFocus) {
fetchLatestDepartures();
mFetchDeparturesOnNextFocus = false;
}
PowerManager powerManaer = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManaer.newWakeLock(
PowerManager.SCREEN_DIM_WAKE_LOCK, "ViewArrivalsActivity");
mWakeLock = powerManaer
.newWakeLock(
PowerManager.SCREEN_DIM_WAKE_LOCK,
"ViewDeparturesActivity");
mWakeLock.acquire();
if (mArrivalsAdapter != null && !mArrivalsAdapter.isEmpty()) {
if (mDeparturesAdapter != null && !mDeparturesAdapter.isEmpty()) {
mIsAutoUpdating = true;
}
runAutoUpdate();
@ -137,28 +141,28 @@ public class ViewArrivalsActivity extends ListActivity {
}
}
private void fetchLatestArrivals() {
private void fetchLatestDepartures() {
if (!hasWindowFocus())
return;
if (mGetArrivalsTask != null
&& mGetArrivalsTask.getStatus()
if (mGetDeparturesTask != null
&& mGetDeparturesTask.getStatus()
.equals(AsyncTask.Status.RUNNING)) {
// Don't overlap fetches
return;
}
mGetArrivalsTask = new GetRealTimeArrivalsTask() {
mGetDeparturesTask = new GetRealTimeDeparturesTask() {
@Override
public void onResult(RealTimeArrivals result) {
public void onResult(RealTimeDepartures result) {
Log.i(Constants.TAG, "Processing data from server");
processLatestArrivals(result);
processLatestDepartures(result);
Log.i(Constants.TAG, "Done processing data from server");
}
@Override
public void onNetworkError(IOException e) {
Log.w(Constants.TAG, e.getMessage());
Toast.makeText(ViewArrivalsActivity.this,
Toast.makeText(ViewDeparturesActivity.this,
R.string.could_not_connect,
Toast.LENGTH_LONG).show();
((TextView) findViewById(android.R.id.empty))
@ -167,80 +171,96 @@ public class ViewArrivalsActivity extends ListActivity {
}
};
Log.i(Constants.TAG, "Fetching data from server");
mGetArrivalsTask.execute(new GetRealTimeArrivalsTask.Params(mOrigin,
mGetDeparturesTask.execute(new GetRealTimeDeparturesTask.Params(
mOrigin,
mDestination));
}
protected void processLatestArrivals(RealTimeArrivals result) {
if (result.getArrivals().isEmpty()) {
((TextView) findViewById(android.R.id.empty))
.setText(R.string.no_data_message);
protected void processLatestDepartures(RealTimeDepartures result) {
if (result.getDepartures().isEmpty()) {
final TextView textView = (TextView) findViewById(android.R.id.empty);
textView.setText(R.string.no_data_message);
Linkify.addLinks(textView, Linkify.WEB_URLS);
return;
}
boolean needsBetterAccuracy = false;
Arrival firstArrival = null;
final List<Arrival> arrivals = result.getArrivals();
if (mArrivalsAdapter.getCount() > 0) {
Departure firstDeparture = null;
final List<Departure> departures = result.getDepartures();
if (mDeparturesAdapter.getCount() > 0) {
int adapterIndex = -1;
for (Arrival arrival : arrivals) {
for (Departure departure : departures) {
adapterIndex++;
Arrival existingArrival = null;
if (adapterIndex < mArrivalsAdapter.getCount()) {
existingArrival = mArrivalsAdapter.getItem(adapterIndex);
Departure existingDeparture = null;
if (adapterIndex < mDeparturesAdapter.getCount()) {
existingDeparture = mDeparturesAdapter
.getItem(adapterIndex);
}
while (existingArrival != null
&& !arrival.equals(existingArrival)) {
mArrivalsAdapter.remove(existingArrival);
if (adapterIndex < mArrivalsAdapter.getCount()) {
existingArrival = mArrivalsAdapter
while (existingDeparture != null
&& !departure.equals(existingDeparture)) {
mDeparturesAdapter.remove(existingDeparture);
if (adapterIndex < mDeparturesAdapter.getCount()) {
existingDeparture = mDeparturesAdapter
.getItem(adapterIndex);
} else {
existingArrival = null;
existingDeparture = null;
}
}
if (existingArrival != null) {
existingArrival.mergeEstimate(arrival);
if (existingDeparture != null) {
existingDeparture.mergeEstimate(departure);
} else {
mArrivalsAdapter.add(arrival);
existingArrival = arrival;
mDeparturesAdapter.add(departure);
existingDeparture = departure;
}
if (firstArrival == null) {
firstArrival = existingArrival;
if (firstDeparture == null) {
firstDeparture = existingDeparture;
}
if (existingArrival.getUncertaintySeconds() > UNCERTAINTY_THRESHOLD) {
if (existingDeparture.getUncertaintySeconds() > UNCERTAINTY_THRESHOLD) {
needsBetterAccuracy = true;
}
}
} else {
for (Arrival arrival : arrivals) {
if (firstArrival == null) {
firstArrival = arrival;
for (Departure departure : departures) {
if (firstDeparture == null) {
firstDeparture = departure;
}
mArrivalsAdapter.add(arrival);
mDeparturesAdapter.add(departure);
}
needsBetterAccuracy = true;
}
mArrivalsAdapter.notifyDataSetChanged();
mDeparturesAdapter.notifyDataSetChanged();
if (hasWindowFocus() && firstArrival != null) {
if (hasWindowFocus() && firstDeparture != null) {
if (needsBetterAccuracy
|| firstArrival.hasArrived()) {
|| firstDeparture.hasDeparted()) {
// Get more data in 20s
mListTitleView.postDelayed(new Runnable() {
@Override
public void run() {
fetchLatestArrivals();
fetchLatestDepartures();
}
}, 20000);
Log.i(Constants.TAG, "Scheduled another data fetch in 20s");
} else {
// Get more when next train arrives
final int interval = firstArrival.getMinSecondsLeft() * 1000;
// Get more 90 seconds before next train arrives, right when
// next train arrives, or 3 minutes, whichever is sooner
final long now = System.currentTimeMillis();
final long nextDepartureMillis = firstDeparture
.getMinSecondsLeft() * 1000L;
final int intervalUntilNextDeparture = (int) (nextDepartureMillis - now);
final int alternativeInterval = 3 * 60 * 1000;
int interval = intervalUntilNextDeparture;
if (intervalUntilNextDeparture > 95000
&& intervalUntilNextDeparture < alternativeInterval) {
interval = interval - 90 * 1000;
} else if (intervalUntilNextDeparture > alternativeInterval) {
interval = alternativeInterval;
}
mListTitleView.postDelayed(new Runnable() {
@Override
public void run() {
fetchLatestArrivals();
fetchLatestDepartures();
}
}, interval);
Log.i(Constants.TAG, "Scheduled another data fetch in "
@ -256,8 +276,8 @@ public class ViewArrivalsActivity extends ListActivity {
}
private void runAutoUpdate() {
if (mIsAutoUpdating && mArrivalsAdapter != null) {
mArrivalsAdapter.notifyDataSetChanged();
if (mIsAutoUpdating && mDeparturesAdapter != null) {
mDeparturesAdapter.notifyDataSetChanged();
}
if (hasWindowFocus()) {
mListTitleView.postDelayed(AUTO_UPDATE_RUNNABLE, 1000);
@ -285,7 +305,7 @@ public class ViewArrivalsActivity extends ListActivity {
+ mOrigin.abbreviation
+ "&dest="
+ mDestination.abbreviation)));
mFetchArrivalsOnNextFocus = true;
mFetchDeparturesOnNextFocus = true;
return true;
} else {
return super.onOptionsItemSelected(item);

View File

@ -6,12 +6,14 @@ import android.os.Parcelable;
import com.dougkeen.bart.Line;
import com.dougkeen.bart.Station;
public class Arrival implements Parcelable, Comparable<Arrival> {
public Arrival() {
public class Departure implements Parcelable, Comparable<Departure> {
private static final int MINIMUM_MERGE_OVERLAP_MILLIS = 10000;
public Departure() {
super();
}
public Arrival(String destinationAbbr, String destinationColor,
public Departure(String destinationAbbr, String destinationColor,
String platform, String direction, boolean bikeAllowed,
int trainLength, int minutes) {
super();
@ -24,7 +26,7 @@ public class Arrival implements Parcelable, Comparable<Arrival> {
this.minutes = minutes;
}
public Arrival(Parcel in) {
public Departure(Parcel in) {
readFromParcel(in);
}
@ -163,20 +165,30 @@ public class Arrival implements Parcelable, Comparable<Arrival> {
.currentTimeMillis()) / 1000);
}
public boolean hasArrived() {
public boolean hasDeparted() {
return getMinutes() == 0 || getMeanSecondsLeft() < 0;
}
public void calculateEstimates(long originalEstimateTime) {
setMinEstimate(originalEstimateTime + (getMinutes() * 60 * 1000));
setMaxEstimate(getMinEstimate() + (59 * 1000));
setMinEstimate(originalEstimateTime + (getMinutes() * 60 * 1000)
- (30000));
setMaxEstimate(getMinEstimate() + 60000);
}
public void mergeEstimate(Arrival arrival) {
public void mergeEstimate(Departure departure) {
if ((getMaxEstimate() - departure.getMinEstimate()) < MINIMUM_MERGE_OVERLAP_MILLIS
|| departure.getMaxEstimate() - getMinEstimate() < MINIMUM_MERGE_OVERLAP_MILLIS) {
// The estimate must have changed... just use the latest incoming
// values
setMinEstimate(departure.getMinEstimate());
setMaxEstimate(departure.getMaxEstimate());
return;
}
final long newMin = Math
.max(getMinEstimate(), arrival.getMinEstimate());
.max(getMinEstimate(), departure.getMinEstimate());
final long newMax = Math
.min(getMaxEstimate(), arrival.getMaxEstimate());
.min(getMaxEstimate(), departure.getMaxEstimate());
if (newMax > newMin) { // We can never have 0 or negative uncertainty
setMinEstimate(newMin);
setMaxEstimate(newMax);
@ -184,7 +196,7 @@ public class Arrival implements Parcelable, Comparable<Arrival> {
}
@Override
public int compareTo(Arrival another) {
public int compareTo(Departure another) {
return (this.getMinutes() > another.getMinutes()) ? 1 : (
(this.getMinutes() == another.getMinutes()) ? 0 : -1);
}
@ -197,7 +209,7 @@ public class Arrival implements Parcelable, Comparable<Arrival> {
return false;
if (getClass() != obj.getClass())
return false;
Arrival other = (Arrival) obj;
Departure other = (Departure) obj;
if (bikeAllowed != other.bikeAllowed)
return false;
if (destination != other.destination)
@ -227,8 +239,8 @@ public class Arrival implements Parcelable, Comparable<Arrival> {
public String getCountdownText() {
StringBuilder builder = new StringBuilder();
int secondsLeft = getMeanSecondsLeft();
if (hasArrived()) {
builder.append("Arrived");
if (hasDeparted()) {
builder.append("Departed");
} else {
builder.append(secondsLeft / 60);
builder.append("m, ");
@ -239,7 +251,7 @@ public class Arrival implements Parcelable, Comparable<Arrival> {
}
public String getUncertaintyText() {
if (hasArrived()) {
if (hasDeparted()) {
return "";
} else {
return "" + getUncertaintySeconds() + "s)";
@ -290,13 +302,13 @@ public class Arrival implements Parcelable, Comparable<Arrival> {
maxEstimate = in.readLong();
}
public static final Parcelable.Creator<Arrival> CREATOR = new Parcelable.Creator<Arrival>() {
public Arrival createFromParcel(Parcel in) {
return new Arrival(in);
public static final Parcelable.Creator<Departure> CREATOR = new Parcelable.Creator<Departure>() {
public Departure createFromParcel(Parcel in) {
return new Departure(in);
}
public Arrival[] newArray(int size) {
return new Arrival[size];
public Departure[] newArray(int size) {
return new Departure[size];
}
};
}

View File

@ -7,8 +7,8 @@ import java.util.List;
import com.dougkeen.bart.Route;
import com.dougkeen.bart.Station;
public class RealTimeArrivals {
public RealTimeArrivals(Station origin, Station destination,
public class RealTimeDepartures {
public RealTimeDepartures(Station origin, Station destination,
List<Route> routes) {
this.origin = origin;
this.destination = destination;
@ -19,7 +19,7 @@ public class RealTimeArrivals {
private Station destination;
private long time;
private List<Arrival> arrivals;
private List<Departure> departures;
private List<Route> routes;
@ -47,32 +47,35 @@ public class RealTimeArrivals {
this.time = time;
}
public List<Arrival> getArrivals() {
if (arrivals == null) {
arrivals = new ArrayList<Arrival>();
public List<Departure> getDepartures() {
if (departures == null) {
departures = new ArrayList<Departure>();
}
return arrivals;
return departures;
}
public void setArrivals(List<Arrival> arrivals) {
this.arrivals = arrivals;
public void setDepartures(List<Departure> departures) {
this.departures = departures;
}
public void addArrival(Arrival arrival) {
Station destination = Station.getByAbbreviation(arrival
public void addDeparture(Departure departure) {
Station destination = Station.getByAbbreviation(departure
.getDestinationAbbreviation());
if (departure.getLine() == null)
return;
for (Route route : routes) {
if (route.trainDestinationIsApplicable(destination, arrival.getLine())) {
arrival.setRequiresTransfer(route.hasTransfer());
getArrivals().add(arrival);
arrival.calculateEstimates(time);
if (route.trainDestinationIsApplicable(destination,
departure.getLine())) {
departure.setRequiresTransfer(route.hasTransfer());
getDepartures().add(departure);
departure.calculateEstimates(time);
return;
}
}
}
public void sortArrivals() {
Collections.sort(getArrivals());
public void sortDepartures() {
Collections.sort(getDepartures());
}
public List<Route> getRoutes() {
@ -86,14 +89,14 @@ public class RealTimeArrivals {
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("RealTimeArrivals [origin=");
builder.append("RealTimeDepartures [origin=");
builder.append(origin);
builder.append(", destination=");
builder.append(destination);
builder.append(", time=");
builder.append(time);
builder.append(", arrivals=");
builder.append(arrivals);
builder.append(", departures=");
builder.append(departures);
builder.append("]");
return builder.toString();
}