Merge with estarrival
This commit is contained in:
commit
5db3a5593b
@ -5,6 +5,7 @@
|
|||||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||||
<classpathentry kind="lib" path="libs/commons-io-2.0.1.jar"/>
|
<classpathentry kind="lib" path="libs/commons-io-2.0.1.jar"/>
|
||||||
<classpathentry kind="lib" path="libs/commons-lang3-3.1.jar"/>
|
<classpathentry kind="lib" path="libs/commons-lang3-3.1.jar"/>
|
||||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
<classpathentry kind="lib" path="libs/android-support-v4.jar"/>
|
||||||
|
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||||
<classpathentry kind="output" path="bin/classes"/>
|
<classpathentry kind="output" path="bin/classes"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
BIN
libs/android-support-v4.jar
Normal file
BIN
libs/android-support-v4.jar
Normal file
Binary file not shown.
@ -43,9 +43,10 @@
|
|||||||
android:layout_below="@id/topRow"
|
android:layout_below="@id/topRow"
|
||||||
android:src="@drawable/xfer" />
|
android:src="@drawable/xfer" />
|
||||||
|
|
||||||
<TextView
|
<TextSwitcher
|
||||||
android:id="@+id/trainLengthText"
|
android:id="@+id/trainLengthText"
|
||||||
style="@style/DepartureTrainLengthText"
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/topRow"
|
android:layout_below="@id/topRow"
|
||||||
android:layout_toRightOf="@id/destinationColorBar" />
|
android:layout_toRightOf="@id/destinationColorBar" />
|
||||||
|
|
||||||
|
3
res/layout/train_length_arrival_textview.xml
Normal file
3
res/layout/train_length_arrival_textview.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
style="@style/DepartureTrainLengthText" />
|
@ -1,6 +1,8 @@
|
|||||||
package com.dougkeen.bart;
|
package com.dougkeen.bart;
|
||||||
|
|
||||||
import com.dougkeen.bart.data.RoutesColumns;
|
import com.dougkeen.bart.data.RoutesColumns;
|
||||||
|
import com.dougkeen.bart.model.Constants;
|
||||||
|
import com.dougkeen.bart.model.Station;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
|
@ -2,21 +2,28 @@ package com.dougkeen.bart;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.drawable.GradientDrawable;
|
import android.graphics.drawable.GradientDrawable;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.animation.AnimationUtils;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
|
import android.widget.TextSwitcher;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.ViewSwitcher.ViewFactory;
|
||||||
|
|
||||||
import com.dougkeen.bart.data.Departure;
|
import com.dougkeen.bart.model.Departure;
|
||||||
|
|
||||||
public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
|
public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
|
||||||
|
|
||||||
|
public static int refreshCounter = 0;
|
||||||
|
|
||||||
public DepartureArrayAdapter(Context context, int textViewResourceId,
|
public DepartureArrayAdapter(Context context, int textViewResourceId,
|
||||||
Departure[] objects) {
|
Departure[] objects) {
|
||||||
super(context, textViewResourceId, objects);
|
super(context, textViewResourceId, objects);
|
||||||
@ -59,8 +66,30 @@ public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
|
|||||||
Departure departure = getItem(position);
|
Departure departure = getItem(position);
|
||||||
((TextView) view.findViewById(R.id.destinationText)).setText(departure
|
((TextView) view.findViewById(R.id.destinationText)).setText(departure
|
||||||
.getDestination().toString());
|
.getDestination().toString());
|
||||||
((TextView) view.findViewById(R.id.trainLengthText)).setText(departure
|
|
||||||
.getTrainLengthText());
|
TextSwitcher textSwitcher = (TextSwitcher) view
|
||||||
|
.findViewById(R.id.trainLengthText);
|
||||||
|
initTextSwitcher(textSwitcher);
|
||||||
|
|
||||||
|
final String estimatedArrivalTimeText = departure
|
||||||
|
.getEstimatedArrivalTimeText(getContext());
|
||||||
|
String arrivalText = "Est. arrival " + estimatedArrivalTimeText;
|
||||||
|
if (StringUtils.isBlank(estimatedArrivalTimeText)) {
|
||||||
|
textSwitcher.setCurrentText(departure.getTrainLengthText());
|
||||||
|
} else if (refreshCounter % 6 < 3) {
|
||||||
|
String trainLengthText = departure.getTrainLengthText();
|
||||||
|
if (refreshCounter % 6 == 0) {
|
||||||
|
textSwitcher.setText(trainLengthText);
|
||||||
|
} else {
|
||||||
|
textSwitcher.setCurrentText(trainLengthText);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (refreshCounter % 6 == 3) {
|
||||||
|
textSwitcher.setText(arrivalText);
|
||||||
|
} else {
|
||||||
|
textSwitcher.setCurrentText(arrivalText);
|
||||||
|
}
|
||||||
|
}
|
||||||
ImageView colorBar = (ImageView) view
|
ImageView colorBar = (ImageView) view
|
||||||
.findViewById(R.id.destinationColorBar);
|
.findViewById(R.id.destinationColorBar);
|
||||||
((GradientDrawable) colorBar.getDrawable()).setColor(Color
|
((GradientDrawable) colorBar.getDrawable()).setColor(Color
|
||||||
@ -87,4 +116,17 @@ public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
|
|||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initTextSwitcher(TextSwitcher textSwitcher) {
|
||||||
|
if (textSwitcher.getInAnimation() == null) {
|
||||||
|
textSwitcher.setFactory(new ViewFactory() {
|
||||||
|
public View makeView() {
|
||||||
|
return LayoutInflater.from(getContext()).inflate(
|
||||||
|
R.layout.train_length_arrival_textview, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
textSwitcher.setInAnimation(AnimationUtils.loadAnimation(
|
||||||
|
getContext(), android.R.anim.fade_in));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,9 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import com.dougkeen.bart.data.CursorUtils;
|
import com.dougkeen.bart.data.CursorUtils;
|
||||||
import com.dougkeen.bart.data.RoutesColumns;
|
import com.dougkeen.bart.data.RoutesColumns;
|
||||||
|
import com.dougkeen.bart.model.Constants;
|
||||||
|
import com.dougkeen.bart.model.Station;
|
||||||
|
import com.dougkeen.bart.networktasks.GetRouteFareTask;
|
||||||
|
|
||||||
public class RoutesListActivity extends ListActivity {
|
public class RoutesListActivity extends ListActivity {
|
||||||
private static final TimeZone PACIFIC_TIME = TimeZone
|
private static final TimeZone PACIFIC_TIME = TimeZone
|
||||||
@ -51,7 +54,9 @@ public class RoutesListActivity extends ListActivity {
|
|||||||
mQuery = managedQuery(Constants.FAVORITE_CONTENT_URI, new String[] {
|
mQuery = managedQuery(Constants.FAVORITE_CONTENT_URI, new String[] {
|
||||||
RoutesColumns._ID.string, RoutesColumns.FROM_STATION.string,
|
RoutesColumns._ID.string, RoutesColumns.FROM_STATION.string,
|
||||||
RoutesColumns.TO_STATION.string, RoutesColumns.FARE.string,
|
RoutesColumns.TO_STATION.string, RoutesColumns.FARE.string,
|
||||||
RoutesColumns.FARE_LAST_UPDATED.string }, null, null,
|
RoutesColumns.FARE_LAST_UPDATED.string,
|
||||||
|
RoutesColumns.AVERAGE_TRIP_SAMPLE_COUNT.string,
|
||||||
|
RoutesColumns.AVERAGE_TRIP_LENGTH.string }, null, null,
|
||||||
RoutesColumns._ID.string);
|
RoutesColumns._ID.string);
|
||||||
|
|
||||||
refreshFares();
|
refreshFares();
|
||||||
|
@ -3,6 +3,7 @@ package com.dougkeen.bart;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import android.app.ListActivity;
|
import android.app.ListActivity;
|
||||||
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
@ -22,10 +23,16 @@ import android.widget.ArrayAdapter;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.dougkeen.bart.GetRealTimeDeparturesTask.Params;
|
|
||||||
import com.dougkeen.bart.data.Departure;
|
|
||||||
import com.dougkeen.bart.data.RealTimeDepartures;
|
|
||||||
import com.dougkeen.bart.data.RoutesColumns;
|
import com.dougkeen.bart.data.RoutesColumns;
|
||||||
|
import com.dougkeen.bart.model.Constants;
|
||||||
|
import com.dougkeen.bart.model.Departure;
|
||||||
|
import com.dougkeen.bart.model.ScheduleInformation;
|
||||||
|
import com.dougkeen.bart.model.ScheduleItem;
|
||||||
|
import com.dougkeen.bart.model.StationPair;
|
||||||
|
import com.dougkeen.bart.model.RealTimeDepartures;
|
||||||
|
import com.dougkeen.bart.model.Station;
|
||||||
|
import com.dougkeen.bart.networktasks.GetRealTimeDeparturesTask;
|
||||||
|
import com.dougkeen.bart.networktasks.GetScheduleInformationTask;
|
||||||
|
|
||||||
public class ViewDeparturesActivity extends ListActivity {
|
public class ViewDeparturesActivity extends ListActivity {
|
||||||
|
|
||||||
@ -35,12 +42,17 @@ public class ViewDeparturesActivity extends ListActivity {
|
|||||||
|
|
||||||
private Station mOrigin;
|
private Station mOrigin;
|
||||||
private Station mDestination;
|
private Station mDestination;
|
||||||
|
private int mAverageTripLength;
|
||||||
|
private int mAverageTripSampleCount;
|
||||||
|
|
||||||
private ArrayAdapter<Departure> mDeparturesAdapter;
|
private ArrayAdapter<Departure> mDeparturesAdapter;
|
||||||
|
|
||||||
|
private ScheduleInformation mLatestScheduleInfo;
|
||||||
|
|
||||||
private TextView mListTitleView;
|
private TextView mListTitleView;
|
||||||
|
|
||||||
private AsyncTask<Params, Integer, RealTimeDepartures> mGetDeparturesTask;
|
private AsyncTask<StationPair, Integer, RealTimeDepartures> mGetDeparturesTask;
|
||||||
|
private AsyncTask<StationPair, Integer, ScheduleInformation> mGetScheduleInformationTask;
|
||||||
|
|
||||||
private boolean mIsAutoUpdating = false;
|
private boolean mIsAutoUpdating = false;
|
||||||
|
|
||||||
@ -52,7 +64,8 @@ public class ViewDeparturesActivity extends ListActivity {
|
|||||||
|
|
||||||
private PowerManager.WakeLock mWakeLock;
|
private PowerManager.WakeLock mWakeLock;
|
||||||
|
|
||||||
private boolean mDataFetchIsPending;
|
private boolean mDepartureFetchIsPending;
|
||||||
|
private boolean mScheduleFetchIsPending;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@ -69,13 +82,18 @@ public class ViewDeparturesActivity extends ListActivity {
|
|||||||
|
|
||||||
Cursor cursor = managedQuery(mUri, new String[] {
|
Cursor cursor = managedQuery(mUri, new String[] {
|
||||||
RoutesColumns.FROM_STATION.string,
|
RoutesColumns.FROM_STATION.string,
|
||||||
RoutesColumns.TO_STATION.string }, null, null, null);
|
RoutesColumns.TO_STATION.string,
|
||||||
|
RoutesColumns.AVERAGE_TRIP_LENGTH.string,
|
||||||
|
RoutesColumns.AVERAGE_TRIP_SAMPLE_COUNT.string }, null, null,
|
||||||
|
null);
|
||||||
|
|
||||||
if (!cursor.moveToFirst()) {
|
if (!cursor.moveToFirst()) {
|
||||||
throw new IllegalStateException("URI not found: " + mUri.toString());
|
throw new IllegalStateException("URI not found: " + mUri.toString());
|
||||||
}
|
}
|
||||||
mOrigin = Station.getByAbbreviation(cursor.getString(0));
|
mOrigin = Station.getByAbbreviation(cursor.getString(0));
|
||||||
mDestination = Station.getByAbbreviation(cursor.getString(1));
|
mDestination = Station.getByAbbreviation(cursor.getString(1));
|
||||||
|
mAverageTripLength = cursor.getInt(2);
|
||||||
|
mAverageTripSampleCount = cursor.getInt(3);
|
||||||
|
|
||||||
String header = "Departures:\n" + mOrigin.name + " to "
|
String header = "Departures:\n" + mOrigin.name + " to "
|
||||||
+ mDestination.name;
|
+ mDestination.name;
|
||||||
@ -114,7 +132,11 @@ public class ViewDeparturesActivity extends ListActivity {
|
|||||||
private void cancelDataFetch() {
|
private void cancelDataFetch() {
|
||||||
if (mGetDeparturesTask != null) {
|
if (mGetDeparturesTask != null) {
|
||||||
mGetDeparturesTask.cancel(true);
|
mGetDeparturesTask.cancel(true);
|
||||||
mDataFetchIsPending = false;
|
mDepartureFetchIsPending = false;
|
||||||
|
}
|
||||||
|
if (mGetScheduleInformationTask != null) {
|
||||||
|
mGetScheduleInformationTask.cancel(true);
|
||||||
|
mScheduleFetchIsPending = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +154,7 @@ public class ViewDeparturesActivity extends ListActivity {
|
|||||||
public void onWindowFocusChanged(boolean hasFocus) {
|
public void onWindowFocusChanged(boolean hasFocus) {
|
||||||
super.onWindowFocusChanged(hasFocus);
|
super.onWindowFocusChanged(hasFocus);
|
||||||
if (hasFocus) {
|
if (hasFocus) {
|
||||||
if (!mDataFetchIsPending) {
|
if (!mDepartureFetchIsPending) {
|
||||||
fetchLatestDepartures();
|
fetchLatestDepartures();
|
||||||
}
|
}
|
||||||
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||||
@ -162,7 +184,7 @@ public class ViewDeparturesActivity extends ListActivity {
|
|||||||
mGetDeparturesTask = new GetRealTimeDeparturesTask() {
|
mGetDeparturesTask = new GetRealTimeDeparturesTask() {
|
||||||
@Override
|
@Override
|
||||||
public void onResult(RealTimeDepartures result) {
|
public void onResult(RealTimeDepartures result) {
|
||||||
mDataFetchIsPending = false;
|
mDepartureFetchIsPending = false;
|
||||||
Log.i(Constants.TAG, "Processing data from server");
|
Log.i(Constants.TAG, "Processing data from server");
|
||||||
processLatestDepartures(result);
|
processLatestDepartures(result);
|
||||||
Log.i(Constants.TAG, "Done processing data from server");
|
Log.i(Constants.TAG, "Done processing data from server");
|
||||||
@ -170,19 +192,55 @@ public class ViewDeparturesActivity extends ListActivity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(Exception e) {
|
public void onError(Exception e) {
|
||||||
mDataFetchIsPending = false;
|
mDepartureFetchIsPending = false;
|
||||||
Log.w(Constants.TAG, e.getMessage(), e);
|
Log.w(Constants.TAG, e.getMessage(), e);
|
||||||
Toast.makeText(ViewDeparturesActivity.this,
|
Toast.makeText(ViewDeparturesActivity.this,
|
||||||
R.string.could_not_connect, Toast.LENGTH_LONG).show();
|
R.string.could_not_connect, Toast.LENGTH_LONG).show();
|
||||||
((TextView) findViewById(android.R.id.empty))
|
((TextView) findViewById(android.R.id.empty))
|
||||||
.setText(R.string.could_not_connect);
|
.setText(R.string.could_not_connect);
|
||||||
// Try again in 60s
|
// Try again in 60s
|
||||||
scheduleDataFetch(60000);
|
scheduleDepartureFetch(60000);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Log.i(Constants.TAG, "Fetching data from server");
|
Log.i(Constants.TAG, "Fetching data from server");
|
||||||
mGetDeparturesTask.execute(new GetRealTimeDeparturesTask.Params(
|
mGetDeparturesTask.execute(new StationPair(mOrigin, mDestination));
|
||||||
mOrigin, mDestination));
|
}
|
||||||
|
|
||||||
|
private void fetchLatestSchedule() {
|
||||||
|
if (!hasWindowFocus())
|
||||||
|
return;
|
||||||
|
if (mGetScheduleInformationTask != null
|
||||||
|
&& mGetScheduleInformationTask.getStatus().equals(
|
||||||
|
AsyncTask.Status.RUNNING)) {
|
||||||
|
// Don't overlap fetches
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mGetScheduleInformationTask = new GetScheduleInformationTask() {
|
||||||
|
@Override
|
||||||
|
public void onResult(ScheduleInformation result) {
|
||||||
|
mScheduleFetchIsPending = false;
|
||||||
|
Log.i(Constants.TAG, "Processing data from server");
|
||||||
|
mLatestScheduleInfo = result;
|
||||||
|
applyScheduleInformation();
|
||||||
|
Log.i(Constants.TAG, "Done processing data from server");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Exception e) {
|
||||||
|
mScheduleFetchIsPending = false;
|
||||||
|
Log.w(Constants.TAG, e.getMessage(), e);
|
||||||
|
Toast.makeText(ViewDeparturesActivity.this,
|
||||||
|
R.string.could_not_connect, Toast.LENGTH_LONG).show();
|
||||||
|
((TextView) findViewById(android.R.id.empty))
|
||||||
|
.setText(R.string.could_not_connect);
|
||||||
|
// Try again in 60s
|
||||||
|
scheduleScheduleInfoFetch(60000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Log.i(Constants.TAG, "Fetching data from server");
|
||||||
|
mGetScheduleInformationTask.execute(new StationPair(mOrigin,
|
||||||
|
mDestination));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processLatestDepartures(RealTimeDepartures result) {
|
protected void processLatestDepartures(RealTimeDepartures result) {
|
||||||
@ -238,11 +296,12 @@ public class ViewDeparturesActivity extends ListActivity {
|
|||||||
needsBetterAccuracy = true;
|
needsBetterAccuracy = true;
|
||||||
}
|
}
|
||||||
mDeparturesAdapter.notifyDataSetChanged();
|
mDeparturesAdapter.notifyDataSetChanged();
|
||||||
|
requestScheduleIfNecessary();
|
||||||
|
|
||||||
if (hasWindowFocus() && firstDeparture != null) {
|
if (hasWindowFocus() && firstDeparture != null) {
|
||||||
if (needsBetterAccuracy || firstDeparture.hasDeparted()) {
|
if (needsBetterAccuracy || firstDeparture.hasDeparted()) {
|
||||||
// Get more data in 20s
|
// Get more data in 20s
|
||||||
scheduleDataFetch(20000);
|
scheduleDepartureFetch(20000);
|
||||||
} else {
|
} else {
|
||||||
// Get more 90 seconds before next train arrives, right when
|
// Get more 90 seconds before next train arrives, right when
|
||||||
// next train arrives, or 3 minutes, whichever is sooner
|
// next train arrives, or 3 minutes, whichever is sooner
|
||||||
@ -262,7 +321,7 @@ public class ViewDeparturesActivity extends ListActivity {
|
|||||||
interval = 20000;
|
interval = 20000;
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduleDataFetch(interval);
|
scheduleDepartureFetch(interval);
|
||||||
}
|
}
|
||||||
if (!mIsAutoUpdating) {
|
if (!mIsAutoUpdating) {
|
||||||
mIsAutoUpdating = true;
|
mIsAutoUpdating = true;
|
||||||
@ -272,21 +331,106 @@ public class ViewDeparturesActivity extends ListActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleDataFetch(int millisUntilExecute) {
|
private void requestScheduleIfNecessary() {
|
||||||
if (!mDataFetchIsPending) {
|
if (mDeparturesAdapter.getCount() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mLatestScheduleInfo == null) {
|
||||||
|
fetchLatestSchedule();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Departure lastDeparture = mDeparturesAdapter.getItem(mDeparturesAdapter
|
||||||
|
.getCount() - 1);
|
||||||
|
if (mLatestScheduleInfo.getLatestDepartureTime() < lastDeparture
|
||||||
|
.getMeanEstimate()) {
|
||||||
|
fetchLatestSchedule();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyScheduleInformation() {
|
||||||
|
int localAverageLength = mLatestScheduleInfo.getAverageTripLength();
|
||||||
|
|
||||||
|
int departuresCount = mDeparturesAdapter.getCount();
|
||||||
|
int lastSearchIndex = 0;
|
||||||
|
int tripCount = mLatestScheduleInfo.getTrips().size();
|
||||||
|
boolean departureUpdated = false;
|
||||||
|
for (int departureIndex = 0; departureIndex < departuresCount; departureIndex++) {
|
||||||
|
Departure departure = mDeparturesAdapter.getItem(departureIndex);
|
||||||
|
for (int i = lastSearchIndex; i < tripCount; i++) {
|
||||||
|
ScheduleItem trip = mLatestScheduleInfo.getTrips().get(i);
|
||||||
|
long departTimeDiff = Math.abs(trip.getDepartureTime()
|
||||||
|
- departure.getMeanEstimate());
|
||||||
|
if (departTimeDiff <= (60000 + departure
|
||||||
|
.getUncertaintySeconds() * 1000)
|
||||||
|
&& departure.getEstimatedTripTime() != trip
|
||||||
|
.getTripLength()) {
|
||||||
|
departure.setEstimatedTripTime(trip.getTripLength());
|
||||||
|
lastSearchIndex = i;
|
||||||
|
departureUpdated = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!departure.hasEstimatedTripTime() && localAverageLength > 0) {
|
||||||
|
departure.setEstimatedTripTime(localAverageLength);
|
||||||
|
} else if (!departure.hasEstimatedTripTime()) {
|
||||||
|
departure.setEstimatedTripTime(mAverageTripLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (departureUpdated) {
|
||||||
|
mDeparturesAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update global average
|
||||||
|
if (mLatestScheduleInfo.getTripCountForAverage() > 0) {
|
||||||
|
int newAverageSampleCount = mAverageTripSampleCount
|
||||||
|
+ mLatestScheduleInfo.getTripCountForAverage();
|
||||||
|
int newAverage = (mAverageTripLength * mAverageTripSampleCount + localAverageLength
|
||||||
|
* mLatestScheduleInfo.getTripCountForAverage())
|
||||||
|
/ newAverageSampleCount;
|
||||||
|
|
||||||
|
ContentValues contentValues = new ContentValues();
|
||||||
|
contentValues.put(RoutesColumns.AVERAGE_TRIP_LENGTH.string,
|
||||||
|
newAverage);
|
||||||
|
contentValues.put(RoutesColumns.AVERAGE_TRIP_SAMPLE_COUNT.string,
|
||||||
|
newAverageSampleCount);
|
||||||
|
|
||||||
|
getContentResolver().update(mUri, contentValues, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scheduleDepartureFetch(int millisUntilExecute) {
|
||||||
|
if (!mDepartureFetchIsPending) {
|
||||||
mListTitleView.postDelayed(new Runnable() {
|
mListTitleView.postDelayed(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
fetchLatestDepartures();
|
fetchLatestDepartures();
|
||||||
}
|
}
|
||||||
}, millisUntilExecute);
|
}, millisUntilExecute);
|
||||||
mDataFetchIsPending = true;
|
mDepartureFetchIsPending = true;
|
||||||
Log.i(Constants.TAG, "Scheduled another data fetch in "
|
Log.i(Constants.TAG, "Scheduled another departure fetch in "
|
||||||
|
+ millisUntilExecute / 1000 + "s");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scheduleScheduleInfoFetch(int millisUntilExecute) {
|
||||||
|
if (!mScheduleFetchIsPending) {
|
||||||
|
mListTitleView.postDelayed(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
fetchLatestSchedule();
|
||||||
|
}
|
||||||
|
}, millisUntilExecute);
|
||||||
|
mScheduleFetchIsPending = true;
|
||||||
|
Log.i(Constants.TAG, "Scheduled another schedule fetch in "
|
||||||
+ millisUntilExecute / 1000 + "s");
|
+ millisUntilExecute / 1000 + "s");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void runAutoUpdate() {
|
private void runAutoUpdate() {
|
||||||
if (mIsAutoUpdating && mDeparturesAdapter != null) {
|
if (mIsAutoUpdating && mDeparturesAdapter != null) {
|
||||||
|
DepartureArrayAdapter.refreshCounter++;
|
||||||
mDeparturesAdapter.notifyDataSetChanged();
|
mDeparturesAdapter.notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
if (hasWindowFocus()) {
|
if (hasWindowFocus()) {
|
||||||
|
@ -2,7 +2,7 @@ package com.dougkeen.bart.data;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
import com.dougkeen.bart.Constants;
|
import com.dougkeen.bart.model.Constants;
|
||||||
|
|
||||||
import android.content.ContentProvider;
|
import android.content.ContentProvider;
|
||||||
import android.content.ContentUris;
|
import android.content.ContentUris;
|
||||||
@ -45,6 +45,10 @@ public class BartContentProvider extends ContentProvider {
|
|||||||
RoutesColumns.FARE.string);
|
RoutesColumns.FARE.string);
|
||||||
sFavoritesProjectionMap.put(RoutesColumns.FARE_LAST_UPDATED.string,
|
sFavoritesProjectionMap.put(RoutesColumns.FARE_LAST_UPDATED.string,
|
||||||
RoutesColumns.FARE_LAST_UPDATED.string);
|
RoutesColumns.FARE_LAST_UPDATED.string);
|
||||||
|
sFavoritesProjectionMap.put(RoutesColumns.AVERAGE_TRIP_SAMPLE_COUNT.string,
|
||||||
|
RoutesColumns.AVERAGE_TRIP_SAMPLE_COUNT.string);
|
||||||
|
sFavoritesProjectionMap.put(RoutesColumns.AVERAGE_TRIP_LENGTH.string,
|
||||||
|
RoutesColumns.AVERAGE_TRIP_LENGTH.string);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DatabaseHelper mDatabaseHelper;
|
private DatabaseHelper mDatabaseHelper;
|
||||||
|
@ -15,7 +15,7 @@ import android.util.Log;
|
|||||||
public class DatabaseHelper extends SQLiteOpenHelper {
|
public class DatabaseHelper extends SQLiteOpenHelper {
|
||||||
|
|
||||||
private static final String DATABASE_NAME = "bart.dougkeen.db";
|
private static final String DATABASE_NAME = "bart.dougkeen.db";
|
||||||
private static final int DATABASE_VERSION = 2;
|
private static final int DATABASE_VERSION = 4;
|
||||||
|
|
||||||
public static final String FAVORITES_TABLE_NAME = "Favorites";
|
public static final String FAVORITES_TABLE_NAME = "Favorites";
|
||||||
|
|
||||||
@ -34,7 +34,9 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||||||
+ RoutesColumns.FROM_STATION.getColumnDef() + ", "
|
+ RoutesColumns.FROM_STATION.getColumnDef() + ", "
|
||||||
+ RoutesColumns.TO_STATION.getColumnDef() + ", "
|
+ RoutesColumns.TO_STATION.getColumnDef() + ", "
|
||||||
+ RoutesColumns.FARE.getColumnDef() + ", "
|
+ RoutesColumns.FARE.getColumnDef() + ", "
|
||||||
+ RoutesColumns.FARE_LAST_UPDATED.getColumnDef() + ");");
|
+ RoutesColumns.FARE_LAST_UPDATED.getColumnDef() + ", "
|
||||||
|
+ RoutesColumns.AVERAGE_TRIP_SAMPLE_COUNT.getColumnDef() + ", "
|
||||||
|
+ RoutesColumns.AVERAGE_TRIP_LENGTH.getColumnDef() + ");");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -5,7 +5,9 @@ public enum RoutesColumns {
|
|||||||
FROM_STATION("FROM_STATION", "TEXT", false),
|
FROM_STATION("FROM_STATION", "TEXT", false),
|
||||||
TO_STATION("TO_STATION", "TEXT", false),
|
TO_STATION("TO_STATION", "TEXT", false),
|
||||||
FARE("FARE", "TEXT", true),
|
FARE("FARE", "TEXT", true),
|
||||||
FARE_LAST_UPDATED("FARE_LAST_UPDATED", "INTEGER", true);
|
FARE_LAST_UPDATED("FARE_LAST_UPDATED", "INTEGER", true),
|
||||||
|
AVERAGE_TRIP_SAMPLE_COUNT("AVE_TRIP_SAMPLE_COUNT", "INTEGER", true),
|
||||||
|
AVERAGE_TRIP_LENGTH("AVE_TRIP_LENGTH", "INTEGER", true);
|
||||||
|
|
||||||
|
|
||||||
// This class cannot be instantiated
|
// This class cannot be instantiated
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package com.dougkeen.bart;
|
package com.dougkeen.bart.model;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
@ -10,6 +10,6 @@ public class Constants {
|
|||||||
+ AUTHORITY + "/favorites");
|
+ AUTHORITY + "/favorites");
|
||||||
public static final String MAP_URL = "http://m.bart.gov/images/global/system-map29.gif";
|
public static final String MAP_URL = "http://m.bart.gov/images/global/system-map29.gif";
|
||||||
|
|
||||||
public static final String TAG = "BartCatcher";
|
public static final String TAG = "BartRunner";
|
||||||
public static final String API_KEY = "5LD9-IAYI-TRAT-MHHW";
|
public static final String API_KEY = "5LD9-IAYI-TRAT-MHHW";
|
||||||
}
|
}
|
@ -1,13 +1,15 @@
|
|||||||
package com.dougkeen.bart.data;
|
package com.dougkeen.bart.model;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
import android.text.format.DateFormat;
|
||||||
import com.dougkeen.bart.Line;
|
|
||||||
import com.dougkeen.bart.Station;
|
|
||||||
|
|
||||||
public class Departure implements Parcelable, Comparable<Departure> {
|
public class Departure implements Parcelable, Comparable<Departure> {
|
||||||
private static final int ESTIMATE_EQUALS_TOLERANCE_MILLIS = 59999;
|
private static final int ESTIMATE_EQUALS_TOLERANCE_MILLIS = 59999;
|
||||||
|
private static final int ESTIMATE_EQUALS_TOLERANCE_LONG_LINGER_MILLIS = 719999;
|
||||||
private static final int MINIMUM_MERGE_OVERLAP_MILLIS = 10000;
|
private static final int MINIMUM_MERGE_OVERLAP_MILLIS = 10000;
|
||||||
|
|
||||||
public Departure() {
|
public Departure() {
|
||||||
@ -31,6 +33,7 @@ public class Departure implements Parcelable, Comparable<Departure> {
|
|||||||
readFromParcel(in);
|
readFromParcel(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Station origin;
|
||||||
private Station destination;
|
private Station destination;
|
||||||
private Line line;
|
private Line line;
|
||||||
private String destinationColor;
|
private String destinationColor;
|
||||||
@ -45,6 +48,20 @@ public class Departure implements Parcelable, Comparable<Departure> {
|
|||||||
private long minEstimate;
|
private long minEstimate;
|
||||||
private long maxEstimate;
|
private long maxEstimate;
|
||||||
|
|
||||||
|
private int estimatedTripTime;
|
||||||
|
|
||||||
|
private boolean beganAsDeparted;
|
||||||
|
|
||||||
|
private long arrivalTimeOverride;
|
||||||
|
|
||||||
|
public Station getOrigin() {
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrigin(Station origin) {
|
||||||
|
this.origin = origin;
|
||||||
|
}
|
||||||
|
|
||||||
public Station getDestination() {
|
public Station getDestination() {
|
||||||
return destination;
|
return destination;
|
||||||
}
|
}
|
||||||
@ -131,6 +148,9 @@ public class Departure implements Parcelable, Comparable<Departure> {
|
|||||||
|
|
||||||
public void setMinutes(int minutes) {
|
public void setMinutes(int minutes) {
|
||||||
this.minutes = minutes;
|
this.minutes = minutes;
|
||||||
|
if (minutes == 0) {
|
||||||
|
beganAsDeparted = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMinEstimate() {
|
public long getMinEstimate() {
|
||||||
@ -149,6 +169,18 @@ public class Departure implements Parcelable, Comparable<Departure> {
|
|||||||
this.maxEstimate = maxEstimate;
|
this.maxEstimate = maxEstimate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getEstimatedTripTime() {
|
||||||
|
return estimatedTripTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEstimatedTripTime(int estimatedTripTime) {
|
||||||
|
this.estimatedTripTime = estimatedTripTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasEstimatedTripTime() {
|
||||||
|
return this.estimatedTripTime > 0;
|
||||||
|
}
|
||||||
|
|
||||||
public int getUncertaintySeconds() {
|
public int getUncertaintySeconds() {
|
||||||
return (int) (maxEstimate - minEstimate + 1000) / 2000;
|
return (int) (maxEstimate - minEstimate + 1000) / 2000;
|
||||||
}
|
}
|
||||||
@ -162,8 +194,27 @@ public class Departure implements Parcelable, Comparable<Departure> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getMeanSecondsLeft() {
|
public int getMeanSecondsLeft() {
|
||||||
return (int) (((getMinEstimate() + getMaxEstimate()) / 2 - System
|
return (int) ((getMeanEstimate() - System.currentTimeMillis()) / 1000);
|
||||||
.currentTimeMillis()) / 1000);
|
}
|
||||||
|
|
||||||
|
public long getMeanEstimate() {
|
||||||
|
return (getMinEstimate() + getMaxEstimate()) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getEstimatedArrivalTime() {
|
||||||
|
if (arrivalTimeOverride > 0) {
|
||||||
|
return arrivalTimeOverride;
|
||||||
|
}
|
||||||
|
return getMeanEstimate() + getEstimatedTripTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEstimatedArrivalTimeText(Context context) {
|
||||||
|
if (getEstimatedTripTime() > 0) {
|
||||||
|
return DateFormat.getTimeFormat(context).format(
|
||||||
|
new Date(getEstimatedArrivalTime()));
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasDeparted() {
|
public boolean hasDeparted() {
|
||||||
@ -177,6 +228,13 @@ public class Departure implements Parcelable, Comparable<Departure> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void mergeEstimate(Departure departure) {
|
public void mergeEstimate(Departure departure) {
|
||||||
|
if (departure.hasDeparted() && origin.longStationLinger
|
||||||
|
&& getMinEstimate() > 0 && !beganAsDeparted) {
|
||||||
|
// This is probably not a true departure, but an indication that
|
||||||
|
// the train is in the station. Don't update the estimates.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ((getMaxEstimate() - departure.getMinEstimate()) < MINIMUM_MERGE_OVERLAP_MILLIS
|
if ((getMaxEstimate() - departure.getMinEstimate()) < MINIMUM_MERGE_OVERLAP_MILLIS
|
||||||
|| departure.getMaxEstimate() - getMinEstimate() < MINIMUM_MERGE_OVERLAP_MILLIS) {
|
|| departure.getMaxEstimate() - getMinEstimate() < MINIMUM_MERGE_OVERLAP_MILLIS) {
|
||||||
// The estimate must have changed... just use the latest incoming
|
// The estimate must have changed... just use the latest incoming
|
||||||
@ -250,7 +308,7 @@ public class Departure implements Parcelable, Comparable<Departure> {
|
|||||||
return false;
|
return false;
|
||||||
if (line != other.line)
|
if (line != other.line)
|
||||||
return false;
|
return false;
|
||||||
if (Math.abs(maxEstimate - other.maxEstimate) > ESTIMATE_EQUALS_TOLERANCE_MILLIS)
|
if (Math.abs(maxEstimate - other.maxEstimate) > getEqualsTolerance())
|
||||||
return false;
|
return false;
|
||||||
if (platform == null) {
|
if (platform == null) {
|
||||||
if (other.platform != null)
|
if (other.platform != null)
|
||||||
@ -267,11 +325,23 @@ public class Departure implements Parcelable, Comparable<Departure> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getEqualsTolerance() {
|
||||||
|
if (origin.longStationLinger && hasDeparted()) {
|
||||||
|
return ESTIMATE_EQUALS_TOLERANCE_LONG_LINGER_MILLIS;
|
||||||
|
} else {
|
||||||
|
return ESTIMATE_EQUALS_TOLERANCE_MILLIS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public String getCountdownText() {
|
public String getCountdownText() {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
int secondsLeft = getMeanSecondsLeft();
|
int secondsLeft = getMeanSecondsLeft();
|
||||||
if (hasDeparted()) {
|
if (hasDeparted()) {
|
||||||
|
if (origin.longStationLinger && beganAsDeparted) {
|
||||||
|
builder.append("At station");
|
||||||
|
} else {
|
||||||
builder.append("Departed");
|
builder.append("Departed");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
builder.append(secondsLeft / 60);
|
builder.append(secondsLeft / 60);
|
||||||
builder.append("m, ");
|
builder.append("m, ");
|
@ -1,4 +1,4 @@
|
|||||||
package com.dougkeen.bart;
|
package com.dougkeen.bart.model;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -37,6 +37,12 @@ public enum Line {
|
|||||||
Station.COLS, Station.SANL, Station.BAYF, Station.HAYW,
|
Station.COLS, Station.SANL, Station.BAYF, Station.HAYW,
|
||||||
Station.SHAY, Station.UCTY, Station.FRMT),
|
Station.SHAY, Station.UCTY, Station.FRMT),
|
||||||
YELLOW_ORANGE_SCHEDULED_TRANSFER(YELLOW, ORANGE, Station.MLBR,
|
YELLOW_ORANGE_SCHEDULED_TRANSFER(YELLOW, ORANGE, Station.MLBR,
|
||||||
|
Station.SFIA, Station.SBRN, Station.SSAN, Station.COLM,
|
||||||
|
Station.DALY, Station.BALB, Station.GLEN, Station._24TH,
|
||||||
|
Station._16TH, Station.CIVC, Station.POWL, Station.MONT,
|
||||||
|
Station.EMBR, Station.WOAK, Station.ASHB, Station.DBRK,
|
||||||
|
Station.NBRK, Station.PLZA, Station.DELN, Station.RICH),
|
||||||
|
YELLOW_RED_SCHEDULED_TRANSFER(YELLOW, RED, Station.MLBR,
|
||||||
Station.SFIA, Station.SBRN, Station.SSAN, Station.COLM,
|
Station.SFIA, Station.SBRN, Station.SSAN, Station.COLM,
|
||||||
Station.DALY, Station.BALB, Station.GLEN, Station._24TH,
|
Station.DALY, Station.BALB, Station.GLEN, Station._24TH,
|
||||||
Station._16TH, Station.CIVC, Station.POWL, Station.MONT,
|
Station._16TH, Station.CIVC, Station.POWL, Station.MONT,
|
@ -1,11 +1,9 @@
|
|||||||
package com.dougkeen.bart.data;
|
package com.dougkeen.bart.model;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.dougkeen.bart.Route;
|
|
||||||
import com.dougkeen.bart.Station;
|
|
||||||
|
|
||||||
public class RealTimeDepartures {
|
public class RealTimeDepartures {
|
||||||
public RealTimeDepartures(Station origin, Station destination,
|
public RealTimeDepartures(Station origin, Station destination,
|
@ -1,4 +1,4 @@
|
|||||||
package com.dougkeen.bart;
|
package com.dougkeen.bart.model;
|
||||||
|
|
||||||
public class Route {
|
public class Route {
|
||||||
private Station origin;
|
private Station origin;
|
91
src/com/dougkeen/bart/model/ScheduleInformation.java
Normal file
91
src/com/dougkeen/bart/model/ScheduleInformation.java
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package com.dougkeen.bart.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import android.R.integer;
|
||||||
|
|
||||||
|
public class ScheduleInformation {
|
||||||
|
|
||||||
|
public ScheduleInformation(Station origin, Station destination) {
|
||||||
|
super();
|
||||||
|
this.origin = origin;
|
||||||
|
this.destination = destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Station origin;
|
||||||
|
private Station destination;
|
||||||
|
private long date;
|
||||||
|
private List<ScheduleItem> trips;
|
||||||
|
|
||||||
|
public Station getOrigin() {
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrigin(Station origin) {
|
||||||
|
this.origin = origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Station getDestination() {
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDestination(Station destination) {
|
||||||
|
this.destination = destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getDate() {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDate(long date) {
|
||||||
|
this.date = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ScheduleItem> getTrips() {
|
||||||
|
if (trips == null) {
|
||||||
|
trips = new ArrayList<ScheduleItem>();
|
||||||
|
}
|
||||||
|
return trips;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTrips(List<ScheduleItem> trips) {
|
||||||
|
this.trips = trips;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTrip(ScheduleItem trip) {
|
||||||
|
getTrips().add(trip);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLatestDepartureTime() {
|
||||||
|
if (getTrips().isEmpty())
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return getTrips().get(getTrips().size() - 1).getDepartureTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int aveTripLength = -1;
|
||||||
|
private int tripCount = 0;
|
||||||
|
|
||||||
|
public int getAverageTripLength() {
|
||||||
|
if (aveTripLength < 0) {
|
||||||
|
int sum = 0;
|
||||||
|
for (ScheduleItem trip : getTrips()) {
|
||||||
|
int tripLength = trip.getTripLength();
|
||||||
|
if (tripLength > 0) {
|
||||||
|
sum += tripLength;
|
||||||
|
tripCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tripCount > 0) {
|
||||||
|
aveTripLength = sum / tripCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return aveTripLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTripCountForAverage() {
|
||||||
|
getAverageTripLength();
|
||||||
|
return tripCount;
|
||||||
|
}
|
||||||
|
}
|
86
src/com/dougkeen/bart/model/ScheduleItem.java
Normal file
86
src/com/dougkeen/bart/model/ScheduleItem.java
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package com.dougkeen.bart.model;
|
||||||
|
|
||||||
|
public class ScheduleItem {
|
||||||
|
|
||||||
|
public ScheduleItem() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScheduleItem(Station origin, Station destination) {
|
||||||
|
super();
|
||||||
|
this.origin = origin;
|
||||||
|
this.destination = destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Station origin;
|
||||||
|
private Station destination;
|
||||||
|
private String fare;
|
||||||
|
private long departureTime;
|
||||||
|
private long arrivalTime;
|
||||||
|
private boolean bikesAllowed;
|
||||||
|
private String trainHeadStation;
|
||||||
|
|
||||||
|
public Station getOrigin() {
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrigin(Station origin) {
|
||||||
|
this.origin = origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Station getDestination() {
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDestination(Station destination) {
|
||||||
|
this.destination = destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFare() {
|
||||||
|
return fare;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFare(String fare) {
|
||||||
|
this.fare = fare;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getDepartureTime() {
|
||||||
|
return departureTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDepartureTime(long departureTime) {
|
||||||
|
this.departureTime = departureTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getArrivalTime() {
|
||||||
|
return arrivalTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setArrivalTime(long arrivalTime) {
|
||||||
|
this.arrivalTime = arrivalTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTripLength() {
|
||||||
|
if (departureTime <= 0 || arrivalTime <= 0) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return (int) (arrivalTime - departureTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBikesAllowed() {
|
||||||
|
return bikesAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBikesAllowed(boolean bikesAllowed) {
|
||||||
|
this.bikesAllowed = bikesAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTrainHeadStation() {
|
||||||
|
return trainHeadStation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTrainHeadStation(String trainHeadStation) {
|
||||||
|
this.trainHeadStation = trainHeadStation;
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,10 @@
|
|||||||
package com.dougkeen.bart;
|
package com.dougkeen.bart.model;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
public enum Station {
|
public enum Station {
|
||||||
_12TH("12th", "12th St./Oakland City Center", false, false, "bayf"),
|
_12TH("12th", "12th St./Oakland City Center", false, false, "bayf"),
|
||||||
_16TH("16th", "16th St. Mission", false, false),
|
_16TH("16th", "16th St. Mission", false, false),
|
||||||
@ -41,7 +43,7 @@ public enum Station {
|
|||||||
ROCK("rock", "Rockridge", false, false, "mcar"),
|
ROCK("rock", "Rockridge", false, false, "mcar"),
|
||||||
SBRN("sbrn", "San Bruno", false, false, "balb", "balb"),
|
SBRN("sbrn", "San Bruno", false, false, "balb", "balb"),
|
||||||
SANL("sanl", "San Leandro", true, false, "mcar"),
|
SANL("sanl", "San Leandro", true, false, "mcar"),
|
||||||
SFIA("sfia", "SFO Airport", false, false, "sbrn", "balb"),
|
SFIA("sfia", "SFO Airport", false, false, "sbrn", "balb", true),
|
||||||
SHAY("shay", "South Hayward", true, false, "bayf"),
|
SHAY("shay", "South Hayward", true, false, "bayf"),
|
||||||
SSAN("ssan", "South San Francisco", false, false, "balb", "balb"),
|
SSAN("ssan", "South San Francisco", false, false, "balb", "balb"),
|
||||||
UCTY("ucty", "Union City", true, false, "bayf"),
|
UCTY("ucty", "Union City", true, false, "bayf"),
|
||||||
@ -56,37 +58,40 @@ public enum Station {
|
|||||||
protected final String inboundTransferStation;
|
protected final String inboundTransferStation;
|
||||||
protected final String outboundTransferStation;
|
protected final String outboundTransferStation;
|
||||||
public final boolean endOfLine;
|
public final boolean endOfLine;
|
||||||
|
public final boolean longStationLinger;
|
||||||
|
|
||||||
private Station(String abbreviation, String name, boolean invertDirection, boolean endOfLine) {
|
private Station(String abbreviation, String name, boolean invertDirection,
|
||||||
this.abbreviation = abbreviation;
|
boolean endOfLine) {
|
||||||
this.name = name;
|
this(abbreviation, name, invertDirection, endOfLine, null, null, false);
|
||||||
this.invertDirection = invertDirection;
|
|
||||||
this.inboundTransferStation = null;
|
|
||||||
this.outboundTransferStation = null;
|
|
||||||
this.endOfLine = endOfLine;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Station(String abbreviation, String name, boolean invertDirection, boolean endOfLine,
|
private Station(String abbreviation, String name, boolean invertDirection,
|
||||||
String transferStation) {
|
boolean endOfLine, String transferStation) {
|
||||||
this.abbreviation = abbreviation;
|
this(abbreviation, name, invertDirection, endOfLine, transferStation,
|
||||||
this.name = name;
|
null, false);
|
||||||
this.invertDirection = invertDirection;
|
|
||||||
this.inboundTransferStation = transferStation;
|
|
||||||
this.outboundTransferStation = null;
|
|
||||||
this.endOfLine = endOfLine;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Station(String abbreviation, String name, boolean invertDirection, boolean endOfLine,
|
private Station(String abbreviation, String name, boolean invertDirection,
|
||||||
String inboundTransferStation, String outboundTransferStation) {
|
boolean endOfLine, String inboundTransferStation,
|
||||||
|
String outboundTransferStation) {
|
||||||
|
this(abbreviation, name, invertDirection, endOfLine,
|
||||||
|
inboundTransferStation, outboundTransferStation, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Station(String abbreviation, String name, boolean invertDirection,
|
||||||
|
boolean endOfLine, String inboundTransferStation,
|
||||||
|
String outboundTransferStation, boolean longStationLinger) {
|
||||||
this.abbreviation = abbreviation;
|
this.abbreviation = abbreviation;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.invertDirection = invertDirection;
|
this.invertDirection = invertDirection;
|
||||||
this.inboundTransferStation = inboundTransferStation;
|
this.inboundTransferStation = inboundTransferStation;
|
||||||
this.outboundTransferStation = outboundTransferStation;
|
this.outboundTransferStation = outboundTransferStation;
|
||||||
this.endOfLine = endOfLine;
|
this.endOfLine = endOfLine;
|
||||||
|
this.longStationLinger = longStationLinger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Station getByAbbreviation(String abbr) {
|
public static Station getByAbbreviation(String abbr) {
|
||||||
|
try {
|
||||||
if (abbr == null) {
|
if (abbr == null) {
|
||||||
return null;
|
return null;
|
||||||
} else if (Character.isDigit(abbr.charAt(0))) {
|
} else if (Character.isDigit(abbr.charAt(0))) {
|
||||||
@ -94,6 +99,10 @@ public enum Station {
|
|||||||
} else {
|
} else {
|
||||||
return Station.valueOf(abbr.toUpperCase());
|
return Station.valueOf(abbr.toUpperCase());
|
||||||
}
|
}
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Log.e(Constants.TAG, "Could not find station for '" + abbr + "'", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Station getInboundTransferStation() {
|
public Station getInboundTransferStation() {
|
||||||
@ -155,8 +164,7 @@ public enum Station {
|
|||||||
}
|
}
|
||||||
if (isNorth == null) {
|
if (isNorth == null) {
|
||||||
if (outboundTransferStation != null) {
|
if (outboundTransferStation != null) {
|
||||||
returnList
|
returnList.addAll(getOutboundTransferStation()
|
||||||
.addAll(getOutboundTransferStation()
|
|
||||||
.getRoutesForDestination(dest,
|
.getRoutesForDestination(dest,
|
||||||
getOutboundTransferStation()));
|
getOutboundTransferStation()));
|
||||||
} else if (dest.getInboundTransferStation() != null) {
|
} else if (dest.getInboundTransferStation() != null) {
|
21
src/com/dougkeen/bart/model/StationPair.java
Normal file
21
src/com/dougkeen/bart/model/StationPair.java
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package com.dougkeen.bart.model;
|
||||||
|
|
||||||
|
|
||||||
|
public class StationPair {
|
||||||
|
public StationPair(Station origin, Station destination) {
|
||||||
|
super();
|
||||||
|
this.origin = origin;
|
||||||
|
this.destination = destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Station origin;
|
||||||
|
private Station destination;
|
||||||
|
|
||||||
|
public Station getOrigin() {
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Station getDestination() {
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.dougkeen.bart;
|
package com.dougkeen.bart.networktasks;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -11,8 +11,12 @@ import org.xml.sax.helpers.DefaultHandler;
|
|||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.dougkeen.bart.data.Departure;
|
import com.dougkeen.bart.model.Constants;
|
||||||
import com.dougkeen.bart.data.RealTimeDepartures;
|
import com.dougkeen.bart.model.Departure;
|
||||||
|
import com.dougkeen.bart.model.Line;
|
||||||
|
import com.dougkeen.bart.model.RealTimeDepartures;
|
||||||
|
import com.dougkeen.bart.model.Route;
|
||||||
|
import com.dougkeen.bart.model.Station;
|
||||||
|
|
||||||
public class EtdContentHandler extends DefaultHandler {
|
public class EtdContentHandler extends DefaultHandler {
|
||||||
public EtdContentHandler(Station origin, Station destination,
|
public EtdContentHandler(Station origin, Station destination,
|
||||||
@ -55,6 +59,7 @@ public class EtdContentHandler extends DefaultHandler {
|
|||||||
currentDeparture = new Departure();
|
currentDeparture = new Departure();
|
||||||
currentDeparture.setDestination(Station
|
currentDeparture.setDestination(Station
|
||||||
.getByAbbreviation(currentDestination));
|
.getByAbbreviation(currentDestination));
|
||||||
|
currentDeparture.setOrigin(realTimeDepartures.getOrigin());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package com.dougkeen.bart;
|
package com.dougkeen.bart.networktasks;
|
||||||
|
|
||||||
import org.xml.sax.Attributes;
|
import org.xml.sax.Attributes;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
@ -1,4 +1,4 @@
|
|||||||
package com.dougkeen.bart;
|
package com.dougkeen.bart.networktasks;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
@ -17,11 +17,13 @@ import android.os.AsyncTask;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Xml;
|
import android.util.Xml;
|
||||||
|
|
||||||
import com.dougkeen.bart.data.RealTimeDepartures;
|
import com.dougkeen.bart.model.Constants;
|
||||||
|
import com.dougkeen.bart.model.StationPair;
|
||||||
|
import com.dougkeen.bart.model.RealTimeDepartures;
|
||||||
|
import com.dougkeen.bart.model.Route;
|
||||||
|
|
||||||
public abstract class GetRealTimeDeparturesTask
|
public abstract class GetRealTimeDeparturesTask extends
|
||||||
extends
|
AsyncTask<StationPair, Integer, RealTimeDepartures> {
|
||||||
AsyncTask<GetRealTimeDeparturesTask.Params, Integer, RealTimeDepartures> {
|
|
||||||
|
|
||||||
private final static String ETD_URL = "http://api.bart.gov/api/etd.aspx?cmd=etd&key="
|
private final static String ETD_URL = "http://api.bart.gov/api/etd.aspx?cmd=etd&key="
|
||||||
+ Constants.API_KEY + "&orig=%1$s&dir=%2$s";
|
+ Constants.API_KEY + "&orig=%1$s&dir=%2$s";
|
||||||
@ -34,11 +36,12 @@ public abstract class GetRealTimeDeparturesTask
|
|||||||
private List<Route> mRoutes;
|
private List<Route> mRoutes;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected RealTimeDepartures doInBackground(Params... paramsArray) {
|
protected RealTimeDepartures doInBackground(StationPair... paramsArray) {
|
||||||
// Always expect one param
|
// Always expect one param
|
||||||
Params params = paramsArray[0];
|
StationPair params = paramsArray[0];
|
||||||
|
|
||||||
mRoutes = params.origin.getRoutesForDestination(params.destination);
|
mRoutes = params.getOrigin().getRoutesForDestination(
|
||||||
|
params.getDestination());
|
||||||
|
|
||||||
if (!isCancelled()) {
|
if (!isCancelled()) {
|
||||||
return getDeparturesFromNetwork(params, 0);
|
return getDeparturesFromNetwork(params, 0);
|
||||||
@ -47,23 +50,23 @@ public abstract class GetRealTimeDeparturesTask
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private RealTimeDepartures getDeparturesFromNetwork(Params params,
|
private RealTimeDepartures getDeparturesFromNetwork(StationPair params,
|
||||||
int attemptNumber) {
|
int attemptNumber) {
|
||||||
String xml = null;
|
String xml = null;
|
||||||
try {
|
try {
|
||||||
String url;
|
String url;
|
||||||
if (params.origin.endOfLine) {
|
if (params.getOrigin().endOfLine) {
|
||||||
url = String.format(ETD_URL_NO_DIRECTION,
|
url = String.format(ETD_URL_NO_DIRECTION,
|
||||||
params.origin.abbreviation);
|
params.getOrigin().abbreviation);
|
||||||
} else {
|
} else {
|
||||||
url = String.format(ETD_URL, params.origin.abbreviation,
|
url = String.format(ETD_URL, params.getOrigin().abbreviation,
|
||||||
mRoutes.get(0).getDirection());
|
mRoutes.get(0).getDirection());
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpUriRequest request = new HttpGet(url);
|
HttpUriRequest request = new HttpGet(url);
|
||||||
|
|
||||||
EtdContentHandler handler = new EtdContentHandler(params.origin,
|
EtdContentHandler handler = new EtdContentHandler(
|
||||||
params.destination, mRoutes);
|
params.getOrigin(), params.getDestination(), mRoutes);
|
||||||
if (isCancelled()) {
|
if (isCancelled()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -113,25 +116,6 @@ public abstract class GetRealTimeDeparturesTask
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Params {
|
|
||||||
public Params(Station origin, Station destination) {
|
|
||||||
super();
|
|
||||||
this.origin = origin;
|
|
||||||
this.destination = destination;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Station origin;
|
|
||||||
private Station destination;
|
|
||||||
|
|
||||||
public Station getOrigin() {
|
|
||||||
return origin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Station getDestination() {
|
|
||||||
return destination;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(RealTimeDepartures result) {
|
protected void onPostExecute(RealTimeDepartures result) {
|
||||||
if (result != null) {
|
if (result != null) {
|
@ -1,4 +1,4 @@
|
|||||||
package com.dougkeen.bart;
|
package com.dougkeen.bart.networktasks;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
@ -12,6 +12,9 @@ import org.apache.http.client.methods.HttpGet;
|
|||||||
import org.apache.http.client.methods.HttpUriRequest;
|
import org.apache.http.client.methods.HttpUriRequest;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
import com.dougkeen.bart.model.Constants;
|
||||||
|
import com.dougkeen.bart.model.Station;
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Xml;
|
import android.util.Xml;
|
@ -0,0 +1,118 @@
|
|||||||
|
package com.dougkeen.bart.networktasks;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.HttpStatus;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.client.methods.HttpUriRequest;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.Xml;
|
||||||
|
|
||||||
|
import com.dougkeen.bart.model.Constants;
|
||||||
|
import com.dougkeen.bart.model.ScheduleInformation;
|
||||||
|
import com.dougkeen.bart.model.StationPair;
|
||||||
|
|
||||||
|
public abstract class GetScheduleInformationTask extends
|
||||||
|
AsyncTask<StationPair, Integer, ScheduleInformation> {
|
||||||
|
|
||||||
|
private final static String SCHED_URL = "http://api.bart.gov/api/sched.aspx?cmd=depart&key="
|
||||||
|
+ Constants.API_KEY + "&orig=%1$s&dest=%2$s&b=1&a=5";
|
||||||
|
|
||||||
|
private final static int MAX_ATTEMPTS = 5;
|
||||||
|
|
||||||
|
private Exception mException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ScheduleInformation doInBackground(StationPair... paramsArray) {
|
||||||
|
// Always expect one param
|
||||||
|
StationPair params = paramsArray[0];
|
||||||
|
|
||||||
|
if (!isCancelled()) {
|
||||||
|
return getScheduleFromNetwork(params, 0);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ScheduleInformation getScheduleFromNetwork(StationPair params,
|
||||||
|
int attemptNumber) {
|
||||||
|
String xml = null;
|
||||||
|
try {
|
||||||
|
String url = String.format(SCHED_URL,
|
||||||
|
params.getOrigin().abbreviation,
|
||||||
|
params.getDestination().abbreviation);
|
||||||
|
|
||||||
|
HttpUriRequest request = new HttpGet(url);
|
||||||
|
|
||||||
|
if (isCancelled()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScheduleContentHandler handler = new ScheduleContentHandler(
|
||||||
|
params.getOrigin(), params.getDestination());
|
||||||
|
|
||||||
|
HttpResponse response = NetworkUtils.executeWithRecovery(request);
|
||||||
|
|
||||||
|
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
|
||||||
|
throw new IOException("Server returned "
|
||||||
|
+ response.getStatusLine().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
IOUtils.copy(response.getEntity().getContent(), writer, "UTF-8");
|
||||||
|
|
||||||
|
xml = writer.toString();
|
||||||
|
if (xml.length() == 0) {
|
||||||
|
throw new IOException("Server returned blank xml document");
|
||||||
|
}
|
||||||
|
|
||||||
|
Xml.parse(xml, handler);
|
||||||
|
final ScheduleInformation schedule = handler.getSchedule();
|
||||||
|
return schedule;
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (attemptNumber < MAX_ATTEMPTS - 1) {
|
||||||
|
try {
|
||||||
|
Log.w(Constants.TAG,
|
||||||
|
"Attempt to contact server failed... retrying in 3s",
|
||||||
|
e);
|
||||||
|
Thread.sleep(3000);
|
||||||
|
} catch (InterruptedException interrupt) {
|
||||||
|
// Ignore... just go on to next attempt
|
||||||
|
}
|
||||||
|
return getScheduleFromNetwork(params, attemptNumber + 1);
|
||||||
|
} else {
|
||||||
|
mException = new Exception("Could not contact BART system", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (SAXException e) {
|
||||||
|
mException = new Exception(
|
||||||
|
"Could not understand response from BART system: " + xml, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(ScheduleInformation result) {
|
||||||
|
if (result != null) {
|
||||||
|
onResult(result);
|
||||||
|
} else {
|
||||||
|
onError(mException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void onResult(ScheduleInformation result);
|
||||||
|
|
||||||
|
public abstract void onError(Exception exception);
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.dougkeen.bart;
|
package com.dougkeen.bart.networktasks;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
160
src/com/dougkeen/bart/networktasks/ScheduleContentHandler.java
Normal file
160
src/com/dougkeen/bart/networktasks/ScheduleContentHandler.java
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
package com.dougkeen.bart.networktasks;
|
||||||
|
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.xml.sax.Attributes;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
import org.xml.sax.helpers.DefaultHandler;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.dougkeen.bart.model.Constants;
|
||||||
|
import com.dougkeen.bart.model.Departure;
|
||||||
|
import com.dougkeen.bart.model.Line;
|
||||||
|
import com.dougkeen.bart.model.RealTimeDepartures;
|
||||||
|
import com.dougkeen.bart.model.Route;
|
||||||
|
import com.dougkeen.bart.model.ScheduleInformation;
|
||||||
|
import com.dougkeen.bart.model.ScheduleItem;
|
||||||
|
import com.dougkeen.bart.model.Station;
|
||||||
|
|
||||||
|
public class ScheduleContentHandler extends DefaultHandler {
|
||||||
|
public ScheduleContentHandler(Station origin, Station destination) {
|
||||||
|
super();
|
||||||
|
schedule = new ScheduleInformation(origin, destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static List<String> TAGS = Arrays.asList("date", "time",
|
||||||
|
"trip");
|
||||||
|
|
||||||
|
private final static DateFormat TRIP_DATE_FORMAT;
|
||||||
|
private final static DateFormat REQUEST_DATE_FORMAT;
|
||||||
|
|
||||||
|
private final static TimeZone PACIFIC_TIME = TimeZone
|
||||||
|
.getTimeZone("America/Los_Angeles");
|
||||||
|
|
||||||
|
static {
|
||||||
|
TRIP_DATE_FORMAT = new SimpleDateFormat("MM/dd/yyyy h:mm a");
|
||||||
|
REQUEST_DATE_FORMAT = new SimpleDateFormat("MMM d, yyyy h:mm a");
|
||||||
|
|
||||||
|
TRIP_DATE_FORMAT.setTimeZone(PACIFIC_TIME);
|
||||||
|
REQUEST_DATE_FORMAT.setTimeZone(PACIFIC_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ScheduleInformation schedule;
|
||||||
|
|
||||||
|
public ScheduleInformation getSchedule() {
|
||||||
|
return schedule;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String currentValue;
|
||||||
|
private boolean isParsingTag;
|
||||||
|
|
||||||
|
private String requestDate;
|
||||||
|
private String requestTime;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void characters(char[] ch, int start, int length)
|
||||||
|
throws SAXException {
|
||||||
|
if (isParsingTag) {
|
||||||
|
currentValue = new String(ch, start, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startElement(String uri, String localName, String qName,
|
||||||
|
Attributes attributes) throws SAXException {
|
||||||
|
if (TAGS.contains(localName)) {
|
||||||
|
isParsingTag = true;
|
||||||
|
}
|
||||||
|
if (localName.equals("trip")) {
|
||||||
|
ScheduleItem trip = new ScheduleItem();
|
||||||
|
String originDate = null;
|
||||||
|
String originTime = null;
|
||||||
|
String destinationDate = null;
|
||||||
|
String destinationTime = null;
|
||||||
|
for (int i = attributes.getLength() - 1; i >= 0; i--) {
|
||||||
|
if (attributes.getLocalName(i).equalsIgnoreCase("origin")) {
|
||||||
|
trip.setOrigin(Station.getByAbbreviation(attributes
|
||||||
|
.getValue(i)));
|
||||||
|
} else if (attributes.getLocalName(i).equalsIgnoreCase(
|
||||||
|
"destination")) {
|
||||||
|
trip.setDestination(Station.getByAbbreviation(attributes
|
||||||
|
.getValue(i)));
|
||||||
|
} else if (attributes.getLocalName(i).equalsIgnoreCase("fare")) {
|
||||||
|
trip.setFare(attributes.getValue(i));
|
||||||
|
} else if (attributes.getLocalName(i).equalsIgnoreCase(
|
||||||
|
"origTimeMin")) {
|
||||||
|
originTime = attributes.getValue(i);
|
||||||
|
} else if (attributes.getLocalName(i).equalsIgnoreCase(
|
||||||
|
"origTimeDate")) {
|
||||||
|
originDate = attributes.getValue(i);
|
||||||
|
} else if (attributes.getLocalName(i).equalsIgnoreCase(
|
||||||
|
"destTimeMin")) {
|
||||||
|
destinationTime = attributes.getValue(i);
|
||||||
|
} else if (attributes.getLocalName(i).equalsIgnoreCase(
|
||||||
|
"destTimeDate")) {
|
||||||
|
destinationDate = attributes.getValue(i);
|
||||||
|
} else if (attributes.getLocalName(i).equalsIgnoreCase(
|
||||||
|
"bikeFlag")) {
|
||||||
|
trip.setBikesAllowed(attributes.getValue(i).equals("1"));
|
||||||
|
} else if (attributes.getLocalName(i).equalsIgnoreCase(
|
||||||
|
"trainHeadStation")) {
|
||||||
|
trip.setTrainHeadStation(attributes.getValue(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long departTime = parseDate(TRIP_DATE_FORMAT, originDate,
|
||||||
|
originTime);
|
||||||
|
if (departTime > 0)
|
||||||
|
trip.setDepartureTime(departTime);
|
||||||
|
|
||||||
|
long arriveTime = parseDate(TRIP_DATE_FORMAT, destinationDate,
|
||||||
|
destinationTime);
|
||||||
|
if (arriveTime > 0)
|
||||||
|
trip.setArrivalTime(arriveTime);
|
||||||
|
|
||||||
|
schedule.addTrip(trip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long parseDate(DateFormat format, String dateString,
|
||||||
|
String timeString) {
|
||||||
|
if (dateString == null || timeString == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return format.parse(dateString + " " + timeString).getTime();
|
||||||
|
} catch (ParseException e) {
|
||||||
|
Log.e(Constants.TAG, "Unable to parse datetime '" + dateString
|
||||||
|
+ " " + timeString + "'", e);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endElement(String uri, String localName, String qName)
|
||||||
|
throws SAXException {
|
||||||
|
if (localName.equals("date")) {
|
||||||
|
requestDate = currentValue;
|
||||||
|
} else if (localName.equals("time")) {
|
||||||
|
requestTime = currentValue;
|
||||||
|
}
|
||||||
|
isParsingTag = false;
|
||||||
|
currentValue = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endDocument() {
|
||||||
|
long date = parseDate(REQUEST_DATE_FORMAT, requestDate, requestTime);
|
||||||
|
if (date > 0) {
|
||||||
|
schedule.setDate(date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user