Added bike and xfer icons.

Now more well-behaved when switching orientations.
Added option to view routes on m.bart.gov

--HG--
rename : res/menu/favorite_context_menu.xml => res/menu/route_context_menu.xml
rename : res/menu/favorites_menu.xml => res/menu/routes_list_menu.xml
rename : src/com/dougkeen/bart/AddFavoriteActivity.java => src/com/dougkeen/bart/AddRouteActivity.java
rename : src/com/dougkeen/bart/FavoritesDashboardActivity.java => src/com/dougkeen/bart/RoutesListActivity.java
rename : src/com/dougkeen/bart/data/FavoritesColumns.java => src/com/dougkeen/bart/data/RoutesColumns.java
This commit is contained in:
dkeen 2011-05-27 14:06:58 -07:00
parent 1f4f5938b5
commit 2bb1b5d035
29 changed files with 534 additions and 102 deletions

View File

@ -1,3 +1,9 @@
syntax: glob
bin/*
bin/*
syntax: regexp
^app_icon\.psd$
syntax: regexp
^bart-train\.jpg$
syntax: regexp
^colorscheme\.jpg$

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.dougkeen.bart" android:versionCode="2"
package="com.dougkeen.bart" android:versionCode="3"
android:versionName="1.0">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
@ -9,7 +9,7 @@
<application android:icon="@drawable/icon" android:label="@string/app_name"
android:debuggable="true">
<activity android:name=".FavoritesDashboardActivity"
<activity android:name="RoutesListActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -28,7 +28,7 @@
<data android:mimeType="vnd.android.cursor.item/com.dougkeen.bart.favorite" />
</intent-filter>
</activity>
<activity android:name=".AddFavoriteActivity" android:label="@string/app_name">
<activity android:name="AddRouteActivity" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.INSERT" />
<category android:name="android.intent.category.DEFAULT" />

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:height="15dp" android:width="60dp" />
<size android:height="45dp" android:width="15dp" />
</shape>

BIN
res/drawable/bike.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1008 B

BIN
res/drawable/xfer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

View File

@ -5,7 +5,7 @@
<TextView android:id="@+id/form_header" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:textSize="24dp"
android:textStyle="bold" android:layout_alignParentTop="true"
android:text="@string/add_favorite_route" android:paddingBottom="10dp" />
android:text="@string/add_route" android:paddingBottom="10dp" />
<TextView android:id="@+id/origin_label" android:text="@string/origin"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:textSize="15dp" android:layout_below="@+id/form_header"

View File

@ -1,12 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<TextView android:id="@+id/destinationText" style="@style/ArrivalDestinationText"
android:layout_alignParentTop="true" android:layout_alignParentLeft="true" />
<ImageView android:id="@+id/destinationColorBar" android:src="@drawable/basic_rectangle"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentLeft="true" android:layout_centerVertical="true" />
<TextView android:id="@+id/destinationText" style="@style/ArrivalDestinationText"
android:layout_alignParentTop="true" android:layout_toRightOf="@id/destinationColorBar" />
<TextView android:id="@+id/trainLengthText" style="@style/ArrivalTrainLengthText"
android:layout_toRightOf="@id/destinationColorBar"
android:layout_below="@id/destinationText" />
<TextView android:layout_centerVertical="true"
android:layout_alignParentRight="true" android:id="@+id/countdown"
<TextView android:layout_alignParentRight="true" android:id="@+id/countdown"
style="@style/ArrivalCountdownText" />
</RelativeLayout>
<TextView android:layout_alignParentRight="true" android:id="@+id/uncertainty"
android:layout_below="@id/countdown" style="@style/ArrivalUncertaintyText" />
<ImageView android:id="@+id/bikeIcon" android:src="@drawable/bike"
android:layout_alignParentTop="true" android:layout_alignParentRight="true"
style="@style/BikeIcon" />
<ImageView android:id="@+id/xferIcon" android:src="@drawable/xfer"
android:layout_below="@id/bikeIcon" android:layout_alignParentRight="true"
style="@style/XferIcon" />
</RelativeLayout>

View File

@ -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">
</TextView>

5
res/menu/route_menu.xml Normal file
View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="@string/view_on_bart_site" android:icon="@android:drawable/ic_menu_view"
android:id="@+id/view_on_bart_site_button"></item>
</menu>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="@string/add_favorite" android:icon="@android:drawable/ic_menu_add"
<item android:title="@string/add_route" android:icon="@android:drawable/ic_menu_add"
android:id="@+id/add_favorite_menu_button"></item>
</menu>

View File

@ -2,23 +2,24 @@
<resources>
<string name="app_name">Bart Catcher</string>
<string name="favorite_routes">Favorite Routes</string>
<string name="empty_favorites_list_message">Press the menu button and select "Add
favorite" to
add a route</string>
<string name="add_favorite">Add favorite</string>
<string name="add_favorite_route">Add a favorite route</string>
<string name="empty_favorites_list_message">Press the menu button and select \"Add route\" to add
a route</string>
<string name="add_route">Add a route</string>
<string name="origin">Origin</string>
<string name="destination">Destination</string>
<string name="save">Save</string>
<string name="error_matching_origin_and_destination">The origin and destination stations must be different</string>
<string name="error_matching_origin_and_destination">The origin and destination stations must be
different</string>
<string name="error_null_destination">You must select a destination station</string>
<string name="error_null_origin">You must select an origin station</string>
<string name="arrival_wait_message">Please wait while real time arrival data is loaded...</string>
<string name="no_data_message">No arrival data is currently available for this route</string>
<string name="arrival_wait_message">Please wait while real time arrival data is
loaded...</string>
<string name="no_data_message">No arrival data is currently available for this
route</string>
<string name="view">View</string>
<string name="view_arrivals">View arrivals</string>
<string name="delete">Delete</string>
<string name="yes">Yes</string>
<string name="cancel">Cancel</string>
<string name="view_on_bart_site">View details on BART site</string>
</resources>

View File

@ -21,8 +21,18 @@
<style name="ArrivalDestinationText">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textSize">20dp</item>
<item name="android:textSize">22dp</item>
<item name="android:textStyle">bold</item>
<item name="android:singleLine">true</item>
<item name="android:layout_marginLeft">3dp</item>
</style>
<style name="ArrivalTrainLengthText">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textSize">18dp</item>
<item name="android:singleLine">true</item>
<item name="android:layout_marginLeft">3dp</item>
</style>
<style name="ArrivalCountdownText">
@ -31,4 +41,22 @@
<item name="android:textSize">20dp</item>
<item name="android:singleLine">true</item>
</style>
<style name="ArrivalUncertaintyText">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textSize">16dp</item>
<item name="android:singleLine">true</item>
</style>
<style name="BikeIcon">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginRight">100dp</item>
<item name="android:layout_marginTop">5dp</item>
</style>
<style name="XferIcon">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginRight">97dp</item>
<item name="android:layout_marginTop">5dp</item>
</style>
</resources>

View File

@ -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);

View File

@ -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<Arrival> {
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<Arrival> 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<Arrival> 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;
}
}

View File

@ -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

View File

@ -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("]");

View File

@ -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);
}

View File

@ -115,10 +115,11 @@ public enum Station {
}
public List<Route> getRoutesForDestination(Station dest) {
return getRoutesForDestination(dest, false);
return getRoutesForDestination(dest, null);
}
public List<Route> getRoutesForDestination(Station dest, boolean isTransfer) {
public List<Route> 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;

View File

@ -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<Arrival>(
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<Arrival> 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);
}
}
}

View File

@ -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<Arrival> {
public class Arrival implements Parcelable, Comparable<Arrival> {
public Arrival() {
super();
}
@ -20,6 +23,10 @@ public class Arrival implements Comparable<Arrival> {
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<Arrival> {
this.trainLength = trainLength;
}
public String getTrainLengthText() {
return trainLength + " car train";
}
public boolean getRequiresTransfer() {
return requiresTransfer;
}
@ -142,14 +153,24 @@ public class Arrival implements Comparable<Arrival> {
.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<Arrival> {
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<Arrival> CREATOR = new Parcelable.Creator<Arrival>() {
public Arrival createFromParcel(Parcel in) {
return new Arrival(in);
}
public Arrival[] newArray(int size) {
return new Arrival[size];
}
};
}

View File

@ -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<String, String>();
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);

View File

@ -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));
}

View File

@ -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

View File

@ -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;
}

109
xfer.svg Normal file
View File

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32px"
height="32px"
id="svg2383"
sodipodi:version="0.32"
inkscape:version="0.46"
sodipodi:docname="xfer.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape">
<defs
id="defs2385">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 16 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="32 : 16 : 1"
inkscape:persp3d-origin="16 : 10.666667 : 1"
id="perspective2391" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="22.395604"
inkscape:cx="14.100126"
inkscape:cy="15.964773"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="1920"
inkscape:window-height="1125"
inkscape:window-x="1911"
inkscape:window-y="-9" />
<metadata
id="metadata2388">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<path
style="fill:#29716d;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 6.117272,8.0220801 L 0.58047106,15.702158 L 26.255152,15.702158 L 26.255152,11.460255 L 6.117272,11.504906 L 6.117272,8.0220801 z"
id="path2393"
sodipodi:nodetypes="cccccc"
inkscape:export-filename="C:\Users\dkeen\Workspaces\AndroidExperiments\DontMissTheBart\res\drawable\xfer.png"
inkscape:export-xdpi="117.18631"
inkscape:export-ydpi="117.18631" />
<path
style="fill:#143635;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 25.964917,23.382236 L 31.501718,15.702158 L 5.8270365,15.702158 L 5.8270365,19.944061 L 25.964917,19.89941 L 25.964917,23.382236 z"
id="path3165"
sodipodi:nodetypes="cccccc"
inkscape:export-filename="C:\Users\dkeen\Workspaces\AndroidExperiments\DontMissTheBart\res\drawable\xfer.png"
inkscape:export-xdpi="117.18631"
inkscape:export-ydpi="117.18631" />
<flowRoot
xml:space="preserve"
id="flowRoot3167"
style="font-size:4;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
transform="translate(8.0372916,9.5554467)"
inkscape:export-filename="C:\Users\dkeen\Workspaces\AndroidExperiments\DontMissTheBart\res\drawable\xfer.png"
inkscape:export-xdpi="117.18631"
inkscape:export-ydpi="117.18631"><flowRegion
id="flowRegion3169"><rect
id="rect3171"
width="17.63739"
height="10.359176"
x="3.2595682"
y="4.2266922"
style="font-size:4px;fill:#000000" /></flowRegion><flowPara
id="flowPara3173"
style="font-size:4" /></flowRoot> <text
xml:space="preserve"
style="font-size:4px;font-style:normal;font-weight:normal;line-height:125%;fill:#f2f3ed;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="10.001963"
y="18.068693"
id="text3175"
sodipodi:linespacing="125%"
inkscape:export-xdpi="117.18631"
inkscape:export-ydpi="117.18631"
inkscape:export-filename="C:\Users\dkeen\Workspaces\AndroidExperiments\DontMissTheBart\res\drawable\xfer.png"><tspan
sodipodi:role="line"
id="tspan3177"
x="10.001963"
y="18.068693"
style="font-size:6px;font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Helvetica Inserat LT Std;-inkscape-font-specification:Helvetica Inserat LT Std Ultra-Bold;fill:#f2f3ed">XFER</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB