2011-05-23 18:59:34 +00:00
|
|
|
package com.dougkeen.bart;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
import android.app.ListActivity;
|
|
|
|
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;
|
|
|
|
import android.util.Log;
|
|
|
|
import android.view.Menu;
|
|
|
|
import android.view.MenuInflater;
|
|
|
|
import android.view.MenuItem;
|
2011-05-23 18:59:34 +00:00
|
|
|
import android.widget.ArrayAdapter;
|
|
|
|
import android.widget.TextView;
|
|
|
|
import android.widget.Toast;
|
|
|
|
|
|
|
|
import com.dougkeen.bart.GetRealTimeArrivalsTask.Params;
|
|
|
|
import com.dougkeen.bart.data.Arrival;
|
|
|
|
import com.dougkeen.bart.data.RealTimeArrivals;
|
2011-06-01 03:42:32 +00:00
|
|
|
import com.dougkeen.bart.data.RoutesColumns;
|
2011-05-23 18:59:34 +00:00
|
|
|
|
|
|
|
public class ViewArrivalsActivity extends ListActivity {
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
private ArrayAdapter<Arrival> mArrivalsAdapter;
|
|
|
|
|
|
|
|
private TextView mListTitleView;
|
|
|
|
|
|
|
|
private AsyncTask<Params, Integer, RealTimeArrivals> mGetArrivalsTask;
|
|
|
|
|
|
|
|
private boolean mIsAutoUpdating = false;
|
|
|
|
|
|
|
|
private final Runnable AUTO_UPDATE_RUNNABLE = new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
runAutoUpdate();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
private PowerManager.WakeLock mWakeLock;
|
|
|
|
|
2011-05-27 21:06:58 +00:00
|
|
|
private boolean mFetchArrivalsOnNextFocus;
|
|
|
|
|
2011-05-23 18:59:34 +00:00
|
|
|
@Override
|
|
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
|
|
super.onCreate(savedInstanceState);
|
|
|
|
setContentView(R.layout.main);
|
|
|
|
|
|
|
|
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,
|
|
|
|
RoutesColumns.TO_STATION.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));
|
|
|
|
|
|
|
|
String header = mOrigin.name + " to " + mDestination.name;
|
|
|
|
|
|
|
|
mListTitleView = (TextView) findViewById(R.id.listTitle);
|
|
|
|
mListTitleView.setText(header);
|
|
|
|
((TextView) findViewById(android.R.id.empty))
|
|
|
|
.setText(R.string.arrival_wait_message);
|
|
|
|
|
2011-05-27 21:06:58 +00:00
|
|
|
mArrivalsAdapter = new ArrivalArrayAdapter(this,
|
|
|
|
R.layout.arrival_listing);
|
|
|
|
if (savedInstanceState != null
|
|
|
|
&& savedInstanceState.containsKey("arrivals")) {
|
|
|
|
for (Parcelable arrival : savedInstanceState
|
|
|
|
.getParcelableArray("arrivals")) {
|
|
|
|
mArrivalsAdapter.add((Arrival) arrival);
|
|
|
|
}
|
|
|
|
}
|
2011-05-23 18:59:34 +00:00
|
|
|
setListAdapter(mArrivalsAdapter);
|
|
|
|
|
2011-05-27 21:06:58 +00:00
|
|
|
mFetchArrivalsOnNextFocus = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onDestroy() {
|
|
|
|
if (mGetArrivalsTask != null) {
|
|
|
|
mGetArrivalsTask.cancel(true);
|
|
|
|
}
|
|
|
|
super.onDestroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onSaveInstanceState(Bundle outState) {
|
|
|
|
super.onSaveInstanceState(outState);
|
|
|
|
Arrival[] arrivals = new Arrival[mArrivalsAdapter.getCount()];
|
|
|
|
for (int i = mArrivalsAdapter.getCount() - 1; i >= 0; i--) {
|
|
|
|
arrivals[i] = mArrivalsAdapter.getItem(i);
|
|
|
|
}
|
|
|
|
outState.putParcelableArray("arrivals", arrivals);
|
2011-05-23 18:59:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onWindowFocusChanged(boolean hasFocus) {
|
|
|
|
super.onWindowFocusChanged(hasFocus);
|
|
|
|
if (hasFocus) {
|
2011-05-27 21:06:58 +00:00
|
|
|
if (mFetchArrivalsOnNextFocus) {
|
|
|
|
fetchLatestArrivals();
|
|
|
|
mFetchArrivalsOnNextFocus = false;
|
|
|
|
}
|
2011-05-23 18:59:34 +00:00
|
|
|
PowerManager powerManaer = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
|
|
|
mWakeLock = powerManaer.newWakeLock(
|
|
|
|
PowerManager.SCREEN_DIM_WAKE_LOCK, "ViewArrivalsActivity");
|
|
|
|
mWakeLock.acquire();
|
2011-05-27 21:06:58 +00:00
|
|
|
if (mArrivalsAdapter != null && !mArrivalsAdapter.isEmpty()) {
|
|
|
|
mIsAutoUpdating = true;
|
|
|
|
}
|
|
|
|
runAutoUpdate();
|
2011-05-23 18:59:34 +00:00
|
|
|
} else if (mWakeLock != null) {
|
|
|
|
mWakeLock.release();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void fetchLatestArrivals() {
|
2011-05-27 21:06:58 +00:00
|
|
|
if (!hasWindowFocus())
|
|
|
|
return;
|
2011-06-01 03:42:32 +00:00
|
|
|
if (mGetArrivalsTask != null
|
|
|
|
&& mGetArrivalsTask.getStatus()
|
|
|
|
.equals(AsyncTask.Status.RUNNING)) {
|
|
|
|
// Don't overlap fetches
|
|
|
|
return;
|
|
|
|
}
|
2011-05-27 21:06:58 +00:00
|
|
|
|
2011-05-23 18:59:34 +00:00
|
|
|
mGetArrivalsTask = new GetRealTimeArrivalsTask() {
|
|
|
|
@Override
|
|
|
|
public void onResult(RealTimeArrivals result) {
|
2011-06-01 03:42:32 +00:00
|
|
|
Log.i(Constants.TAG, "Processing data from server");
|
2011-05-23 18:59:34 +00:00
|
|
|
processLatestArrivals(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
|
|
|
|
public void onNetworkError(IOException e) {
|
2011-06-01 03:42:32 +00:00
|
|
|
Log.w(Constants.TAG, e.getMessage());
|
|
|
|
Toast.makeText(ViewArrivalsActivity.this,
|
|
|
|
R.string.could_not_connect,
|
|
|
|
Toast.LENGTH_LONG).show();
|
|
|
|
((TextView) findViewById(android.R.id.empty))
|
|
|
|
.setText(R.string.could_not_connect);
|
|
|
|
|
2011-05-23 18:59:34 +00:00
|
|
|
}
|
|
|
|
};
|
2011-06-01 03:42:32 +00:00
|
|
|
Log.i(Constants.TAG, "Fetching data from server");
|
2011-05-23 18:59:34 +00:00
|
|
|
mGetArrivalsTask.execute(new GetRealTimeArrivalsTask.Params(mOrigin,
|
|
|
|
mDestination));
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void processLatestArrivals(RealTimeArrivals result) {
|
2011-05-25 15:31:55 +00:00
|
|
|
if (result.getArrivals().isEmpty()) {
|
|
|
|
((TextView) findViewById(android.R.id.empty))
|
|
|
|
.setText(R.string.no_data_message);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-05-27 21:06:58 +00:00
|
|
|
boolean needsBetterAccuracy = false;
|
2011-05-23 18:59:34 +00:00
|
|
|
Arrival firstArrival = null;
|
|
|
|
final List<Arrival> arrivals = result.getArrivals();
|
|
|
|
if (mArrivalsAdapter.getCount() > 0) {
|
|
|
|
int adapterIndex = -1;
|
|
|
|
for (Arrival arrival : arrivals) {
|
|
|
|
adapterIndex++;
|
|
|
|
Arrival existingArrival = null;
|
|
|
|
if (adapterIndex < mArrivalsAdapter.getCount()) {
|
|
|
|
existingArrival = mArrivalsAdapter.getItem(adapterIndex);
|
|
|
|
}
|
|
|
|
while (existingArrival != null
|
|
|
|
&& !arrival.equals(existingArrival)) {
|
|
|
|
mArrivalsAdapter.remove(existingArrival);
|
|
|
|
if (adapterIndex < mArrivalsAdapter.getCount()) {
|
|
|
|
existingArrival = mArrivalsAdapter
|
|
|
|
.getItem(adapterIndex);
|
|
|
|
} else {
|
|
|
|
existingArrival = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (existingArrival != null) {
|
|
|
|
existingArrival.mergeEstimate(arrival);
|
|
|
|
} else {
|
|
|
|
mArrivalsAdapter.add(arrival);
|
|
|
|
existingArrival = arrival;
|
|
|
|
}
|
|
|
|
if (firstArrival == null) {
|
|
|
|
firstArrival = existingArrival;
|
|
|
|
}
|
2011-05-27 21:06:58 +00:00
|
|
|
if (existingArrival.getUncertaintySeconds() > UNCERTAINTY_THRESHOLD) {
|
|
|
|
needsBetterAccuracy = true;
|
|
|
|
}
|
2011-05-23 18:59:34 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (Arrival arrival : arrivals) {
|
|
|
|
if (firstArrival == null) {
|
|
|
|
firstArrival = arrival;
|
|
|
|
}
|
|
|
|
mArrivalsAdapter.add(arrival);
|
|
|
|
}
|
2011-05-27 21:06:58 +00:00
|
|
|
needsBetterAccuracy = true;
|
2011-05-23 18:59:34 +00:00
|
|
|
}
|
|
|
|
mArrivalsAdapter.notifyDataSetChanged();
|
|
|
|
|
|
|
|
if (hasWindowFocus() && firstArrival != null) {
|
2011-05-27 21:06:58 +00:00
|
|
|
if (needsBetterAccuracy
|
|
|
|
|| firstArrival.hasArrived()) {
|
2011-05-23 18:59:34 +00:00
|
|
|
// Get more data in 20s
|
|
|
|
mListTitleView.postDelayed(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
fetchLatestArrivals();
|
|
|
|
}
|
|
|
|
}, 20000);
|
2011-06-01 03:42:32 +00:00
|
|
|
Log.i(Constants.TAG, "Scheduled another data fetch in 20s");
|
2011-05-23 18:59:34 +00:00
|
|
|
} else {
|
|
|
|
// Get more when next train arrives
|
2011-05-27 21:06:58 +00:00
|
|
|
final int interval = firstArrival.getMinSecondsLeft() * 1000;
|
2011-05-23 18:59:34 +00:00
|
|
|
mListTitleView.postDelayed(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
fetchLatestArrivals();
|
|
|
|
}
|
2011-05-27 21:06:58 +00:00
|
|
|
}, interval);
|
2011-06-01 03:42:32 +00:00
|
|
|
Log.i(Constants.TAG, "Scheduled another data fetch in "
|
|
|
|
+ interval / 1000
|
2011-05-27 21:06:58 +00:00
|
|
|
+ "s");
|
2011-05-23 18:59:34 +00:00
|
|
|
}
|
|
|
|
if (!mIsAutoUpdating) {
|
|
|
|
mIsAutoUpdating = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mIsAutoUpdating = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void runAutoUpdate() {
|
2011-05-27 21:06:58 +00:00
|
|
|
if (mIsAutoUpdating && mArrivalsAdapter != null) {
|
2011-05-23 18:59:34 +00:00
|
|
|
mArrivalsAdapter.notifyDataSetChanged();
|
|
|
|
}
|
|
|
|
if (hasWindowFocus()) {
|
|
|
|
mListTitleView.postDelayed(AUTO_UPDATE_RUNNABLE, 1000);
|
|
|
|
} else {
|
|
|
|
mIsAutoUpdating = false;
|
|
|
|
}
|
|
|
|
}
|
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",
|
|
|
|
System.currentTimeMillis()) + "&orig="
|
|
|
|
+ mOrigin.abbreviation
|
|
|
|
+ "&dest="
|
|
|
|
+ mDestination.abbreviation)));
|
|
|
|
mFetchArrivalsOnNextFocus = true;
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return super.onOptionsItemSelected(item);
|
|
|
|
}
|
|
|
|
}
|
2011-05-23 18:59:34 +00:00
|
|
|
}
|