diff --git a/.hgignore b/.hgignore index 60aefa5..0c40d8c 100644 --- a/.hgignore +++ b/.hgignore @@ -1,3 +1,9 @@ syntax: glob -bin/* \ No newline at end of file +bin/* +syntax: regexp +^app_icon\.psd$ +syntax: regexp +^bart-train\.jpg$ +syntax: regexp +^colorscheme\.jpg$ \ No newline at end of file diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 6032628..9f860fa 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1,6 +1,6 @@ @@ -9,7 +9,7 @@ - @@ -28,7 +28,7 @@ - + diff --git a/res/drawable-hdpi/icon.png b/res/drawable-hdpi/icon.png index 8074c4c..a5c7cbd 100644 Binary files a/res/drawable-hdpi/icon.png and b/res/drawable-hdpi/icon.png differ diff --git a/res/drawable-ldpi/icon.png b/res/drawable-ldpi/icon.png index 1095584..2929050 100644 Binary files a/res/drawable-ldpi/icon.png and b/res/drawable-ldpi/icon.png differ diff --git a/res/drawable-mdpi/icon.png b/res/drawable-mdpi/icon.png index a07c69f..b8194a9 100644 Binary files a/res/drawable-mdpi/icon.png and b/res/drawable-mdpi/icon.png differ diff --git a/res/drawable/basic_rectangle.xml b/res/drawable/basic_rectangle.xml index 63a7114..abb8504 100644 --- a/res/drawable/basic_rectangle.xml +++ b/res/drawable/basic_rectangle.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/res/drawable/bike.png b/res/drawable/bike.png new file mode 100644 index 0000000..3730150 Binary files /dev/null and b/res/drawable/bike.png differ diff --git a/res/drawable/xfer.png b/res/drawable/xfer.png new file mode 100644 index 0000000..eca7e6c Binary files /dev/null and b/res/drawable/xfer.png differ diff --git a/res/layout/add_favorite.xml b/res/layout/add_favorite.xml index c4766af..641154c 100644 --- a/res/layout/add_favorite.xml +++ b/res/layout/add_favorite.xml @@ -5,7 +5,7 @@ + android:text="@string/add_route" android:paddingBottom="10dp" /> - + + - - + + + + \ No newline at end of file diff --git a/res/layout/simple_spinner_item.xml b/res/layout/simple_spinner_item.xml index feb52ff..d20985a 100644 --- a/res/layout/simple_spinner_item.xml +++ b/res/layout/simple_spinner_item.xml @@ -3,6 +3,6 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:paddingTop="15dip" android:paddingBottom="15dip" android:paddingLeft="5dip" android:paddingRight="5dip" android:id="@android:id/text1" - android:textColor="#cccccc" android:ellipsize="marquee" + android:ellipsize="marquee" android:textColor="@color/black" android:singleLine="true"> diff --git a/res/menu/favorite_context_menu.xml b/res/menu/route_context_menu.xml similarity index 100% rename from res/menu/favorite_context_menu.xml rename to res/menu/route_context_menu.xml diff --git a/res/menu/route_menu.xml b/res/menu/route_menu.xml new file mode 100644 index 0000000..9e77838 --- /dev/null +++ b/res/menu/route_menu.xml @@ -0,0 +1,5 @@ + + + + diff --git a/res/menu/favorites_menu.xml b/res/menu/routes_list_menu.xml similarity index 64% rename from res/menu/favorites_menu.xml rename to res/menu/routes_list_menu.xml index b233735..c9d1c67 100644 --- a/res/menu/favorites_menu.xml +++ b/res/menu/routes_list_menu.xml @@ -1,5 +1,5 @@ - diff --git a/res/values/strings.xml b/res/values/strings.xml index 608bde4..8b9b596 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -2,23 +2,24 @@ Bart Catcher Favorite Routes - Press the menu button and select "Add - favorite" to - add a route - Add favorite - Add a favorite route + Press the menu button and select \"Add route\" to add + a route + Add a route Origin Destination Save - The origin and destination stations must be different + The origin and destination stations must be + different You must select a destination station You must select an origin station - Please wait while real time arrival data is loaded... - No arrival data is currently available for this route + Please wait while real time arrival data is + loaded... + No arrival data is currently available for this + route View View arrivals Delete Yes Cancel - + View details on BART site diff --git a/res/values/styles.xml b/res/values/styles.xml index ceb242f..22dd137 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -21,8 +21,18 @@ + + + + + diff --git a/src/com/dougkeen/bart/AddFavoriteActivity.java b/src/com/dougkeen/bart/AddRouteActivity.java similarity index 87% rename from src/com/dougkeen/bart/AddFavoriteActivity.java rename to src/com/dougkeen/bart/AddRouteActivity.java index b7a5e54..d813dba 100644 --- a/src/com/dougkeen/bart/AddFavoriteActivity.java +++ b/src/com/dougkeen/bart/AddRouteActivity.java @@ -1,6 +1,6 @@ package com.dougkeen.bart; -import com.dougkeen.bart.data.FavoritesColumns; +import com.dougkeen.bart.data.RoutesColumns; import android.app.Activity; import android.content.ContentValues; @@ -14,7 +14,7 @@ import android.widget.Spinner; import android.widget.SpinnerAdapter; import android.widget.Toast; -public class AddFavoriteActivity extends Activity { +public class AddRouteActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { @@ -80,8 +80,8 @@ public class AddFavoriteActivity extends Activity { } ContentValues values = new ContentValues(); - values.put(FavoritesColumns.FROM_STATION.string, origin.abbreviation); - values.put(FavoritesColumns.TO_STATION.string, destination.abbreviation); + values.put(RoutesColumns.FROM_STATION.string, origin.abbreviation); + values.put(RoutesColumns.TO_STATION.string, destination.abbreviation); Uri newUri = getContentResolver().insert( Constants.FAVORITE_CONTENT_URI, values); diff --git a/src/com/dougkeen/bart/ArrivalArrayAdapter.java b/src/com/dougkeen/bart/ArrivalArrayAdapter.java new file mode 100644 index 0000000..fdfa095 --- /dev/null +++ b/src/com/dougkeen/bart/ArrivalArrayAdapter.java @@ -0,0 +1,90 @@ +package com.dougkeen.bart; + +import java.util.List; + +import android.content.Context; +import android.graphics.Color; +import android.graphics.drawable.GradientDrawable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.dougkeen.bart.data.Arrival; + +public class ArrivalArrayAdapter extends ArrayAdapter { + + public ArrivalArrayAdapter(Context context, int textViewResourceId, + Arrival[] objects) { + super(context, textViewResourceId, objects); + } + + public ArrivalArrayAdapter(Context context, int resource, + int textViewResourceId, Arrival[] objects) { + super(context, resource, textViewResourceId, objects); + } + + public ArrivalArrayAdapter(Context context, int resource, + int textViewResourceId, List objects) { + super(context, resource, textViewResourceId, objects); + } + + public ArrivalArrayAdapter(Context context, int resource, + int textViewResourceId) { + super(context, resource, textViewResourceId); + } + + public ArrivalArrayAdapter(Context context, int textViewResourceId, + List objects) { + super(context, textViewResourceId, objects); + } + + public ArrivalArrayAdapter(Context context, int textViewResourceId) { + super(context, textViewResourceId); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view; + if (convertView != null && convertView instanceof RelativeLayout) { + view = convertView; + } else { + LayoutInflater inflater = LayoutInflater.from(getContext()); + view = inflater.inflate(R.layout.arrival_listing, parent, false); + } + + Arrival arrival = getItem(position); + ((TextView) view.findViewById(R.id.destinationText)).setText(arrival + .getDestination().toString()); + ((TextView) view.findViewById(R.id.trainLengthText)).setText(arrival + .getTrainLengthText()); + ImageView colorBar = (ImageView) view + .findViewById(R.id.destinationColorBar); + ((GradientDrawable) colorBar.getDrawable()).setColor(Color + .parseColor(arrival.getDestinationColor())); + ((TextView) view.findViewById(R.id.countdown)).setText(arrival + .getCountdownText()); + ((TextView) view.findViewById(R.id.uncertainty)).setText(arrival + .getUncertaintyText()); + if (arrival.isBikeAllowed()) { + ((ImageView) view.findViewById(R.id.bikeIcon)) + .setVisibility(View.VISIBLE); + } else { + ((ImageView) view.findViewById(R.id.bikeIcon)) + .setVisibility(View.INVISIBLE); + } + if (arrival.getRequiresTransfer()) { + ((ImageView) view.findViewById(R.id.xferIcon)) + .setVisibility(View.VISIBLE); + } else { + ((ImageView) view.findViewById(R.id.xferIcon)) + .setVisibility(View.INVISIBLE); + } + + return view; + } + +} diff --git a/src/com/dougkeen/bart/GetRealTimeArrivalsTask.java b/src/com/dougkeen/bart/GetRealTimeArrivalsTask.java index 7cbb075..d4712f1 100644 --- a/src/com/dougkeen/bart/GetRealTimeArrivalsTask.java +++ b/src/com/dougkeen/bart/GetRealTimeArrivalsTask.java @@ -40,7 +40,11 @@ public abstract class GetRealTimeArrivalsTask extends throw new RuntimeException(e); } - return getArrivalsFromNetwork(params, sourceUrl, 0); + if (!isCancelled()) { + return getArrivalsFromNetwork(params, sourceUrl, 0); + } else { + return null; + } } private RealTimeArrivals getArrivalsFromNetwork(Params params, @@ -48,6 +52,9 @@ public abstract class GetRealTimeArrivalsTask extends try { EtdContentHandler handler = new EtdContentHandler(params.origin, params.destination, mRoutes); + if (isCancelled()) { + return null; + } Xml.parse(sourceUrl.openStream(), Xml.findEncodingByName("UTF-8"), handler); final RealTimeArrivals realTimeArrivals = handler diff --git a/src/com/dougkeen/bart/Route.java b/src/com/dougkeen/bart/Route.java index 3ac9c00..8d100f5 100644 --- a/src/com/dougkeen/bart/Route.java +++ b/src/com/dougkeen/bart/Route.java @@ -5,6 +5,7 @@ public class Route { private Station destination; private Line line; private boolean requiresTransfer; + private Station transferStation; private String direction; public Station getOrigin() { @@ -39,6 +40,14 @@ public class Route { this.requiresTransfer = requiresTransfer; } + public Station getTransferStation() { + return transferStation; + } + + public void setTransferStation(Station transferStation) { + this.transferStation = transferStation; + } + public String getDirection() { return direction; } @@ -58,6 +67,8 @@ public class Route { builder.append(line); builder.append(", requiresTransfer="); builder.append(requiresTransfer); + builder.append(", transferStation="); + builder.append(transferStation); builder.append(", direction="); builder.append(direction); builder.append("]"); diff --git a/src/com/dougkeen/bart/FavoritesDashboardActivity.java b/src/com/dougkeen/bart/RoutesListActivity.java similarity index 89% rename from src/com/dougkeen/bart/FavoritesDashboardActivity.java rename to src/com/dougkeen/bart/RoutesListActivity.java index 3a23a02..f8fa9b8 100644 --- a/src/com/dougkeen/bart/FavoritesDashboardActivity.java +++ b/src/com/dougkeen/bart/RoutesListActivity.java @@ -24,9 +24,9 @@ import android.widget.SimpleCursorAdapter.ViewBinder; import android.widget.TextView; import com.dougkeen.bart.data.CursorUtils; -import com.dougkeen.bart.data.FavoritesColumns; +import com.dougkeen.bart.data.RoutesColumns; -public class FavoritesDashboardActivity extends ListActivity { +public class RoutesListActivity extends ListActivity { private static final int DIALOG_DELETE_EVENT = 0; protected Cursor mQuery; @@ -41,22 +41,17 @@ public class FavoritesDashboardActivity extends ListActivity { super.onCreate(savedInstanceState); setContentView(R.layout.main); - ((TextView) findViewById(R.id.listTitle)) - .setText(R.string.favorite_routes); - ((TextView) findViewById(android.R.id.empty)) - .setText(R.string.empty_favorites_list_message); - mQuery = managedQuery(Constants.FAVORITE_CONTENT_URI, new String[] { - FavoritesColumns._ID.string, - FavoritesColumns.FROM_STATION.string, - FavoritesColumns.TO_STATION.string }, null, null, - FavoritesColumns._ID.string); + RoutesColumns._ID.string, + RoutesColumns.FROM_STATION.string, + RoutesColumns.TO_STATION.string }, null, null, + RoutesColumns._ID.string); SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.favorite_listing, mQuery, - new String[] { FavoritesColumns.FROM_STATION.string, - FavoritesColumns.TO_STATION.string }, + new String[] { RoutesColumns.FROM_STATION.string, + RoutesColumns.TO_STATION.string }, new int[] { R.id.originText, R.id.destinationText }); adapter.setViewBinder(new ViewBinder() { @@ -74,10 +69,19 @@ public class FavoritesDashboardActivity extends ListActivity { registerForContextMenu(getListView()); } + @Override + protected void onResume() { + super.onResume(); + ((TextView) findViewById(R.id.listTitle)) + .setText(R.string.favorite_routes); + ((TextView) findViewById(android.R.id.empty)) + .setText(R.string.empty_favorites_list_message); + } + @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.favorites_menu, menu); + inflater.inflate(R.menu.routes_list_menu, menu); return true; } @@ -105,15 +109,15 @@ public class FavoritesDashboardActivity extends ListActivity { super.onCreateContextMenu(menu, v, menuInfo); MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.favorite_context_menu, menu); + inflater.inflate(R.menu.route_context_menu, menu); AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; CursorWrapper item = (CursorWrapper) getListAdapter().getItem( info.position); Station orig = Station.getByAbbreviation(CursorUtils.getString(item, - FavoritesColumns.FROM_STATION)); + RoutesColumns.FROM_STATION)); Station dest = Station.getByAbbreviation(CursorUtils.getString(item, - FavoritesColumns.TO_STATION)); + RoutesColumns.TO_STATION)); mCurrentlySelectedRouteName = orig.name + " - " + dest.name; menu.setHeaderTitle(mCurrentlySelectedRouteName); } diff --git a/src/com/dougkeen/bart/Station.java b/src/com/dougkeen/bart/Station.java index 984928d..fe5120d 100644 --- a/src/com/dougkeen/bart/Station.java +++ b/src/com/dougkeen/bart/Station.java @@ -115,10 +115,11 @@ public enum Station { } public List getRoutesForDestination(Station dest) { - return getRoutesForDestination(dest, false); + return getRoutesForDestination(dest, null); } - public List getRoutesForDestination(Station dest, boolean isTransfer) { + public List getRoutesForDestination(Station dest, + Station transferStation) { if (dest == null) return null; Boolean isNorth = null; @@ -140,7 +141,7 @@ public enum Station { route.setDestination(dest); route.setDirection(isNorth ? "n" : "s"); route.setLine(line); - if (isTransfer || line.requiresTransfer) { + if (transferStation != null || line.requiresTransfer) { route.setTransfer(true); } else { route.setTransfer(false); @@ -149,11 +150,14 @@ public enum Station { } if (isNorth == null) { if (outboundTransferStation != null) { - returnList.addAll(getOutboundTransferStation() - .getRoutesForDestination(dest, true)); + returnList + .addAll(getOutboundTransferStation() + .getRoutesForDestination(dest, + getOutboundTransferStation())); } else { returnList.addAll(getRoutesForDestination(dest - .getInboundTransferStation(), true)); + .getInboundTransferStation(), dest + .getInboundTransferStation())); } } return returnList; diff --git a/src/com/dougkeen/bart/ViewArrivalsActivity.java b/src/com/dougkeen/bart/ViewArrivalsActivity.java index 15c373c..66ec9cf 100644 --- a/src/com/dougkeen/bart/ViewArrivalsActivity.java +++ b/src/com/dougkeen/bart/ViewArrivalsActivity.java @@ -10,18 +10,29 @@ import android.database.Cursor; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; +import android.os.Parcelable; import android.os.PowerManager; +import android.text.format.DateFormat; +import android.util.Log; +import android.util.TimeFormatException; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; 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.FavoritesColumns; +import com.dougkeen.bart.data.RoutesColumns; import com.dougkeen.bart.data.RealTimeArrivals; public class ViewArrivalsActivity extends ListActivity { + private static final String TAG = "BartCatcher"; + + private static final int UNCERTAINTY_THRESHOLD = 17; + private Uri mUri; private Station mOrigin; @@ -44,9 +55,10 @@ public class ViewArrivalsActivity extends ListActivity { private PowerManager.WakeLock mWakeLock; + private boolean mFetchArrivalsOnNextFocus; + @Override protected void onCreate(Bundle savedInstanceState) { - // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.main); @@ -59,8 +71,8 @@ public class ViewArrivalsActivity extends ListActivity { } Cursor cursor = managedQuery(mUri, new String[] { - FavoritesColumns.FROM_STATION.string, - FavoritesColumns.TO_STATION.string }, null, null, null); + RoutesColumns.FROM_STATION.string, + RoutesColumns.TO_STATION.string }, null, null, null); if (!cursor.moveToFirst()) { throw new IllegalStateException("URI not found: " + mUri.toString()); @@ -75,31 +87,69 @@ public class ViewArrivalsActivity extends ListActivity { ((TextView) findViewById(android.R.id.empty)) .setText(R.string.arrival_wait_message); - mArrivalsAdapter = new ArrayAdapter( - this, R.layout.simple_spinner_item); + mArrivalsAdapter = new ArrivalArrayAdapter(this, + R.layout.arrival_listing); + if (savedInstanceState != null + && savedInstanceState.containsKey("arrivals")) { + for (Parcelable arrival : savedInstanceState + .getParcelableArray("arrivals")) { + mArrivalsAdapter.add((Arrival) arrival); + } + } setListAdapter(mArrivalsAdapter); - fetchLatestArrivals(); + 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); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus) { + if (mFetchArrivalsOnNextFocus) { + fetchLatestArrivals(); + mFetchArrivalsOnNextFocus = false; + } PowerManager powerManaer = (PowerManager) getSystemService(Context.POWER_SERVICE); mWakeLock = powerManaer.newWakeLock( PowerManager.SCREEN_DIM_WAKE_LOCK, "ViewArrivalsActivity"); mWakeLock.acquire(); + if (mArrivalsAdapter != null && !mArrivalsAdapter.isEmpty()) { + mIsAutoUpdating = true; + } + runAutoUpdate(); } else if (mWakeLock != null) { mWakeLock.release(); } } private void fetchLatestArrivals() { + if (!hasWindowFocus()) + return; + mGetArrivalsTask = new GetRealTimeArrivalsTask() { @Override public void onResult(RealTimeArrivals result) { + Log.i(TAG, "Processing data from server"); processLatestArrivals(result); + Log.i(TAG, "Done processing data from server"); } @Override @@ -108,6 +158,7 @@ public class ViewArrivalsActivity extends ListActivity { Toast.LENGTH_SHORT).show(); } }; + Log.i(TAG, "Fetching data from server"); mGetArrivalsTask.execute(new GetRealTimeArrivalsTask.Params(mOrigin, mDestination)); } @@ -119,6 +170,7 @@ public class ViewArrivalsActivity extends ListActivity { return; } + boolean needsBetterAccuracy = false; Arrival firstArrival = null; final List arrivals = result.getArrivals(); if (mArrivalsAdapter.getCount() > 0) { @@ -148,6 +200,9 @@ public class ViewArrivalsActivity extends ListActivity { if (firstArrival == null) { firstArrival = existingArrival; } + if (existingArrival.getUncertaintySeconds() > UNCERTAINTY_THRESHOLD) { + needsBetterAccuracy = true; + } } } else { for (Arrival arrival : arrivals) { @@ -156,12 +211,13 @@ public class ViewArrivalsActivity extends ListActivity { } mArrivalsAdapter.add(arrival); } + needsBetterAccuracy = true; } mArrivalsAdapter.notifyDataSetChanged(); if (hasWindowFocus() && firstArrival != null) { - if (firstArrival.getUncertaintySeconds() > 17 - || firstArrival.getMinutes() == 0) { + if (needsBetterAccuracy + || firstArrival.hasArrived()) { // Get more data in 20s mListTitleView.postDelayed(new Runnable() { @Override @@ -169,27 +225,29 @@ public class ViewArrivalsActivity extends ListActivity { fetchLatestArrivals(); } }, 20000); + Log.i(TAG, "Scheduled another data fetch in 20s"); } else { // Get more when next train arrives + final int interval = firstArrival.getMinSecondsLeft() * 1000; mListTitleView.postDelayed(new Runnable() { @Override public void run() { fetchLatestArrivals(); } - }, firstArrival.getMinSecondsLeft() * 1000); + }, interval); + Log.i(TAG, "Scheduled another data fetch in " + interval / 1000 + + "s"); } if (!mIsAutoUpdating) { mIsAutoUpdating = true; - runAutoUpdate(); } } else { mIsAutoUpdating = false; } - } private void runAutoUpdate() { - if (mIsAutoUpdating) { + if (mIsAutoUpdating && mArrivalsAdapter != null) { mArrivalsAdapter.notifyDataSetChanged(); } if (hasWindowFocus()) { @@ -198,4 +256,30 @@ public class ViewArrivalsActivity extends ListActivity { mIsAutoUpdating = false; } } + + @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); + } + } } diff --git a/src/com/dougkeen/bart/data/Arrival.java b/src/com/dougkeen/bart/data/Arrival.java index 291e21c..0ba3cc4 100644 --- a/src/com/dougkeen/bart/data/Arrival.java +++ b/src/com/dougkeen/bart/data/Arrival.java @@ -1,8 +1,11 @@ package com.dougkeen.bart.data; +import android.os.Parcel; +import android.os.Parcelable; + import com.dougkeen.bart.Station; -public class Arrival implements Comparable { +public class Arrival implements Parcelable, Comparable { public Arrival() { super(); } @@ -20,6 +23,10 @@ public class Arrival implements Comparable { this.minutes = minutes; } + public Arrival(Parcel in) { + readFromParcel(in); + } + private Station destination; private String destinationColor; private String platform; @@ -93,6 +100,10 @@ public class Arrival implements Comparable { this.trainLength = trainLength; } + public String getTrainLengthText() { + return trainLength + " car train"; + } + public boolean getRequiresTransfer() { return requiresTransfer; } @@ -142,14 +153,24 @@ public class Arrival implements Comparable { .currentTimeMillis()) / 1000); } + public boolean hasArrived() { + return getMinutes() == 0 || getMeanSecondsLeft() < 0; + } + public void calculateEstimates(long originalEstimateTime) { setMinEstimate(originalEstimateTime + (getMinutes() * 60 * 1000)); setMaxEstimate(getMinEstimate() + (59 * 1000)); } public void mergeEstimate(Arrival arrival) { - setMinEstimate(Math.max(getMinEstimate(), arrival.getMinEstimate())); - setMaxEstimate(Math.min(getMaxEstimate(), arrival.getMaxEstimate())); + final long newMin = Math + .max(getMinEstimate(), arrival.getMinEstimate()); + final long newMax = Math + .min(getMaxEstimate(), arrival.getMaxEstimate()); + if (newMax > newMin) { // We can never have 0 or negative uncertainty + setMinEstimate(newMin); + setMaxEstimate(newMax); + } } @Override @@ -193,27 +214,79 @@ public class Arrival implements Comparable { return delta > -60000 && delta < 60000; } - @Override - public String toString() { + public String getCountdownText() { StringBuilder builder = new StringBuilder(); - builder.append(destination); - if(requiresTransfer) { - builder.append(" (w/ xfer)"); - } - builder.append(", "); - builder.append(trainLength); int secondsLeft = getMeanSecondsLeft(); - if (getMinutes() == 0 || secondsLeft < 0) { - builder.append(" car train has arrived"); + if (hasArrived()) { + builder.append("Arrived"); } else { - builder.append(" car train in "); builder.append(secondsLeft / 60); builder.append("m, "); builder.append(secondsLeft % 60); - builder.append("s, ±"); - builder.append(getUncertaintySeconds()); builder.append("s"); } return builder.toString(); } -} + + public String getUncertaintyText() { + if (hasArrived()) { + return ""; + } else { + return "(±" + getUncertaintySeconds() + "s)"; + } + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(destination); + if (requiresTransfer) { + builder.append(" (w/ xfer)"); + } + builder.append(", "); + builder.append(getCountdownText()); + return builder.toString(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(destination.abbreviation); + dest.writeString(destinationColor); + dest.writeString(platform); + dest.writeString(direction); + dest.writeByte((byte) (bikeAllowed ? 1 : 0)); + dest.writeInt(trainLength); + dest.writeByte((byte) (requiresTransfer ? 1 : 0)); + dest.writeInt(minutes); + dest.writeLong(minEstimate); + dest.writeLong(maxEstimate); + } + + private void readFromParcel(Parcel in) { + destination = Station.getByAbbreviation(in.readString()); + destinationColor = in.readString(); + platform = in.readString(); + direction = in.readString(); + bikeAllowed = in.readByte() != 0; + trainLength = in.readInt(); + requiresTransfer = in.readByte() != 0; + minutes = in.readInt(); + minEstimate = in.readLong(); + maxEstimate = in.readLong(); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public Arrival createFromParcel(Parcel in) { + return new Arrival(in); + } + + public Arrival[] newArray(int size) { + return new Arrival[size]; + } + }; +} \ No newline at end of file diff --git a/src/com/dougkeen/bart/data/BartContentProvider.java b/src/com/dougkeen/bart/data/BartContentProvider.java index fb43be6..b3307c3 100644 --- a/src/com/dougkeen/bart/data/BartContentProvider.java +++ b/src/com/dougkeen/bart/data/BartContentProvider.java @@ -26,7 +26,7 @@ public class BartContentProvider extends ContentProvider { /** * The default sort order for events */ - private static final String DEFAULT_SORT_ORDER = FavoritesColumns.FROM_STATION.string + private static final String DEFAULT_SORT_ORDER = RoutesColumns.FROM_STATION.string + " DESC"; static { @@ -35,12 +35,12 @@ public class BartContentProvider extends ContentProvider { sUriMatcher.addURI(Constants.AUTHORITY, "favorites/#", FAVORITE_ID); sFavoritesProjectionMap = new HashMap(); - sFavoritesProjectionMap.put(FavoritesColumns._ID.string, - FavoritesColumns._ID.string); - sFavoritesProjectionMap.put(FavoritesColumns.FROM_STATION.string, - FavoritesColumns.FROM_STATION.string); - sFavoritesProjectionMap.put(FavoritesColumns.TO_STATION.string, - FavoritesColumns.TO_STATION.string); + sFavoritesProjectionMap.put(RoutesColumns._ID.string, + RoutesColumns._ID.string); + sFavoritesProjectionMap.put(RoutesColumns.FROM_STATION.string, + RoutesColumns.FROM_STATION.string); + sFavoritesProjectionMap.put(RoutesColumns.TO_STATION.string, + RoutesColumns.TO_STATION.string); } private DatabaseHelper mDatabaseHelper; @@ -81,7 +81,7 @@ public class BartContentProvider extends ContentProvider { } else if (match == FAVORITE_ID) { qb.setTables(DatabaseHelper.FAVORITES_TABLE_NAME); qb.setProjectionMap(sFavoritesProjectionMap); - qb.appendWhere(FavoritesColumns._ID + " = " + qb.appendWhere(RoutesColumns._ID + " = " + uri.getPathSegments().get(1)); } else { throw new IllegalArgumentException("Unknown URI " + uri); @@ -121,12 +121,12 @@ public class BartContentProvider extends ContentProvider { long rowId = -1; Cursor cursor = db .query(DatabaseHelper.FAVORITES_TABLE_NAME, - new String[] { FavoritesColumns._ID.string }, - FavoritesColumns.FROM_STATION + "=? AND " - + FavoritesColumns.TO_STATION + "=?", + new String[] { RoutesColumns._ID.string }, + RoutesColumns.FROM_STATION + "=? AND " + + RoutesColumns.TO_STATION + "=?", new String[] { - values.getAsString(FavoritesColumns.FROM_STATION.string), - values.getAsString(FavoritesColumns.TO_STATION.string) }, + values.getAsString(RoutesColumns.FROM_STATION.string), + values.getAsString(RoutesColumns.TO_STATION.string) }, null, null, null); @@ -139,7 +139,7 @@ public class BartContentProvider extends ContentProvider { } if (rowId < 0) { rowId = db.insert(DatabaseHelper.FAVORITES_TABLE_NAME, - FavoritesColumns.FROM_STATION.string, values); + RoutesColumns.FROM_STATION.string, values); } if (rowId > 0) { Uri eventUri = ContentUris.withAppendedId( @@ -174,7 +174,7 @@ public class BartContentProvider extends ContentProvider { } else if (match == FAVORITE_ID) { String favoriteId = uri.getPathSegments().get(1); count = db.delete(DatabaseHelper.FAVORITES_TABLE_NAME, - FavoritesColumns._ID + " = " + RoutesColumns._ID + " = " + favoriteId + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); diff --git a/src/com/dougkeen/bart/data/CursorUtils.java b/src/com/dougkeen/bart/data/CursorUtils.java index 5288f72..868ab1d 100644 --- a/src/com/dougkeen/bart/data/CursorUtils.java +++ b/src/com/dougkeen/bart/data/CursorUtils.java @@ -13,7 +13,7 @@ public final class CursorUtils { } } - public static final String getString(Cursor cursor, FavoritesColumns column) { + public static final String getString(Cursor cursor, RoutesColumns column) { return cursor.getString(cursor.getColumnIndex(column.string)); } diff --git a/src/com/dougkeen/bart/data/DatabaseHelper.java b/src/com/dougkeen/bart/data/DatabaseHelper.java index a9decb5..9c53e4d 100644 --- a/src/com/dougkeen/bart/data/DatabaseHelper.java +++ b/src/com/dougkeen/bart/data/DatabaseHelper.java @@ -18,9 +18,9 @@ public class DatabaseHelper extends SQLiteOpenHelper { @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + FAVORITES_TABLE_NAME + " (" + - FavoritesColumns._ID.getColumnDef() + " PRIMARY KEY, " + - FavoritesColumns.FROM_STATION.getColumnDef() + ", " + - FavoritesColumns.TO_STATION.getColumnDef() + ");"); + RoutesColumns._ID.getColumnDef() + " PRIMARY KEY, " + + RoutesColumns.FROM_STATION.getColumnDef() + ", " + + RoutesColumns.TO_STATION.getColumnDef() + ");"); } @Override diff --git a/src/com/dougkeen/bart/data/FavoritesColumns.java b/src/com/dougkeen/bart/data/RoutesColumns.java similarity index 77% rename from src/com/dougkeen/bart/data/FavoritesColumns.java rename to src/com/dougkeen/bart/data/RoutesColumns.java index fc3d53e..435fb59 100644 --- a/src/com/dougkeen/bart/data/FavoritesColumns.java +++ b/src/com/dougkeen/bart/data/RoutesColumns.java @@ -1,12 +1,12 @@ package com.dougkeen.bart.data; -public enum FavoritesColumns { +public enum RoutesColumns { _ID("_id", "INTEGER"), FROM_STATION("FROM_STATION", "TEXT"), TO_STATION("TO_STATION", "TEXT"); // This class cannot be instantiated - private FavoritesColumns(String string, String type) { + private RoutesColumns(String string, String type) { this.string = string; this.sqliteType = type; } diff --git a/xfer.svg b/xfer.svg new file mode 100644 index 0000000..60929ef --- /dev/null +++ b/xfer.svg @@ -0,0 +1,109 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + XFER + +