2011-05-23 18:59:34 +00:00
|
|
|
package com.dougkeen.bart;
|
|
|
|
|
|
|
|
import java.util.List;
|
|
|
|
|
2012-04-30 21:16:38 +00:00
|
|
|
import org.apache.commons.lang3.math.NumberUtils;
|
|
|
|
|
2012-04-13 01:07:55 +00:00
|
|
|
import android.content.ContentValues;
|
2011-05-23 18:59:34 +00:00
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.database.Cursor;
|
|
|
|
import android.net.Uri;
|
|
|
|
import android.os.AsyncTask;
|
|
|
|
import android.os.Bundle;
|
2011-05-27 21:06:58 +00:00
|
|
|
import android.os.Parcelable;
|
2011-05-23 18:59:34 +00:00
|
|
|
import android.os.PowerManager;
|
2011-05-27 21:06:58 +00:00
|
|
|
import android.text.format.DateFormat;
|
2011-06-05 18:13:21 +00:00
|
|
|
import android.text.util.Linkify;
|
2011-05-27 21:06:58 +00:00
|
|
|
import android.util.Log;
|
|
|
|
import android.view.Menu;
|
|
|
|
import android.view.MenuInflater;
|
|
|
|
import android.view.MenuItem;
|
2012-02-20 01:46:11 +00:00
|
|
|
import android.view.View;
|
2011-05-23 18:59:34 +00:00
|
|
|
import android.widget.TextView;
|
|
|
|
import android.widget.Toast;
|
|
|
|
|
2012-07-15 17:00:39 +00:00
|
|
|
import com.dougkeen.bart.actionbarcompat.ActionBarListActivity;
|
2011-06-01 03:42:32 +00:00
|
|
|
import com.dougkeen.bart.data.RoutesColumns;
|
2012-04-13 01:07:55 +00:00
|
|
|
import com.dougkeen.bart.model.Constants;
|
|
|
|
import com.dougkeen.bart.model.Departure;
|
2012-04-13 22:37:19 +00:00
|
|
|
import com.dougkeen.bart.model.RealTimeDepartures;
|
2012-04-13 01:07:55 +00:00
|
|
|
import com.dougkeen.bart.model.ScheduleInformation;
|
|
|
|
import com.dougkeen.bart.model.ScheduleItem;
|
|
|
|
import com.dougkeen.bart.model.Station;
|
2012-04-13 22:37:19 +00:00
|
|
|
import com.dougkeen.bart.model.StationPair;
|
2012-04-13 01:07:55 +00:00
|
|
|
import com.dougkeen.bart.networktasks.GetRealTimeDeparturesTask;
|
|
|
|
import com.dougkeen.bart.networktasks.GetScheduleInformationTask;
|
2011-05-23 18:59:34 +00:00
|
|
|
|
2012-07-15 17:00:39 +00:00
|
|
|
public class ViewDeparturesActivity extends ActionBarListActivity {
|
2011-05-23 18:59:34 +00:00
|
|
|
|
2011-05-27 21:06:58 +00:00
|
|
|
private static final int UNCERTAINTY_THRESHOLD = 17;
|
|
|
|
|
2011-05-23 18:59:34 +00:00
|
|
|
private Uri mUri;
|
|
|
|
|
|
|
|
private Station mOrigin;
|
|
|
|
private Station mDestination;
|
2012-04-13 01:07:55 +00:00
|
|
|
private int mAverageTripLength;
|
|
|
|
private int mAverageTripSampleCount;
|
2011-05-23 18:59:34 +00:00
|
|
|
|
2012-04-13 22:37:19 +00:00
|
|
|
private DepartureArrayAdapter mDeparturesAdapter;
|
2011-05-23 18:59:34 +00:00
|
|
|
|
2012-04-13 01:07:55 +00:00
|
|
|
private ScheduleInformation mLatestScheduleInfo;
|
|
|
|
|
2012-07-15 17:00:39 +00:00
|
|
|
private TextView mEmptyView;
|
2011-05-23 18:59:34 +00:00
|
|
|
|
2012-04-13 01:07:55 +00:00
|
|
|
private AsyncTask<StationPair, Integer, RealTimeDepartures> mGetDeparturesTask;
|
|
|
|
private AsyncTask<StationPair, Integer, ScheduleInformation> mGetScheduleInformationTask;
|
2011-05-23 18:59:34 +00:00
|
|
|
|
|
|
|
private boolean mIsAutoUpdating = false;
|
|
|
|
|
|
|
|
private final Runnable AUTO_UPDATE_RUNNABLE = new Runnable() {
|
|
|
|
public void run() {
|
|
|
|
runAutoUpdate();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
private PowerManager.WakeLock mWakeLock;
|
|
|
|
|
2012-04-13 01:07:55 +00:00
|
|
|
private boolean mDepartureFetchIsPending;
|
|
|
|
private boolean mScheduleFetchIsPending;
|
2011-05-27 21:06:58 +00:00
|
|
|
|
2011-05-23 18:59:34 +00:00
|
|
|
@Override
|
|
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
|
|
super.onCreate(savedInstanceState);
|
2012-07-15 17:00:39 +00:00
|
|
|
setContentView(R.layout.departures);
|
2011-05-23 18:59:34 +00:00
|
|
|
|
|
|
|
final Intent intent = getIntent();
|
|
|
|
|
|
|
|
String action = intent.getAction();
|
|
|
|
|
|
|
|
if (Intent.ACTION_VIEW.equals(action)) {
|
|
|
|
mUri = intent.getData();
|
|
|
|
}
|
|
|
|
|
|
|
|
Cursor cursor = managedQuery(mUri, new String[] {
|
2011-05-27 21:06:58 +00:00
|
|
|
RoutesColumns.FROM_STATION.string,
|
2012-04-13 01:07:55 +00:00
|
|
|
RoutesColumns.TO_STATION.string,
|
|
|
|
RoutesColumns.AVERAGE_TRIP_LENGTH.string,
|
|
|
|
RoutesColumns.AVERAGE_TRIP_SAMPLE_COUNT.string }, null, null,
|
|
|
|
null);
|
2011-05-23 18:59:34 +00:00
|
|
|
|
|
|
|
if (!cursor.moveToFirst()) {
|
|
|
|
throw new IllegalStateException("URI not found: " + mUri.toString());
|
|
|
|
}
|
|
|
|
mOrigin = Station.getByAbbreviation(cursor.getString(0));
|
|
|
|
mDestination = Station.getByAbbreviation(cursor.getString(1));
|
2012-04-13 01:07:55 +00:00
|
|
|
mAverageTripLength = cursor.getInt(2);
|
|
|
|
mAverageTripSampleCount = cursor.getInt(3);
|
2011-05-23 18:59:34 +00:00
|
|
|
|
2012-07-15 17:00:39 +00:00
|
|
|
((TextView) findViewById(R.id.listTitle)).setText(mOrigin.name + " to "
|
|
|
|
+ mDestination.name);
|
2011-05-23 18:59:34 +00:00
|
|
|
|
2012-07-15 17:00:39 +00:00
|
|
|
mEmptyView = (TextView) findViewById(android.R.id.empty);
|
|
|
|
mEmptyView.setText(R.string.departure_wait_message);
|
2011-05-23 18:59:34 +00:00
|
|
|
|
2011-06-05 18:13:21 +00:00
|
|
|
mDeparturesAdapter = new DepartureArrayAdapter(this,
|
|
|
|
R.layout.departure_listing);
|
2011-05-27 21:06:58 +00:00
|
|
|
if (savedInstanceState != null
|
2011-06-05 18:13:21 +00:00
|
|
|
&& savedInstanceState.containsKey("departures")) {
|
|
|
|
for (Parcelable departure : savedInstanceState
|
|
|
|
.getParcelableArray("departures")) {
|
|
|
|
mDeparturesAdapter.add((Departure) departure);
|
2011-05-27 21:06:58 +00:00
|
|
|
}
|
|
|
|
}
|
2011-06-05 18:13:21 +00:00
|
|
|
setListAdapter(mDeparturesAdapter);
|
2012-02-20 01:46:11 +00:00
|
|
|
|
|
|
|
findViewById(R.id.missingDepartureText).setVisibility(View.VISIBLE);
|
2011-05-27 21:06:58 +00:00
|
|
|
}
|
|
|
|
|
2011-06-20 22:34:09 +00:00
|
|
|
@Override
|
|
|
|
protected void onPause() {
|
|
|
|
cancelDataFetch();
|
|
|
|
super.onPause();
|
|
|
|
}
|
|
|
|
|
2011-05-27 21:06:58 +00:00
|
|
|
@Override
|
|
|
|
protected void onDestroy() {
|
2011-06-20 22:34:09 +00:00
|
|
|
cancelDataFetch();
|
|
|
|
super.onDestroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void cancelDataFetch() {
|
2011-06-05 18:13:21 +00:00
|
|
|
if (mGetDeparturesTask != null) {
|
|
|
|
mGetDeparturesTask.cancel(true);
|
2012-04-13 01:07:55 +00:00
|
|
|
mDepartureFetchIsPending = false;
|
|
|
|
}
|
|
|
|
if (mGetScheduleInformationTask != null) {
|
|
|
|
mGetScheduleInformationTask.cancel(true);
|
|
|
|
mScheduleFetchIsPending = false;
|
2011-05-27 21:06:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onSaveInstanceState(Bundle outState) {
|
|
|
|
super.onSaveInstanceState(outState);
|
2011-06-05 18:13:21 +00:00
|
|
|
Departure[] departures = new Departure[mDeparturesAdapter.getCount()];
|
|
|
|
for (int i = mDeparturesAdapter.getCount() - 1; i >= 0; i--) {
|
|
|
|
departures[i] = mDeparturesAdapter.getItem(i);
|
2011-05-27 21:06:58 +00:00
|
|
|
}
|
2011-06-05 18:13:21 +00:00
|
|
|
outState.putParcelableArray("departures", departures);
|
2011-05-23 18:59:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onWindowFocusChanged(boolean hasFocus) {
|
|
|
|
super.onWindowFocusChanged(hasFocus);
|
|
|
|
if (hasFocus) {
|
2012-04-13 01:07:55 +00:00
|
|
|
if (!mDepartureFetchIsPending) {
|
2011-06-05 18:13:21 +00:00
|
|
|
fetchLatestDepartures();
|
2011-05-27 21:06:58 +00:00
|
|
|
}
|
2011-06-20 22:34:09 +00:00
|
|
|
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
|
|
|
mWakeLock = powerManager
|
2011-11-05 18:51:20 +00:00
|
|
|
.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK,
|
2011-06-05 18:13:21 +00:00
|
|
|
"ViewDeparturesActivity");
|
2011-05-23 18:59:34 +00:00
|
|
|
mWakeLock.acquire();
|
2011-06-05 18:13:21 +00:00
|
|
|
if (mDeparturesAdapter != null && !mDeparturesAdapter.isEmpty()) {
|
2011-05-27 21:06:58 +00:00
|
|
|
mIsAutoUpdating = true;
|
|
|
|
}
|
|
|
|
runAutoUpdate();
|
2011-05-23 18:59:34 +00:00
|
|
|
} else if (mWakeLock != null) {
|
|
|
|
mWakeLock.release();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-30 21:16:38 +00:00
|
|
|
private boolean mIgnoreDepartureDirection = false;
|
|
|
|
|
2011-06-05 18:13:21 +00:00
|
|
|
private void fetchLatestDepartures() {
|
2011-05-27 21:06:58 +00:00
|
|
|
if (!hasWindowFocus())
|
|
|
|
return;
|
2011-06-05 18:13:21 +00:00
|
|
|
if (mGetDeparturesTask != null
|
2011-11-05 18:51:20 +00:00
|
|
|
&& mGetDeparturesTask.getStatus().equals(
|
|
|
|
AsyncTask.Status.RUNNING)) {
|
2011-06-01 03:42:32 +00:00
|
|
|
// Don't overlap fetches
|
|
|
|
return;
|
|
|
|
}
|
2011-05-27 21:06:58 +00:00
|
|
|
|
2012-04-30 21:16:38 +00:00
|
|
|
mGetDeparturesTask = new GetRealTimeDeparturesTask(
|
|
|
|
mIgnoreDepartureDirection) {
|
2011-05-23 18:59:34 +00:00
|
|
|
@Override
|
2011-06-05 18:13:21 +00:00
|
|
|
public void onResult(RealTimeDepartures result) {
|
2012-04-13 01:07:55 +00:00
|
|
|
mDepartureFetchIsPending = false;
|
2011-06-01 03:42:32 +00:00
|
|
|
Log.i(Constants.TAG, "Processing data from server");
|
2011-06-05 18:13:21 +00:00
|
|
|
processLatestDepartures(result);
|
2011-06-01 03:42:32 +00:00
|
|
|
Log.i(Constants.TAG, "Done processing data from server");
|
2011-05-23 18:59:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-06-17 21:35:20 +00:00
|
|
|
public void onError(Exception e) {
|
2012-04-13 01:07:55 +00:00
|
|
|
mDepartureFetchIsPending = false;
|
|
|
|
Log.w(Constants.TAG, e.getMessage(), e);
|
|
|
|
Toast.makeText(ViewDeparturesActivity.this,
|
|
|
|
R.string.could_not_connect, Toast.LENGTH_LONG).show();
|
2012-07-15 17:00:39 +00:00
|
|
|
mEmptyView.setText(R.string.could_not_connect);
|
2012-04-13 01:07:55 +00:00
|
|
|
// Try again in 60s
|
|
|
|
scheduleDepartureFetch(60000);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Log.i(Constants.TAG, "Fetching data from server");
|
|
|
|
mGetDeparturesTask.execute(new StationPair(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;
|
2011-06-17 21:35:20 +00:00
|
|
|
Log.w(Constants.TAG, e.getMessage(), e);
|
2011-06-05 18:13:21 +00:00
|
|
|
Toast.makeText(ViewDeparturesActivity.this,
|
2011-11-05 18:51:20 +00:00
|
|
|
R.string.could_not_connect, Toast.LENGTH_LONG).show();
|
2012-07-15 17:00:39 +00:00
|
|
|
mEmptyView.setText(R.string.could_not_connect);
|
2011-07-03 01:11:10 +00:00
|
|
|
// Try again in 60s
|
2012-04-13 01:07:55 +00:00
|
|
|
scheduleScheduleInfoFetch(60000);
|
2011-05-23 18:59:34 +00:00
|
|
|
}
|
|
|
|
};
|
2011-06-01 03:42:32 +00:00
|
|
|
Log.i(Constants.TAG, "Fetching data from server");
|
2012-04-13 01:07:55 +00:00
|
|
|
mGetScheduleInformationTask.execute(new StationPair(mOrigin,
|
|
|
|
mDestination));
|
2011-05-23 18:59:34 +00:00
|
|
|
}
|
|
|
|
|
2011-06-05 18:13:21 +00:00
|
|
|
protected void processLatestDepartures(RealTimeDepartures result) {
|
2012-04-29 16:42:54 +00:00
|
|
|
if (result.getDepartures().isEmpty()) {
|
|
|
|
result.includeTransferRoutes();
|
|
|
|
}
|
|
|
|
if (result.getDepartures().isEmpty()) {
|
|
|
|
result.includeDoubleTransferRoutes();
|
|
|
|
}
|
2012-04-30 21:16:38 +00:00
|
|
|
if (result.getDepartures().isEmpty() && !mIgnoreDepartureDirection) {
|
|
|
|
// Let's try again, ignoring direction (this sometimes comes up when
|
|
|
|
// you travel between Millbrae and SFO)
|
|
|
|
mIgnoreDepartureDirection = true;
|
|
|
|
scheduleDepartureFetch(50);
|
|
|
|
return;
|
|
|
|
}
|
2011-06-05 18:13:21 +00:00
|
|
|
if (result.getDepartures().isEmpty()) {
|
2012-07-15 17:00:39 +00:00
|
|
|
final TextView textView = mEmptyView;
|
2011-06-05 18:13:21 +00:00
|
|
|
textView.setText(R.string.no_data_message);
|
|
|
|
Linkify.addLinks(textView, Linkify.WEB_URLS);
|
2011-05-25 15:31:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-05-27 21:06:58 +00:00
|
|
|
boolean needsBetterAccuracy = false;
|
2011-06-05 18:13:21 +00:00
|
|
|
Departure firstDeparture = null;
|
|
|
|
final List<Departure> departures = result.getDepartures();
|
|
|
|
if (mDeparturesAdapter.getCount() > 0) {
|
2011-05-23 18:59:34 +00:00
|
|
|
int adapterIndex = -1;
|
2011-06-05 18:13:21 +00:00
|
|
|
for (Departure departure : departures) {
|
2011-05-23 18:59:34 +00:00
|
|
|
adapterIndex++;
|
2011-06-05 18:13:21 +00:00
|
|
|
Departure existingDeparture = null;
|
|
|
|
if (adapterIndex < mDeparturesAdapter.getCount()) {
|
|
|
|
existingDeparture = mDeparturesAdapter
|
|
|
|
.getItem(adapterIndex);
|
2011-05-23 18:59:34 +00:00
|
|
|
}
|
2011-06-05 18:13:21 +00:00
|
|
|
while (existingDeparture != null
|
|
|
|
&& !departure.equals(existingDeparture)) {
|
|
|
|
mDeparturesAdapter.remove(existingDeparture);
|
|
|
|
if (adapterIndex < mDeparturesAdapter.getCount()) {
|
|
|
|
existingDeparture = mDeparturesAdapter
|
2011-05-23 18:59:34 +00:00
|
|
|
.getItem(adapterIndex);
|
|
|
|
} else {
|
2011-06-05 18:13:21 +00:00
|
|
|
existingDeparture = null;
|
2011-05-23 18:59:34 +00:00
|
|
|
}
|
|
|
|
}
|
2011-06-05 18:13:21 +00:00
|
|
|
if (existingDeparture != null) {
|
|
|
|
existingDeparture.mergeEstimate(departure);
|
2011-05-23 18:59:34 +00:00
|
|
|
} else {
|
2011-06-05 18:13:21 +00:00
|
|
|
mDeparturesAdapter.add(departure);
|
|
|
|
existingDeparture = departure;
|
2011-05-23 18:59:34 +00:00
|
|
|
}
|
2011-06-05 18:13:21 +00:00
|
|
|
if (firstDeparture == null) {
|
|
|
|
firstDeparture = existingDeparture;
|
2011-05-23 18:59:34 +00:00
|
|
|
}
|
2011-06-05 18:13:21 +00:00
|
|
|
if (existingDeparture.getUncertaintySeconds() > UNCERTAINTY_THRESHOLD) {
|
2011-05-27 21:06:58 +00:00
|
|
|
needsBetterAccuracy = true;
|
|
|
|
}
|
2011-05-23 18:59:34 +00:00
|
|
|
}
|
|
|
|
} else {
|
2011-06-05 18:13:21 +00:00
|
|
|
for (Departure departure : departures) {
|
|
|
|
if (firstDeparture == null) {
|
|
|
|
firstDeparture = departure;
|
2011-05-23 18:59:34 +00:00
|
|
|
}
|
2011-06-05 18:13:21 +00:00
|
|
|
mDeparturesAdapter.add(departure);
|
2011-05-23 18:59:34 +00:00
|
|
|
}
|
2011-05-27 21:06:58 +00:00
|
|
|
needsBetterAccuracy = true;
|
2011-05-23 18:59:34 +00:00
|
|
|
}
|
2011-06-05 18:13:21 +00:00
|
|
|
mDeparturesAdapter.notifyDataSetChanged();
|
2012-04-13 01:07:55 +00:00
|
|
|
requestScheduleIfNecessary();
|
2011-05-23 18:59:34 +00:00
|
|
|
|
2011-06-05 18:13:21 +00:00
|
|
|
if (hasWindowFocus() && firstDeparture != null) {
|
2011-11-05 18:51:20 +00:00
|
|
|
if (needsBetterAccuracy || firstDeparture.hasDeparted()) {
|
2011-05-23 18:59:34 +00:00
|
|
|
// Get more data in 20s
|
2012-04-13 01:07:55 +00:00
|
|
|
scheduleDepartureFetch(20000);
|
2011-05-23 18:59:34 +00:00
|
|
|
} else {
|
2011-06-05 18:13:21 +00:00
|
|
|
// Get more 90 seconds before next train arrives, right when
|
|
|
|
// next train arrives, or 3 minutes, whichever is sooner
|
2011-06-06 15:42:11 +00:00
|
|
|
final int intervalUntilNextDeparture = firstDeparture
|
|
|
|
.getMinSecondsLeft() * 1000;
|
2011-06-05 18:13:21 +00:00
|
|
|
final int alternativeInterval = 3 * 60 * 1000;
|
|
|
|
|
|
|
|
int interval = intervalUntilNextDeparture;
|
|
|
|
if (intervalUntilNextDeparture > 95000
|
|
|
|
&& intervalUntilNextDeparture < alternativeInterval) {
|
|
|
|
interval = interval - 90 * 1000;
|
|
|
|
} else if (intervalUntilNextDeparture > alternativeInterval) {
|
|
|
|
interval = alternativeInterval;
|
|
|
|
}
|
2011-06-17 21:35:20 +00:00
|
|
|
|
|
|
|
if (interval < 0) {
|
|
|
|
interval = 20000;
|
|
|
|
}
|
|
|
|
|
2012-04-13 01:07:55 +00:00
|
|
|
scheduleDepartureFetch(interval);
|
2011-05-23 18:59:34 +00:00
|
|
|
}
|
|
|
|
if (!mIsAutoUpdating) {
|
|
|
|
mIsAutoUpdating = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mIsAutoUpdating = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-13 01:07:55 +00:00
|
|
|
private void requestScheduleIfNecessary() {
|
|
|
|
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();
|
2012-04-30 21:16:38 +00:00
|
|
|
|
|
|
|
// Let's get smallest interval between departures
|
|
|
|
int smallestDepartureInterval = 0;
|
|
|
|
long previousDepartureTime = 0;
|
|
|
|
for (int departureIndex = 0; departureIndex < departuresCount; departureIndex++) {
|
|
|
|
Departure departure = mDeparturesAdapter.getItem(departureIndex);
|
|
|
|
if (previousDepartureTime == 0) {
|
|
|
|
previousDepartureTime = departure.getMeanEstimate();
|
|
|
|
} else if (smallestDepartureInterval == 0) {
|
|
|
|
smallestDepartureInterval = (int) (departure.getMeanEstimate() - previousDepartureTime);
|
|
|
|
} else {
|
|
|
|
smallestDepartureInterval = Math
|
|
|
|
.min(smallestDepartureInterval,
|
|
|
|
(int) (departure.getMeanEstimate() - previousDepartureTime));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-13 01:07:55 +00:00
|
|
|
int lastSearchIndex = 0;
|
|
|
|
int tripCount = mLatestScheduleInfo.getTrips().size();
|
|
|
|
boolean departureUpdated = false;
|
2012-04-29 16:42:54 +00:00
|
|
|
Departure lastUnestimatedTransfer = null;
|
|
|
|
int departuresWithoutEstimates = 0;
|
2012-04-13 01:07:55 +00:00
|
|
|
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);
|
2012-04-28 15:44:53 +00:00
|
|
|
if (!departure.getDestination().abbreviation.equals(trip
|
|
|
|
.getTrainHeadStation())) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-04-13 01:07:55 +00:00
|
|
|
long departTimeDiff = Math.abs(trip.getDepartureTime()
|
|
|
|
- departure.getMeanEstimate());
|
2012-04-28 15:44:53 +00:00
|
|
|
final long millisUntilTripDeparture = trip.getDepartureTime()
|
|
|
|
- System.currentTimeMillis();
|
2012-04-30 21:16:38 +00:00
|
|
|
final int equalityTolerance = (departure.getOrigin() != null) ? NumberUtils
|
2012-04-29 16:42:54 +00:00
|
|
|
.max(departure.getOrigin().departureEqualityTolerance,
|
2012-04-30 21:16:38 +00:00
|
|
|
ScheduleItem.SCHEDULE_ITEM_DEPARTURE_EQUALS_TOLERANCE,
|
|
|
|
smallestDepartureInterval)
|
2012-04-29 16:42:54 +00:00
|
|
|
: ScheduleItem.SCHEDULE_ITEM_DEPARTURE_EQUALS_TOLERANCE;
|
2012-04-28 15:44:53 +00:00
|
|
|
if (departure.getOrigin() != null
|
|
|
|
&& departure.getOrigin().longStationLinger
|
|
|
|
&& departure.hasDeparted()
|
|
|
|
&& millisUntilTripDeparture > 0
|
|
|
|
&& millisUntilTripDeparture < equalityTolerance) {
|
|
|
|
departure.setArrivalTimeOverride(trip.getArrivalTime());
|
|
|
|
lastSearchIndex = i;
|
|
|
|
departureUpdated = true;
|
2012-04-29 16:42:54 +00:00
|
|
|
if (lastUnestimatedTransfer != null) {
|
|
|
|
lastUnestimatedTransfer.setArrivalTimeOverride(trip
|
|
|
|
.getArrivalTime());
|
|
|
|
departuresWithoutEstimates--;
|
|
|
|
}
|
2012-04-28 15:44:53 +00:00
|
|
|
break;
|
|
|
|
} else if (departTimeDiff <= (equalityTolerance + departure
|
2012-04-13 01:07:55 +00:00
|
|
|
.getUncertaintySeconds() * 1000)
|
|
|
|
&& departure.getEstimatedTripTime() != trip
|
2012-04-30 21:29:40 +00:00
|
|
|
.getTripLength()
|
|
|
|
&& !(departure.getOrigin().longStationLinger && departure
|
|
|
|
.hasDeparted())) {
|
2012-04-13 01:07:55 +00:00
|
|
|
departure.setEstimatedTripTime(trip.getTripLength());
|
|
|
|
lastSearchIndex = i;
|
|
|
|
departureUpdated = true;
|
2012-04-29 16:42:54 +00:00
|
|
|
if (lastUnestimatedTransfer != null) {
|
|
|
|
lastUnestimatedTransfer.setArrivalTimeOverride(trip
|
|
|
|
.getArrivalTime());
|
|
|
|
departuresWithoutEstimates--;
|
|
|
|
}
|
2012-04-13 01:07:55 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-04-29 16:42:54 +00:00
|
|
|
// Don't estimate for non-scheduled transfers
|
|
|
|
if (!departure.getRequiresTransfer()) {
|
|
|
|
if (!departure.hasEstimatedTripTime() && localAverageLength > 0) {
|
|
|
|
departure.setEstimatedTripTime(localAverageLength);
|
|
|
|
} else if (!departure.hasEstimatedTripTime()) {
|
|
|
|
departure.setEstimatedTripTime(mAverageTripLength);
|
|
|
|
}
|
|
|
|
} else if (departure.getRequiresTransfer()
|
|
|
|
&& !departure.hasAnyArrivalEstimate()) {
|
|
|
|
lastUnestimatedTransfer = departure;
|
|
|
|
}
|
|
|
|
if (!departure.hasAnyArrivalEstimate()) {
|
|
|
|
departuresWithoutEstimates++;
|
2012-04-13 01:07:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2012-04-29 16:42:54 +00:00
|
|
|
|
|
|
|
if (departuresWithoutEstimates > 0) {
|
|
|
|
scheduleScheduleInfoFetch(20000);
|
|
|
|
}
|
2012-04-13 01:07:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private void scheduleDepartureFetch(int millisUntilExecute) {
|
|
|
|
if (!mDepartureFetchIsPending) {
|
2012-07-15 17:00:39 +00:00
|
|
|
postDelayed(new Runnable() {
|
2011-06-17 21:35:20 +00:00
|
|
|
public void run() {
|
|
|
|
fetchLatestDepartures();
|
|
|
|
}
|
|
|
|
}, millisUntilExecute);
|
2012-04-13 01:07:55 +00:00
|
|
|
mDepartureFetchIsPending = true;
|
|
|
|
Log.i(Constants.TAG, "Scheduled another departure fetch in "
|
|
|
|
+ millisUntilExecute / 1000 + "s");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void scheduleScheduleInfoFetch(int millisUntilExecute) {
|
|
|
|
if (!mScheduleFetchIsPending) {
|
2012-07-15 17:00:39 +00:00
|
|
|
postDelayed(new Runnable() {
|
2012-04-13 01:07:55 +00:00
|
|
|
public void run() {
|
|
|
|
fetchLatestSchedule();
|
|
|
|
}
|
|
|
|
}, millisUntilExecute);
|
|
|
|
mScheduleFetchIsPending = true;
|
|
|
|
Log.i(Constants.TAG, "Scheduled another schedule fetch in "
|
2011-06-17 21:35:20 +00:00
|
|
|
+ millisUntilExecute / 1000 + "s");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-28 15:44:53 +00:00
|
|
|
private long mLastAutoUpdate = 0;
|
|
|
|
|
2011-05-23 18:59:34 +00:00
|
|
|
private void runAutoUpdate() {
|
2012-04-28 15:44:53 +00:00
|
|
|
long now = System.currentTimeMillis();
|
|
|
|
if (now - mLastAutoUpdate < 950) {
|
|
|
|
return;
|
|
|
|
}
|
2011-06-05 18:13:21 +00:00
|
|
|
if (mIsAutoUpdating && mDeparturesAdapter != null) {
|
2012-04-13 22:37:19 +00:00
|
|
|
mDeparturesAdapter.incrementRefreshCounter();
|
2011-06-05 18:13:21 +00:00
|
|
|
mDeparturesAdapter.notifyDataSetChanged();
|
2011-05-23 18:59:34 +00:00
|
|
|
}
|
2012-04-28 15:44:53 +00:00
|
|
|
mLastAutoUpdate = now;
|
2011-05-23 18:59:34 +00:00
|
|
|
if (hasWindowFocus()) {
|
2012-07-15 17:00:39 +00:00
|
|
|
postDelayed(AUTO_UPDATE_RUNNABLE, 1000);
|
2011-05-23 18:59:34 +00:00
|
|
|
} else {
|
|
|
|
mIsAutoUpdating = false;
|
|
|
|
}
|
|
|
|
}
|
2011-05-27 21:06:58 +00:00
|
|
|
|
2012-07-15 17:00:39 +00:00
|
|
|
private boolean postDelayed(Runnable runnable, long delayMillis) {
|
|
|
|
return mEmptyView.postDelayed(runnable, delayMillis);
|
|
|
|
}
|
|
|
|
|
2011-05-27 21:06:58 +00:00
|
|
|
@Override
|
|
|
|
public boolean onCreateOptionsMenu(Menu menu) {
|
|
|
|
MenuInflater inflater = getMenuInflater();
|
|
|
|
inflater.inflate(R.menu.route_menu, menu);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
|
|
int itemId = item.getItemId();
|
|
|
|
if (itemId == R.id.view_on_bart_site_button) {
|
|
|
|
startActivity(new Intent(
|
|
|
|
Intent.ACTION_VIEW,
|
|
|
|
Uri.parse("http://m.bart.gov/schedules/qp_results.aspx?type=departure&date=today&time="
|
|
|
|
+ DateFormat.format("h:mmaa",
|
2011-11-05 18:51:20 +00:00
|
|
|
System.currentTimeMillis())
|
|
|
|
+ "&orig="
|
2011-05-27 21:06:58 +00:00
|
|
|
+ mOrigin.abbreviation
|
|
|
|
+ "&dest="
|
|
|
|
+ mDestination.abbreviation)));
|
|
|
|
return true;
|
2011-07-16 18:01:02 +00:00
|
|
|
} else if (itemId == R.id.view_system_map_button) {
|
2011-11-05 18:51:20 +00:00
|
|
|
startActivity(new Intent(this, ViewMapActivity.class));
|
2011-07-16 18:01:02 +00:00
|
|
|
return true;
|
2011-05-27 21:06:58 +00:00
|
|
|
} else {
|
|
|
|
return super.onOptionsItemSelected(item);
|
|
|
|
}
|
|
|
|
}
|
2011-05-23 18:59:34 +00:00
|
|
|
}
|