diff --git a/res/values/strings.xml b/res/values/strings.xml index 41f894b..d88faf4 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -42,5 +42,6 @@ Silence alarm Cancel alarm Set alarm + Leaving \ No newline at end of file diff --git a/src/com/dougkeen/bart/BartRunnerApplication.java b/src/com/dougkeen/bart/BartRunnerApplication.java index ab73720..458a786 100644 --- a/src/com/dougkeen/bart/BartRunnerApplication.java +++ b/src/com/dougkeen/bart/BartRunnerApplication.java @@ -31,6 +31,18 @@ public class BartRunnerApplication extends Application { private MediaPlayer mAlarmMediaPlayer; + private static Context context; + + @Override + public void onCreate() { + super.onCreate(); + context = getApplicationContext(); + } + + public static Context getAppContext() { + return context; + } + public boolean shouldPlayAlarmRingtone() { return mPlayAlarmRingtone; } diff --git a/src/com/dougkeen/bart/activities/RoutesListActivity.java b/src/com/dougkeen/bart/activities/RoutesListActivity.java index 74735c6..65ae665 100644 --- a/src/com/dougkeen/bart/activities/RoutesListActivity.java +++ b/src/com/dougkeen/bart/activities/RoutesListActivity.java @@ -18,11 +18,10 @@ import android.widget.AdapterView; import android.widget.Button; import android.widget.ListAdapter; -import com.WazaBe.HoloEverywhere.widget.TextView; - import com.WazaBe.HoloEverywhere.app.AlertDialog; import com.WazaBe.HoloEverywhere.app.DialogFragment; import com.WazaBe.HoloEverywhere.sherlock.SActivity; +import com.WazaBe.HoloEverywhere.widget.TextView; import com.actionbarsherlock.view.ActionMode; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuInflater; diff --git a/src/com/dougkeen/bart/controls/CountdownTextView.java b/src/com/dougkeen/bart/controls/CountdownTextView.java index e573567..3183938 100644 --- a/src/com/dougkeen/bart/controls/CountdownTextView.java +++ b/src/com/dougkeen/bart/controls/CountdownTextView.java @@ -1,57 +1,57 @@ -package com.dougkeen.bart.controls; - -import android.content.Context; -import android.util.AttributeSet; -import android.widget.TextView; - -import com.dougkeen.bart.model.TextProvider; - -public class CountdownTextView extends TextView implements - Ticker.TickSubscriber { - - private TextProvider mTextProvider; - private int mTickInterval; - - public CountdownTextView(Context context) { - super(context); - } - - public CountdownTextView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - setInstanceVarsFromAttrs(attrs); - } - - public CountdownTextView(Context context, AttributeSet attrs) { - super(context, attrs); - setInstanceVarsFromAttrs(attrs); - } - - private void setInstanceVarsFromAttrs(AttributeSet attrs) { - int tickInterval = attrs.getAttributeIntValue( - "http://schemas.android.com/apk/res/com.dougkeen.bart", - "tickInterval", 0); - if (tickInterval > 0) { - setTickInterval(tickInterval); - } - } - - public void setTextProvider(TextProvider provider) { - mTextProvider = provider; - Ticker.getInstance().addSubscriber(this, getContext()); - } - - @Override - public int getTickInterval() { - return mTickInterval; - } - - public void setTickInterval(int tickInterval) { - this.mTickInterval = tickInterval; - } - - @Override - public void onTick(long tickNumber) { - setText(mTextProvider.getText(tickNumber)); - } - -} +package com.dougkeen.bart.controls; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.TextView; + +import com.dougkeen.bart.model.TextProvider; + +public class CountdownTextView extends TextView implements + Ticker.TickSubscriber { + + private TextProvider mTextProvider; + private int mTickInterval; + + public CountdownTextView(Context context) { + super(context); + } + + public CountdownTextView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setInstanceVarsFromAttrs(attrs); + } + + public CountdownTextView(Context context, AttributeSet attrs) { + super(context, attrs); + setInstanceVarsFromAttrs(attrs); + } + + private void setInstanceVarsFromAttrs(AttributeSet attrs) { + int tickInterval = attrs.getAttributeIntValue( + "http://schemas.android.com/apk/res/com.dougkeen.bart", + "tickInterval", 0); + if (tickInterval > 0) { + setTickInterval(tickInterval); + } + } + + public void setTextProvider(TextProvider provider) { + mTextProvider = provider; + Ticker.getInstance().addSubscriber(this, getContext()); + } + + @Override + public int getTickInterval() { + return mTickInterval; + } + + public void setTickInterval(int tickInterval) { + this.mTickInterval = tickInterval; + } + + @Override + public void onTick(long tickNumber) { + setText(mTextProvider.getText(tickNumber)); + } + +} diff --git a/src/com/dougkeen/bart/controls/SwipeHelper.java b/src/com/dougkeen/bart/controls/SwipeHelper.java index 490d21b..f242038 100644 --- a/src/com/dougkeen/bart/controls/SwipeHelper.java +++ b/src/com/dougkeen/bart/controls/SwipeHelper.java @@ -86,8 +86,8 @@ public class SwipeHelper implements View.OnTouchListener { private float mTranslationX; /** - * The callback interface used by {@link SwipeHelper} to inform its - * client about a successful dismissal of the view for which it was created. + * The callback interface used by {@link SwipeHelper} to inform its client + * about a successful dismissal of the view for which it was created. */ public interface OnDismissCallback { /** diff --git a/src/com/dougkeen/bart/controls/Ticker.java b/src/com/dougkeen/bart/controls/Ticker.java index da2c052..03ddee3 100644 --- a/src/com/dougkeen/bart/controls/Ticker.java +++ b/src/com/dougkeen/bart/controls/Ticker.java @@ -1,114 +1,114 @@ -package com.dougkeen.bart.controls; - -import java.util.Iterator; -import java.util.WeakHashMap; - -import android.content.Context; -import android.os.Handler; - -public class Ticker { - public static interface TickSubscriber { - int getTickInterval(); - - void onTick(long mTickCount); - } - - private static Ticker sInstance; - - private WeakHashMap mSubscribers; - - private WeakHashMap mTickerHosts; - - private TickerEngine mEngine; - - private static class TickerEngine implements Runnable { - - private static final int TICK_INTERVAL_MILLIS = 1000; - private Ticker publisher; - private Handler mHandler; - private boolean mPendingRequest = false; - private boolean mForceStop = false; - private long mTickCount = 0; - - public TickerEngine(Ticker publisher) { - this.publisher = publisher; - this.mHandler = new Handler(); - } - - @Override - public void run() { - mPendingRequest = false; - if (mForceStop) { - mForceStop = false; - return; - } - - long startTimeNanos = System.nanoTime(); - Iterator iterator = publisher.mSubscribers.keySet() - .iterator(); - boolean stillHasListeners = false; - while (iterator.hasNext()) { - TickSubscriber subscriber = iterator.next(); - if (subscriber == null) { - continue; - } - - stillHasListeners = true; - if (subscriber.getTickInterval() > 0 - && mTickCount % subscriber.getTickInterval() == 0) - subscriber.onTick(mTickCount); - } - long endTimeNanos = System.nanoTime(); - - if (stillHasListeners && !mPendingRequest) { - mHandler.postDelayed(this, TICK_INTERVAL_MILLIS - - ((endTimeNanos - startTimeNanos) / 1000000)); - mPendingRequest = true; - mTickCount++; - } else { - mPendingRequest = false; - } - } - - public boolean isOn() { - return mPendingRequest; - } - - public void stop() { - mForceStop = true; - } - - }; - - public synchronized static Ticker getInstance() { - if (sInstance == null) { - sInstance = new Ticker(); - } - return sInstance; - } - - public void addSubscriber(TickSubscriber subscriber, Context host) { - if (!mSubscribers.containsKey(subscriber) && subscriber != null) { - mSubscribers.put(subscriber, null); - startTicking(host); - } - } - - private Ticker() { - mSubscribers = new WeakHashMap(); - mTickerHosts = new WeakHashMap(); - mEngine = new TickerEngine(this); - } - - public void startTicking(Context host) { - mTickerHosts.put(host, true); - if (!mEngine.isOn()) - mEngine.run(); - } - - public void stopTicking(Context host) { - mTickerHosts.remove(host); - if (mEngine.isOn() && mTickerHosts.isEmpty()) - mEngine.stop(); - } -} +package com.dougkeen.bart.controls; + +import java.util.Iterator; +import java.util.WeakHashMap; + +import android.content.Context; +import android.os.Handler; + +public class Ticker { + public static interface TickSubscriber { + int getTickInterval(); + + void onTick(long mTickCount); + } + + private static Ticker sInstance; + + private WeakHashMap mSubscribers; + + private WeakHashMap mTickerHosts; + + private TickerEngine mEngine; + + private static class TickerEngine implements Runnable { + + private static final int TICK_INTERVAL_MILLIS = 1000; + private Ticker publisher; + private Handler mHandler; + private boolean mPendingRequest = false; + private boolean mForceStop = false; + private long mTickCount = 0; + + public TickerEngine(Ticker publisher) { + this.publisher = publisher; + this.mHandler = new Handler(); + } + + @Override + public void run() { + mPendingRequest = false; + if (mForceStop) { + mForceStop = false; + return; + } + + long startTimeNanos = System.nanoTime(); + Iterator iterator = publisher.mSubscribers.keySet() + .iterator(); + boolean stillHasListeners = false; + while (iterator.hasNext()) { + TickSubscriber subscriber = iterator.next(); + if (subscriber == null) { + continue; + } + + stillHasListeners = true; + if (subscriber.getTickInterval() > 0 + && mTickCount % subscriber.getTickInterval() == 0) + subscriber.onTick(mTickCount); + } + long endTimeNanos = System.nanoTime(); + + if (stillHasListeners && !mPendingRequest) { + mHandler.postDelayed(this, TICK_INTERVAL_MILLIS + - ((endTimeNanos - startTimeNanos) / 1000000)); + mPendingRequest = true; + mTickCount++; + } else { + mPendingRequest = false; + } + } + + public boolean isOn() { + return mPendingRequest; + } + + public void stop() { + mForceStop = true; + } + + }; + + public synchronized static Ticker getInstance() { + if (sInstance == null) { + sInstance = new Ticker(); + } + return sInstance; + } + + public void addSubscriber(TickSubscriber subscriber, Context host) { + if (!mSubscribers.containsKey(subscriber) && subscriber != null) { + mSubscribers.put(subscriber, null); + startTicking(host); + } + } + + private Ticker() { + mSubscribers = new WeakHashMap(); + mTickerHosts = new WeakHashMap(); + mEngine = new TickerEngine(this); + } + + public void startTicking(Context host) { + mTickerHosts.put(host, true); + if (!mEngine.isOn()) + mEngine.run(); + } + + public void stopTicking(Context host) { + mTickerHosts.remove(host); + if (mEngine.isOn() && mTickerHosts.isEmpty()) + mEngine.stop(); + } +} diff --git a/src/com/dougkeen/bart/controls/TimedTextSwitcher.java b/src/com/dougkeen/bart/controls/TimedTextSwitcher.java index a82c052..60716ba 100644 --- a/src/com/dougkeen/bart/controls/TimedTextSwitcher.java +++ b/src/com/dougkeen/bart/controls/TimedTextSwitcher.java @@ -1,67 +1,67 @@ -package com.dougkeen.bart.controls; - -import org.apache.commons.lang3.StringUtils; - -import android.content.Context; -import android.util.AttributeSet; -import android.widget.TextSwitcher; - -import com.dougkeen.bart.model.TextProvider; - -public class TimedTextSwitcher extends TextSwitcher implements - Ticker.TickSubscriber { - - public TimedTextSwitcher(Context context, AttributeSet attrs) { - super(context, attrs); - setInstanceVarsFromAttrs(attrs); - } - - public TimedTextSwitcher(Context context) { - super(context); - } - - private void setInstanceVarsFromAttrs(AttributeSet attrs) { - int tickInterval = attrs.getAttributeIntValue( - "http://schemas.android.com/apk/res/com.dougkeen.bart", - "tickInterval", 0); - if (tickInterval > 0) { - setTickInterval(tickInterval); - } - } - - private int mTickInterval; - private TextProvider mTextProvider; - - @Override - public int getTickInterval() { - return mTickInterval; - } - - public void setTickInterval(int tickInterval) { - this.mTickInterval = tickInterval; - } - - public void setTextProvider(TextProvider textProvider) { - mTextProvider = textProvider; - Ticker.getInstance().addSubscriber(this, getContext()); - } - - private CharSequence mLastText; - - @Override - public void setCurrentText(CharSequence text) { - mLastText = text; - super.setCurrentText(text); - } - - @Override - public void onTick(long tickNumber) { - String text = mTextProvider.getText(tickNumber); - if (StringUtils.isNotBlank(text) - && !StringUtils.equalsIgnoreCase(text, mLastText)) { - mLastText = text; - setText(text); - } - } - -} +package com.dougkeen.bart.controls; + +import org.apache.commons.lang3.StringUtils; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.TextSwitcher; + +import com.dougkeen.bart.model.TextProvider; + +public class TimedTextSwitcher extends TextSwitcher implements + Ticker.TickSubscriber { + + public TimedTextSwitcher(Context context, AttributeSet attrs) { + super(context, attrs); + setInstanceVarsFromAttrs(attrs); + } + + public TimedTextSwitcher(Context context) { + super(context); + } + + private void setInstanceVarsFromAttrs(AttributeSet attrs) { + int tickInterval = attrs.getAttributeIntValue( + "http://schemas.android.com/apk/res/com.dougkeen.bart", + "tickInterval", 0); + if (tickInterval > 0) { + setTickInterval(tickInterval); + } + } + + private int mTickInterval; + private TextProvider mTextProvider; + + @Override + public int getTickInterval() { + return mTickInterval; + } + + public void setTickInterval(int tickInterval) { + this.mTickInterval = tickInterval; + } + + public void setTextProvider(TextProvider textProvider) { + mTextProvider = textProvider; + Ticker.getInstance().addSubscriber(this, getContext()); + } + + private CharSequence mLastText; + + @Override + public void setCurrentText(CharSequence text) { + mLastText = text; + super.setCurrentText(text); + } + + @Override + public void onTick(long tickNumber) { + String text = mTextProvider.getText(tickNumber); + if (StringUtils.isNotBlank(text) + && !StringUtils.equalsIgnoreCase(text, mLastText)) { + mLastText = text; + setText(text); + } + } + +} diff --git a/src/com/dougkeen/bart/controls/YourTrainLayout.java b/src/com/dougkeen/bart/controls/YourTrainLayout.java index a042c61..2b7a748 100644 --- a/src/com/dougkeen/bart/controls/YourTrainLayout.java +++ b/src/com/dougkeen/bart/controls/YourTrainLayout.java @@ -95,7 +95,7 @@ public class YourTrainLayout extends RelativeLayout implements Checkable { @Override public String getText(long tickNumber) { if (boardedDeparture.hasDeparted()) { - return "Departed"; + return getContext().getString(R.string.leaving); } else { return "Leaves in " + boardedDeparture.getCountdownText() + " " + boardedDeparture.getUncertaintyText(); diff --git a/src/com/dougkeen/bart/data/BartContentProvider.java b/src/com/dougkeen/bart/data/BartContentProvider.java index 0e66b3e..dd5239a 100644 --- a/src/com/dougkeen/bart/data/BartContentProvider.java +++ b/src/com/dougkeen/bart/data/BartContentProvider.java @@ -1,271 +1,271 @@ -package com.dougkeen.bart.data; - -import java.util.HashMap; - -import android.content.ContentProvider; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.UriMatcher; -import android.database.Cursor; -import android.database.MatrixCursor; -import android.database.MatrixCursor.RowBuilder; -import android.database.SQLException; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteQueryBuilder; -import android.net.Uri; -import android.text.TextUtils; - -import com.dougkeen.bart.model.Constants; - -public class BartContentProvider extends ContentProvider { - - private static final UriMatcher sUriMatcher; - private static HashMap sFavoritesProjectionMap; - - private static final int FAVORITES = 1; - private static final int FAVORITE_ID = 2; - private static final int ARBITRARY_ROUTE = 3; - private static final int ARBITRARY_ROUTE_UNDEFINED = 4; - - /** - * The default sort order for events - */ - private static final String DEFAULT_SORT_ORDER = RoutesColumns.FROM_STATION.string - + " DESC"; - - static { - sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); - sUriMatcher.addURI(Constants.AUTHORITY, "favorites", FAVORITES); - sUriMatcher.addURI(Constants.AUTHORITY, "favorites/#", FAVORITE_ID); - sUriMatcher.addURI(Constants.AUTHORITY, "route/*/*", ARBITRARY_ROUTE); - sUriMatcher.addURI(Constants.AUTHORITY, "route", - ARBITRARY_ROUTE_UNDEFINED); - - sFavoritesProjectionMap = new HashMap(); - 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); - sFavoritesProjectionMap.put(RoutesColumns.FARE.string, - RoutesColumns.FARE.string); - sFavoritesProjectionMap.put(RoutesColumns.FARE_LAST_UPDATED.string, - RoutesColumns.FARE_LAST_UPDATED.string); - sFavoritesProjectionMap.put( - RoutesColumns.AVERAGE_TRIP_SAMPLE_COUNT.string, - RoutesColumns.AVERAGE_TRIP_SAMPLE_COUNT.string); - sFavoritesProjectionMap.put(RoutesColumns.AVERAGE_TRIP_LENGTH.string, - RoutesColumns.AVERAGE_TRIP_LENGTH.string); - } - - private DatabaseHelper mDatabaseHelper; - - @Override - public boolean onCreate() { - mDatabaseHelper = new DatabaseHelper(getContext()); - return true; - } - - @Override - public String getType(Uri uri) { - int match = sUriMatcher.match(uri); - if (match == FAVORITES) { - return Constants.FAVORITE_CONTENT_TYPE; - } else if (match == FAVORITE_ID) { - return Constants.FAVORITE_CONTENT_ITEM_TYPE; - } else if (match == ARBITRARY_ROUTE) { - return Constants.ARBITRARY_ROUTE_TYPE; - } else if (match == ARBITRARY_ROUTE_UNDEFINED) { - return Constants.ARBITRARY_ROUTE_UNDEFINED_TYPE; - } else { - throw new IllegalArgumentException("Unknown URI " + uri); - } - } - - @Override - public Cursor query(Uri uri, String[] projection, String selection, - String[] selectionArgs, String sortOrder) { - SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); - - SQLiteDatabase db = mDatabaseHelper.getReadableDatabase(); - - String orderBy = sortOrder; - - int match = sUriMatcher.match(uri); - - if (match == ARBITRARY_ROUTE) { - final String origin = uri.getPathSegments().get(1); - final String destination = uri.getPathSegments().get(2); - - qb.setTables(DatabaseHelper.FAVORITES_TABLE_NAME); - qb.setProjectionMap(sFavoritesProjectionMap); - qb.appendWhere(String.format("%s = '%s' AND %s = '%s'", - RoutesColumns.FROM_STATION, origin, - RoutesColumns.TO_STATION, destination)); - Cursor query = qb.query(db, projection, selection, selectionArgs, - null, null, sortOrder); - if (query.getCount() > 0) - return query; - - MatrixCursor returnCursor = new MatrixCursor(projection); - RowBuilder newRow = returnCursor.newRow(); - - for (String column : projection) { - if (column.equals(RoutesColumns.FROM_STATION.string)) { - newRow.add(origin); - } else if (column.equals(RoutesColumns.TO_STATION.string)) { - newRow.add(destination); - } else { - newRow.add(null); - } - } - - return returnCursor; - } else if (match == FAVORITE_ID) { - qb.setTables(DatabaseHelper.FAVORITES_TABLE_NAME); - qb.setProjectionMap(sFavoritesProjectionMap); - qb.appendWhere(RoutesColumns._ID + " = " - + uri.getPathSegments().get(1)); - } else if (match == FAVORITES) { - qb.setTables(DatabaseHelper.FAVORITES_TABLE_NAME); - qb.setProjectionMap(sFavoritesProjectionMap); - } else { - throw new IllegalArgumentException("Unknown URI " + uri); - } - - // If no sort order is specified use the default - if (TextUtils.isEmpty(orderBy)) { - orderBy = DEFAULT_SORT_ORDER; - } - - // Get the database and run the query - Cursor cursor = qb.query(db, projection, selection, selectionArgs, - null, null, orderBy); - - // Tell the cursor what uri to watch, so it knows when its source data - // changes - cursor.setNotificationUri(getContext().getContentResolver(), uri); - return cursor; - } - - @Override - public Uri insert(Uri uri, ContentValues initialValues) { - ContentValues values; - if (initialValues != null) { - values = new ContentValues(initialValues); - } else { - values = new ContentValues(); - } - - SQLiteDatabase db = mDatabaseHelper.getWritableDatabase(); - - // Validate the requested uri - int match = sUriMatcher.match(uri); - if (match == FAVORITES) { - long rowId = -1; - Cursor cursor = db - .query(DatabaseHelper.FAVORITES_TABLE_NAME, - new String[] { RoutesColumns._ID.string }, - RoutesColumns.FROM_STATION + "=? AND " - + RoutesColumns.TO_STATION + "=?", - new String[] { - values.getAsString(RoutesColumns.FROM_STATION.string), - values.getAsString(RoutesColumns.TO_STATION.string) }, - null, null, null); - try { - if (cursor.moveToFirst()) { - rowId = cursor.getLong(0); - } - } finally { - CursorUtils.closeCursorQuietly(cursor); - } - if (rowId < 0) { - rowId = db.insert(DatabaseHelper.FAVORITES_TABLE_NAME, - RoutesColumns.FROM_STATION.string, values); - } - if (rowId > 0) { - Uri eventUri = ContentUris.withAppendedId( - Constants.FAVORITE_CONTENT_URI, rowId); - getContext().getContentResolver().notifyChange(eventUri, null, - false); - return eventUri; - } - } else { - throw new IllegalArgumentException("Unknown URI " + uri); - } - - throw new SQLException("Failed to insert row into " + uri); - } - - @Override - public int update(Uri uri, ContentValues values, String where, - String[] whereArgs) { - SQLiteDatabase db = mDatabaseHelper.getWritableDatabase(); - - // Validate the requested uri - int match = sUriMatcher.match(uri); - if (match == FAVORITE_ID) { - String favoriteId = uri.getPathSegments().get(1); - int count = db.update( - DatabaseHelper.FAVORITES_TABLE_NAME, - values, - RoutesColumns._ID - + " = " - + favoriteId - + (!TextUtils.isEmpty(where) ? " AND (" + where - + ')' : ""), whereArgs); - getContext().getContentResolver().notifyChange(uri, null); - return count; - } else if (match == ARBITRARY_ROUTE) { - // Get the route with the origin and destination provided, and - // simply delegate to the previous log branch. If the given route - // doesn't exist, do nothing. - String origin = uri.getPathSegments().get(1); - String destination = uri.getPathSegments().get(2); - - Cursor query = db.query(DatabaseHelper.FAVORITES_TABLE_NAME, - new String[] { RoutesColumns._ID.string }, - RoutesColumns.FROM_STATION.string + "=? AND " - + RoutesColumns.TO_STATION.string + "=?", - new String[] { origin, destination }, null, null, null); - - try { - if (query.moveToFirst()) { - return update(ContentUris.withAppendedId( - Constants.FAVORITE_CONTENT_URI, query.getLong(0)), - values, where, whereArgs); - } - } finally { - CursorUtils.closeCursorQuietly(query); - } - } - return 0; - } - - @Override - public int delete(Uri uri, String where, String[] whereArgs) { - // TODO: Sync with REST service? - SQLiteDatabase db = mDatabaseHelper.getWritableDatabase(); - int count; - int match = sUriMatcher.match(uri); - if (match == FAVORITES) { - count = db.delete(DatabaseHelper.FAVORITES_TABLE_NAME, where, - whereArgs); - } else if (match == FAVORITE_ID) { - String favoriteId = uri.getPathSegments().get(1); - count = db.delete( - DatabaseHelper.FAVORITES_TABLE_NAME, - RoutesColumns._ID - + " = " - + favoriteId - + (!TextUtils.isEmpty(where) ? " AND (" + where - + ')' : ""), whereArgs); - } else { - throw new IllegalArgumentException("Unknown URI " + uri); - } - - getContext().getContentResolver().notifyChange(uri, null); - return count; - } -} +package com.dougkeen.bart.data; + +import java.util.HashMap; + +import android.content.ContentProvider; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.UriMatcher; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.database.MatrixCursor.RowBuilder; +import android.database.SQLException; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteQueryBuilder; +import android.net.Uri; +import android.text.TextUtils; + +import com.dougkeen.bart.model.Constants; + +public class BartContentProvider extends ContentProvider { + + private static final UriMatcher sUriMatcher; + private static HashMap sFavoritesProjectionMap; + + private static final int FAVORITES = 1; + private static final int FAVORITE_ID = 2; + private static final int ARBITRARY_ROUTE = 3; + private static final int ARBITRARY_ROUTE_UNDEFINED = 4; + + /** + * The default sort order for events + */ + private static final String DEFAULT_SORT_ORDER = RoutesColumns.FROM_STATION.string + + " DESC"; + + static { + sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); + sUriMatcher.addURI(Constants.AUTHORITY, "favorites", FAVORITES); + sUriMatcher.addURI(Constants.AUTHORITY, "favorites/#", FAVORITE_ID); + sUriMatcher.addURI(Constants.AUTHORITY, "route/*/*", ARBITRARY_ROUTE); + sUriMatcher.addURI(Constants.AUTHORITY, "route", + ARBITRARY_ROUTE_UNDEFINED); + + sFavoritesProjectionMap = new HashMap(); + 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); + sFavoritesProjectionMap.put(RoutesColumns.FARE.string, + RoutesColumns.FARE.string); + sFavoritesProjectionMap.put(RoutesColumns.FARE_LAST_UPDATED.string, + RoutesColumns.FARE_LAST_UPDATED.string); + sFavoritesProjectionMap.put( + RoutesColumns.AVERAGE_TRIP_SAMPLE_COUNT.string, + RoutesColumns.AVERAGE_TRIP_SAMPLE_COUNT.string); + sFavoritesProjectionMap.put(RoutesColumns.AVERAGE_TRIP_LENGTH.string, + RoutesColumns.AVERAGE_TRIP_LENGTH.string); + } + + private DatabaseHelper mDatabaseHelper; + + @Override + public boolean onCreate() { + mDatabaseHelper = new DatabaseHelper(getContext()); + return true; + } + + @Override + public String getType(Uri uri) { + int match = sUriMatcher.match(uri); + if (match == FAVORITES) { + return Constants.FAVORITE_CONTENT_TYPE; + } else if (match == FAVORITE_ID) { + return Constants.FAVORITE_CONTENT_ITEM_TYPE; + } else if (match == ARBITRARY_ROUTE) { + return Constants.ARBITRARY_ROUTE_TYPE; + } else if (match == ARBITRARY_ROUTE_UNDEFINED) { + return Constants.ARBITRARY_ROUTE_UNDEFINED_TYPE; + } else { + throw new IllegalArgumentException("Unknown URI " + uri); + } + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, + String[] selectionArgs, String sortOrder) { + SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); + + SQLiteDatabase db = mDatabaseHelper.getReadableDatabase(); + + String orderBy = sortOrder; + + int match = sUriMatcher.match(uri); + + if (match == ARBITRARY_ROUTE) { + final String origin = uri.getPathSegments().get(1); + final String destination = uri.getPathSegments().get(2); + + qb.setTables(DatabaseHelper.FAVORITES_TABLE_NAME); + qb.setProjectionMap(sFavoritesProjectionMap); + qb.appendWhere(String.format("%s = '%s' AND %s = '%s'", + RoutesColumns.FROM_STATION, origin, + RoutesColumns.TO_STATION, destination)); + Cursor query = qb.query(db, projection, selection, selectionArgs, + null, null, sortOrder); + if (query.getCount() > 0) + return query; + + MatrixCursor returnCursor = new MatrixCursor(projection); + RowBuilder newRow = returnCursor.newRow(); + + for (String column : projection) { + if (column.equals(RoutesColumns.FROM_STATION.string)) { + newRow.add(origin); + } else if (column.equals(RoutesColumns.TO_STATION.string)) { + newRow.add(destination); + } else { + newRow.add(null); + } + } + + return returnCursor; + } else if (match == FAVORITE_ID) { + qb.setTables(DatabaseHelper.FAVORITES_TABLE_NAME); + qb.setProjectionMap(sFavoritesProjectionMap); + qb.appendWhere(RoutesColumns._ID + " = " + + uri.getPathSegments().get(1)); + } else if (match == FAVORITES) { + qb.setTables(DatabaseHelper.FAVORITES_TABLE_NAME); + qb.setProjectionMap(sFavoritesProjectionMap); + } else { + throw new IllegalArgumentException("Unknown URI " + uri); + } + + // If no sort order is specified use the default + if (TextUtils.isEmpty(orderBy)) { + orderBy = DEFAULT_SORT_ORDER; + } + + // Get the database and run the query + Cursor cursor = qb.query(db, projection, selection, selectionArgs, + null, null, orderBy); + + // Tell the cursor what uri to watch, so it knows when its source data + // changes + cursor.setNotificationUri(getContext().getContentResolver(), uri); + return cursor; + } + + @Override + public Uri insert(Uri uri, ContentValues initialValues) { + ContentValues values; + if (initialValues != null) { + values = new ContentValues(initialValues); + } else { + values = new ContentValues(); + } + + SQLiteDatabase db = mDatabaseHelper.getWritableDatabase(); + + // Validate the requested uri + int match = sUriMatcher.match(uri); + if (match == FAVORITES) { + long rowId = -1; + Cursor cursor = db + .query(DatabaseHelper.FAVORITES_TABLE_NAME, + new String[] { RoutesColumns._ID.string }, + RoutesColumns.FROM_STATION + "=? AND " + + RoutesColumns.TO_STATION + "=?", + new String[] { + values.getAsString(RoutesColumns.FROM_STATION.string), + values.getAsString(RoutesColumns.TO_STATION.string) }, + null, null, null); + try { + if (cursor.moveToFirst()) { + rowId = cursor.getLong(0); + } + } finally { + CursorUtils.closeCursorQuietly(cursor); + } + if (rowId < 0) { + rowId = db.insert(DatabaseHelper.FAVORITES_TABLE_NAME, + RoutesColumns.FROM_STATION.string, values); + } + if (rowId > 0) { + Uri eventUri = ContentUris.withAppendedId( + Constants.FAVORITE_CONTENT_URI, rowId); + getContext().getContentResolver().notifyChange(eventUri, null, + false); + return eventUri; + } + } else { + throw new IllegalArgumentException("Unknown URI " + uri); + } + + throw new SQLException("Failed to insert row into " + uri); + } + + @Override + public int update(Uri uri, ContentValues values, String where, + String[] whereArgs) { + SQLiteDatabase db = mDatabaseHelper.getWritableDatabase(); + + // Validate the requested uri + int match = sUriMatcher.match(uri); + if (match == FAVORITE_ID) { + String favoriteId = uri.getPathSegments().get(1); + int count = db.update( + DatabaseHelper.FAVORITES_TABLE_NAME, + values, + RoutesColumns._ID + + " = " + + favoriteId + + (!TextUtils.isEmpty(where) ? " AND (" + where + + ')' : ""), whereArgs); + getContext().getContentResolver().notifyChange(uri, null); + return count; + } else if (match == ARBITRARY_ROUTE) { + // Get the route with the origin and destination provided, and + // simply delegate to the previous log branch. If the given route + // doesn't exist, do nothing. + String origin = uri.getPathSegments().get(1); + String destination = uri.getPathSegments().get(2); + + Cursor query = db.query(DatabaseHelper.FAVORITES_TABLE_NAME, + new String[] { RoutesColumns._ID.string }, + RoutesColumns.FROM_STATION.string + "=? AND " + + RoutesColumns.TO_STATION.string + "=?", + new String[] { origin, destination }, null, null, null); + + try { + if (query.moveToFirst()) { + return update(ContentUris.withAppendedId( + Constants.FAVORITE_CONTENT_URI, query.getLong(0)), + values, where, whereArgs); + } + } finally { + CursorUtils.closeCursorQuietly(query); + } + } + return 0; + } + + @Override + public int delete(Uri uri, String where, String[] whereArgs) { + // TODO: Sync with REST service? + SQLiteDatabase db = mDatabaseHelper.getWritableDatabase(); + int count; + int match = sUriMatcher.match(uri); + if (match == FAVORITES) { + count = db.delete(DatabaseHelper.FAVORITES_TABLE_NAME, where, + whereArgs); + } else if (match == FAVORITE_ID) { + String favoriteId = uri.getPathSegments().get(1); + count = db.delete( + DatabaseHelper.FAVORITES_TABLE_NAME, + RoutesColumns._ID + + " = " + + favoriteId + + (!TextUtils.isEmpty(where) ? " AND (" + where + + ')' : ""), whereArgs); + } else { + throw new IllegalArgumentException("Unknown URI " + uri); + } + + getContext().getContentResolver().notifyChange(uri, null); + return count; + } +} diff --git a/src/com/dougkeen/bart/data/CursorUtils.java b/src/com/dougkeen/bart/data/CursorUtils.java index 35a6b53..6f3edc7 100644 --- a/src/com/dougkeen/bart/data/CursorUtils.java +++ b/src/com/dougkeen/bart/data/CursorUtils.java @@ -1,23 +1,23 @@ -package com.dougkeen.bart.data; - -import android.database.Cursor; - -public final class CursorUtils { - private CursorUtils() { - // Static only class - } - - public static final void closeCursorQuietly(Cursor cursor) { - if (cursor != null && !cursor.isClosed()) { - cursor.close(); - } - } - - public static final String getString(Cursor cursor, RoutesColumns column) { - return cursor.getString(cursor.getColumnIndex(column.string)); - } - - public static final Long getLong(Cursor cursor, RoutesColumns column) { - return cursor.getLong(cursor.getColumnIndex(column.string)); - } -} +package com.dougkeen.bart.data; + +import android.database.Cursor; + +public final class CursorUtils { + private CursorUtils() { + // Static only class + } + + public static final void closeCursorQuietly(Cursor cursor) { + if (cursor != null && !cursor.isClosed()) { + cursor.close(); + } + } + + public static final String getString(Cursor cursor, RoutesColumns column) { + return cursor.getString(cursor.getColumnIndex(column.string)); + } + + public static final Long getLong(Cursor cursor, RoutesColumns column) { + return cursor.getLong(cursor.getColumnIndex(column.string)); + } +} diff --git a/src/com/dougkeen/bart/data/DatabaseHelper.java b/src/com/dougkeen/bart/data/DatabaseHelper.java index d22383b..383aba1 100644 --- a/src/com/dougkeen/bart/data/DatabaseHelper.java +++ b/src/com/dougkeen/bart/data/DatabaseHelper.java @@ -1,98 +1,98 @@ -package com.dougkeen.bart.data; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.apache.commons.lang3.StringUtils; - -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.util.Log; - -public class DatabaseHelper extends SQLiteOpenHelper { - - private static final String DATABASE_NAME = "bart.dougkeen.db"; - private static final int DATABASE_VERSION = 4; - - public static final String FAVORITES_TABLE_NAME = "Favorites"; - - public DatabaseHelper(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - } - - @Override - public void onCreate(SQLiteDatabase db) { - createFavoritesTable(db); - } - - private void createFavoritesTable(SQLiteDatabase db) { - db.execSQL("CREATE TABLE IF NOT EXISTS " + FAVORITES_TABLE_NAME + " (" - + RoutesColumns._ID.getColumnDef() + " PRIMARY KEY, " - + RoutesColumns.FROM_STATION.getColumnDef() + ", " - + RoutesColumns.TO_STATION.getColumnDef() + ", " - + RoutesColumns.FARE.getColumnDef() + ", " - + RoutesColumns.FARE_LAST_UPDATED.getColumnDef() + ", " - + RoutesColumns.AVERAGE_TRIP_SAMPLE_COUNT.getColumnDef() + ", " - + RoutesColumns.AVERAGE_TRIP_LENGTH.getColumnDef() + ");"); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - db.beginTransaction(); - try { - createFavoritesTable(db); - - List columns = getColumns(db, FAVORITES_TABLE_NAME); - - db.execSQL("ALTER TABLE " + FAVORITES_TABLE_NAME - + " RENAME TO temp_" + FAVORITES_TABLE_NAME); - - createFavoritesTable(db); - - columns.retainAll(getColumns(db, FAVORITES_TABLE_NAME)); - - String cols = StringUtils.join(columns, ","); - db.execSQL(String.format( - "INSERT INTO %s (%s) SELECT %s from temp_%s", - FAVORITES_TABLE_NAME, cols, cols, FAVORITES_TABLE_NAME)); - - db.execSQL("DROP TABLE temp_" + FAVORITES_TABLE_NAME); - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } - - public static List getColumns(SQLiteDatabase db, String tableName) { - List ar = null; - Cursor c = null; - try { - c = db.rawQuery("select * from " + tableName + " limit 1", null); - if (c != null) { - ar = new ArrayList(Arrays.asList(c.getColumnNames())); - } - } catch (Exception e) { - Log.v(tableName, e.getMessage(), e); - e.printStackTrace(); - } finally { - if (c != null) - c.close(); - } - return ar; - } - - public static String join(List list, String delim) { - StringBuilder buf = new StringBuilder(); - int num = list.size(); - for (int i = 0; i < num; i++) { - if (i != 0) - buf.append(delim); - buf.append((String) list.get(i)); - } - return buf.toString(); - } -} +package com.dougkeen.bart.data; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; + +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.util.Log; + +public class DatabaseHelper extends SQLiteOpenHelper { + + private static final String DATABASE_NAME = "bart.dougkeen.db"; + private static final int DATABASE_VERSION = 4; + + public static final String FAVORITES_TABLE_NAME = "Favorites"; + + public DatabaseHelper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + createFavoritesTable(db); + } + + private void createFavoritesTable(SQLiteDatabase db) { + db.execSQL("CREATE TABLE IF NOT EXISTS " + FAVORITES_TABLE_NAME + " (" + + RoutesColumns._ID.getColumnDef() + " PRIMARY KEY, " + + RoutesColumns.FROM_STATION.getColumnDef() + ", " + + RoutesColumns.TO_STATION.getColumnDef() + ", " + + RoutesColumns.FARE.getColumnDef() + ", " + + RoutesColumns.FARE_LAST_UPDATED.getColumnDef() + ", " + + RoutesColumns.AVERAGE_TRIP_SAMPLE_COUNT.getColumnDef() + ", " + + RoutesColumns.AVERAGE_TRIP_LENGTH.getColumnDef() + ");"); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + db.beginTransaction(); + try { + createFavoritesTable(db); + + List columns = getColumns(db, FAVORITES_TABLE_NAME); + + db.execSQL("ALTER TABLE " + FAVORITES_TABLE_NAME + + " RENAME TO temp_" + FAVORITES_TABLE_NAME); + + createFavoritesTable(db); + + columns.retainAll(getColumns(db, FAVORITES_TABLE_NAME)); + + String cols = StringUtils.join(columns, ","); + db.execSQL(String.format( + "INSERT INTO %s (%s) SELECT %s from temp_%s", + FAVORITES_TABLE_NAME, cols, cols, FAVORITES_TABLE_NAME)); + + db.execSQL("DROP TABLE temp_" + FAVORITES_TABLE_NAME); + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + + public static List getColumns(SQLiteDatabase db, String tableName) { + List ar = null; + Cursor c = null; + try { + c = db.rawQuery("select * from " + tableName + " limit 1", null); + if (c != null) { + ar = new ArrayList(Arrays.asList(c.getColumnNames())); + } + } catch (Exception e) { + Log.v(tableName, e.getMessage(), e); + e.printStackTrace(); + } finally { + if (c != null) + c.close(); + } + return ar; + } + + public static String join(List list, String delim) { + StringBuilder buf = new StringBuilder(); + int num = list.size(); + for (int i = 0; i < num; i++) { + if (i != 0) + buf.append(delim); + buf.append((String) list.get(i)); + } + return buf.toString(); + } +} diff --git a/src/com/dougkeen/bart/data/RoutesColumns.java b/src/com/dougkeen/bart/data/RoutesColumns.java index e4d3097..0033cd7 100644 --- a/src/com/dougkeen/bart/data/RoutesColumns.java +++ b/src/com/dougkeen/bart/data/RoutesColumns.java @@ -1,28 +1,26 @@ -package com.dougkeen.bart.data; - - -public enum RoutesColumns { - _ID("_id", "INTEGER", false), - FROM_STATION("FROM_STATION", "TEXT", false), - TO_STATION("TO_STATION", "TEXT", false), - FARE("FARE", "TEXT", true), - FARE_LAST_UPDATED("FARE_LAST_UPDATED", "INTEGER", true), - AVERAGE_TRIP_SAMPLE_COUNT("AVE_TRIP_SAMPLE_COUNT", "INTEGER", true), - AVERAGE_TRIP_LENGTH("AVE_TRIP_LENGTH", "INTEGER", true); - - - // This class cannot be instantiated - private RoutesColumns(String string, String type, Boolean nullable) { - this.string = string; - this.sqliteType = type; - this.nullable = nullable; - } - - public final String string; - public final String sqliteType; - public final Boolean nullable; - - protected String getColumnDef() { - return string + " " + sqliteType + (nullable?"":" NOT NULL"); - } -} +package com.dougkeen.bart.data; + +public enum RoutesColumns { + _ID("_id", "INTEGER", false), + FROM_STATION("FROM_STATION", "TEXT", false), + TO_STATION("TO_STATION", "TEXT", false), + FARE("FARE", "TEXT", true), + FARE_LAST_UPDATED("FARE_LAST_UPDATED", "INTEGER", true), + AVERAGE_TRIP_SAMPLE_COUNT("AVE_TRIP_SAMPLE_COUNT", "INTEGER", true), + AVERAGE_TRIP_LENGTH("AVE_TRIP_LENGTH", "INTEGER", true); + + // This class cannot be instantiated + private RoutesColumns(String string, String type, Boolean nullable) { + this.string = string; + this.sqliteType = type; + this.nullable = nullable; + } + + public final String string; + public final String sqliteType; + public final Boolean nullable; + + protected String getColumnDef() { + return string + " " + sqliteType + (nullable ? "" : " NOT NULL"); + } +} diff --git a/src/com/dougkeen/bart/model/Constants.java b/src/com/dougkeen/bart/model/Constants.java index 479a077..c780c14 100644 --- a/src/com/dougkeen/bart/model/Constants.java +++ b/src/com/dougkeen/bart/model/Constants.java @@ -1,20 +1,20 @@ -package com.dougkeen.bart.model; - -import android.net.Uri; - -public class Constants { - public static final String AUTHORITY = "com.dougkeen.bart.dataprovider"; - public static final String FAVORITE_CONTENT_TYPE = "vnd.android.cursor.dir/com.dougkeen.bart.favorite"; - public static final String ARBITRARY_ROUTE_UNDEFINED_TYPE = "vnd.android.cursor.dir/com.dougkeen.bart.arbitraryroute"; - public static final String ARBITRARY_ROUTE_TYPE = "vnd.android.cursor.item/com.dougkeen.bart.arbitraryroute"; - public static final String FAVORITE_CONTENT_ITEM_TYPE = "vnd.android.cursor.item/com.dougkeen.bart.favorite"; - public static final Uri FAVORITE_CONTENT_URI = Uri.parse("content://" - + AUTHORITY + "/favorites"); - public static final Uri ARBITRARY_ROUTE_CONTENT_URI_ROOT = Uri - .parse("content://" + AUTHORITY + "/route"); - public static final String MAP_URL = "http://m.bart.gov/images/global/system-map29.gif"; - - public static final String TAG = "com.dougkeen.BartRunner"; - public static final String API_KEY = "5LD9-IAYI-TRAT-MHHW"; - public static final String ACTION_ALARM = "com.dougkeen.action.ALARM"; -} +package com.dougkeen.bart.model; + +import android.net.Uri; + +public class Constants { + public static final String AUTHORITY = "com.dougkeen.bart.dataprovider"; + public static final String FAVORITE_CONTENT_TYPE = "vnd.android.cursor.dir/com.dougkeen.bart.favorite"; + public static final String ARBITRARY_ROUTE_UNDEFINED_TYPE = "vnd.android.cursor.dir/com.dougkeen.bart.arbitraryroute"; + public static final String ARBITRARY_ROUTE_TYPE = "vnd.android.cursor.item/com.dougkeen.bart.arbitraryroute"; + public static final String FAVORITE_CONTENT_ITEM_TYPE = "vnd.android.cursor.item/com.dougkeen.bart.favorite"; + public static final Uri FAVORITE_CONTENT_URI = Uri.parse("content://" + + AUTHORITY + "/favorites"); + public static final Uri ARBITRARY_ROUTE_CONTENT_URI_ROOT = Uri + .parse("content://" + AUTHORITY + "/route"); + public static final String MAP_URL = "http://m.bart.gov/images/global/system-map29.gif"; + + public static final String TAG = "com.dougkeen.BartRunner"; + public static final String API_KEY = "5LD9-IAYI-TRAT-MHHW"; + public static final String ACTION_ALARM = "com.dougkeen.action.ALARM"; +} diff --git a/src/com/dougkeen/bart/model/Departure.java b/src/com/dougkeen/bart/model/Departure.java index 52f5ce9..794aa9c 100644 --- a/src/com/dougkeen/bart/model/Departure.java +++ b/src/com/dougkeen/bart/model/Departure.java @@ -1,676 +1,678 @@ -package com.dougkeen.bart.model; - -import java.text.SimpleDateFormat; -import java.util.Date; - -import org.apache.commons.lang3.time.DateFormatUtils; - -import android.app.AlarmManager; -import android.app.Notification; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.os.Parcel; -import android.os.Parcelable; -import android.support.v4.app.NotificationCompat; -import android.support.v4.app.NotificationCompat.Builder; -import android.text.format.DateFormat; -import android.util.Log; - -import com.dougkeen.bart.R; -import com.dougkeen.bart.services.BoardedDepartureService; -import com.dougkeen.util.Observable; - -public class Departure implements Parcelable, Comparable { - private static final int MINIMUM_MERGE_OVERLAP_MILLIS = 5000; - private static final int EXPIRE_MINUTES_AFTER_ARRIVAL = 1; - - public Departure() { - super(); - } - - public Departure(String destinationAbbr, String destinationColor, - String platform, String direction, boolean bikeAllowed, - String trainLength, int minutes) { - super(); - this.trainDestination = Station.getByAbbreviation(destinationAbbr); - this.destinationColor = destinationColor; - this.platform = platform; - this.direction = direction; - this.bikeAllowed = bikeAllowed; - this.trainLength = trainLength; - this.minutes = minutes; - } - - public Departure(Parcel in) { - readFromParcel(in); - } - - private Station origin; - private Station trainDestination; - private Station passengerDestination; - private Line line; - private String destinationColor; - private String platform; - private String direction; - private boolean bikeAllowed; - private String trainLength; - private boolean requiresTransfer; - private boolean transferScheduled; - - private int minutes; - - private long minEstimate; - private long maxEstimate; - - private int estimatedTripTime; - - private boolean beganAsDeparted; - - private long arrivalTimeOverride; - - private Observable alarmLeadTimeMinutes = new Observable( - 0); - private Observable alarmPending = new Observable(false); - - public Station getOrigin() { - return origin; - } - - public void setOrigin(Station origin) { - this.origin = origin; - } - - public Station getTrainDestination() { - return trainDestination; - } - - public void setTrainDestination(Station destination) { - this.trainDestination = destination; - } - - public String getTrainDestinationName() { - if (trainDestination != null) - return trainDestination.name; - return null; - } - - public String getTrainDestinationAbbreviation() { - if (trainDestination != null) - return trainDestination.abbreviation; - return null; - } - - public Station getPassengerDestination() { - return passengerDestination; - } - - public void setPassengerDestination(Station passengerDestination) { - this.passengerDestination = passengerDestination; - } - - public StationPair getStationPair() { - if (passengerDestination != null) { - return new StationPair(origin, passengerDestination); - } else { - return null; - } - } - - public Line getLine() { - return line; - } - - public void setLine(Line line) { - this.line = line; - } - - public String getTrainDestinationColor() { - return destinationColor; - } - - public void setTrainDestinationColor(String destinationColor) { - this.destinationColor = destinationColor; - } - - public String getPlatform() { - return platform; - } - - public void setPlatform(String platform) { - this.platform = platform; - } - - public String getDirection() { - return direction; - } - - public void setDirection(String direction) { - this.direction = direction; - } - - public boolean isBikeAllowed() { - return bikeAllowed; - } - - public void setBikeAllowed(boolean bikeAllowed) { - this.bikeAllowed = bikeAllowed; - } - - public String getTrainLength() { - return trainLength; - } - - public void setTrainLength(String trainLength) { - this.trainLength = trainLength; - } - - public String getTrainLengthText() { - return trainLength + " car train"; - } - - public boolean getRequiresTransfer() { - return requiresTransfer; - } - - public void setRequiresTransfer(boolean requiresTransfer) { - this.requiresTransfer = requiresTransfer; - } - - public boolean isTransferScheduled() { - return transferScheduled; - } - - public void setTransferScheduled(boolean transferScheduled) { - this.transferScheduled = transferScheduled; - } - - public int getMinutes() { - return minutes; - } - - public void setMinutes(int minutes) { - this.minutes = minutes; - if (minutes == 0) { - beganAsDeparted = true; - } - } - - public long getMinEstimate() { - return minEstimate; - } - - public void setMinEstimate(long minEstimate) { - this.minEstimate = minEstimate; - } - - public long getMaxEstimate() { - return maxEstimate; - } - - public void setMaxEstimate(long maxEstimate) { - this.maxEstimate = maxEstimate; - } - - public int getEstimatedTripTime() { - return estimatedTripTime; - } - - public void setEstimatedTripTime(int estimatedTripTime) { - this.estimatedTripTime = estimatedTripTime; - } - - public boolean hasEstimatedTripTime() { - return this.estimatedTripTime > 0; - } - - public boolean hasAnyArrivalEstimate() { - return this.estimatedTripTime > 0 || this.arrivalTimeOverride > 0; - } - - public int getUncertaintySeconds() { - return (int) (maxEstimate - minEstimate + 1000) / 2000; - } - - public int getMinSecondsLeft() { - return (int) ((getMinEstimate() - System.currentTimeMillis()) / 1000); - } - - public int getMaxSecondsLeft() { - return (int) ((getMaxEstimate() - System.currentTimeMillis()) / 1000); - } - - public int getMeanSecondsLeft() { - return (int) getMeanSecondsLeft(getMinEstimate(), getMaxEstimate()); - } - - public int getMeanSecondsLeft(long min, long max) { - return (int) ((getMeanEstimate(min, max) - System.currentTimeMillis()) / 1000); - } - - public long getMeanEstimate() { - return getMeanEstimate(getMinEstimate(), getMaxEstimate()); - } - - public long getMeanEstimate(long min, long max) { - return (min + max) / 2; - } - - public long getArrivalTimeOverride() { - return arrivalTimeOverride; - } - - public void setArrivalTimeOverride(long arrivalTimeOverride) { - this.arrivalTimeOverride = arrivalTimeOverride; - } - - public long getEstimatedArrivalTime() { - if (arrivalTimeOverride > 0) { - return arrivalTimeOverride; - } - return getMeanEstimate() + getEstimatedTripTime(); - } - - public long getEstimatedArrivalMinutesLeft() { - long millisLeft = getEstimatedArrivalTime() - - System.currentTimeMillis(); - if (millisLeft < 0) { - return -1; - } else { - // Add ~30s to emulate rounding - return (millisLeft + 29999) / (60 * 1000); - } - } - - public String getEstimatedArrivalMinutesLeftText(Context context) { - if (!hasAnyArrivalEstimate()) { - return "Estimated arrival unknown"; - } - long minutesLeft = getEstimatedArrivalMinutesLeft(); - if (minutesLeft < 0) { - return "Arrived at destination"; - } else if (minutesLeft == 0) { - return "Arrives ~" + getEstimatedArrivalTimeText(context) - + " (<1 min)"; - } else if (minutesLeft == 1) { - return "Arrives ~" + getEstimatedArrivalTimeText(context) - + " (1 min)"; - } else { - return "Arrives ~" + getEstimatedArrivalTimeText(context) + " (" - + minutesLeft + " mins)"; - } - } - - public String getEstimatedArrivalTimeText(Context context) { - if (getEstimatedTripTime() > 0 || arrivalTimeOverride > 0) { - return DateFormat.getTimeFormat(context).format( - new Date(getEstimatedArrivalTime())); - } else { - return ""; - } - } - - public boolean hasDeparted() { - return getMeanSecondsLeft() <= 0; - } - - public void calculateEstimates(long originalEstimateTime) { - setMinEstimate(originalEstimateTime + (getMinutes() * 60 * 1000) - - (30000)); - setMaxEstimate(getMinEstimate() + 60000); - } - - public void mergeEstimate(Departure departure) { - if (departure.hasDeparted() && origin.longStationLinger - && getMinEstimate() > 0 && !beganAsDeparted) { - /* - * This is probably not a true departure, but an indication that the - * train is in the station. Don't update the estimates. - */ - return; - } - - boolean wasDeparted = hasDeparted(); - if (!hasAnyArrivalEstimate() && departure.hasAnyArrivalEstimate()) { - setArrivalTimeOverride(departure.getArrivalTimeOverride()); - setEstimatedTripTime(departure.getEstimatedTripTime()); - } - - long newMin = Math.max(getMinEstimate(), departure.getMinEstimate()); - long newMax = Math.min(getMaxEstimate(), departure.getMaxEstimate()); - - if ((getMaxEstimate() - departure.getMinEstimate()) < MINIMUM_MERGE_OVERLAP_MILLIS - || departure.getMaxEstimate() - getMinEstimate() < MINIMUM_MERGE_OVERLAP_MILLIS) { - /* - * The estimate must have changed... just use the latest incoming - * values - */ - newMin = departure.getMinEstimate(); - newMax = departure.getMaxEstimate(); - } - - /* - * If the new departure would mark this as departed, and we have < 60 - * seconds left on a fairly accurate local estimate, ignore the incoming - * departure - */ - if (!wasDeparted && getMeanSecondsLeft(newMin, newMax) <= 0 - && getMeanSecondsLeft() < 60 && getUncertaintySeconds() < 30) { - Log.d(Constants.TAG, - "Skipping estimate merge, since it would make this departure show as 'departed' prematurely"); - return; - } - - if (newMax > newMin) { - // We must never have 0 or negative uncertainty - setMinEstimate(newMin); - setMaxEstimate(newMax); - } - } - - public boolean hasExpired() { - final long now = System.currentTimeMillis(); - return getMaxEstimate() < now - && getEstimatedArrivalTime() + EXPIRE_MINUTES_AFTER_ARRIVAL - * 60000 < now; - } - - public int compareTo(Departure another) { - return (this.getMeanSecondsLeft() > another.getMeanSecondsLeft()) ? 1 - : ((this.getMeanSecondsLeft() == another.getMeanSecondsLeft()) ? 0 - : -1); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (bikeAllowed ? 1231 : 1237); - result = prime - * result - + ((trainDestination == null) ? 0 : trainDestination.hashCode()); - result = prime - * result - + ((destinationColor == null) ? 0 : destinationColor.hashCode()); - result = prime * result - + ((direction == null) ? 0 : direction.hashCode()); - result = prime * result + ((line == null) ? 0 : line.hashCode()); - result = prime * result + (int) (maxEstimate ^ (maxEstimate >>> 32)); - result = prime * result + (int) (minEstimate ^ (minEstimate >>> 32)); - result = prime * result + minutes; - result = prime * result - + ((platform == null) ? 0 : platform.hashCode()); - result = prime * result + (requiresTransfer ? 1231 : 1237); - result = prime * result - + ((trainLength == null) ? 0 : trainLength.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Departure other = (Departure) obj; - if (bikeAllowed != other.bikeAllowed) - return false; - if (trainDestination != other.trainDestination) - return false; - if (destinationColor == null) { - if (other.destinationColor != null) - return false; - } else if (!destinationColor.equals(other.destinationColor)) - return false; - if (direction == null) { - if (other.direction != null) - return false; - } else if (!direction.equals(other.direction)) - return false; - if (line != other.line) - return false; - if (Math.abs(maxEstimate - other.maxEstimate) > getEqualsTolerance()) - return false; - if (platform == null) { - if (other.platform != null) - return false; - } else if (!platform.equals(other.platform)) - return false; - if (requiresTransfer != other.requiresTransfer) - return false; - if (trainLength == null) { - if (other.trainLength != null) - return false; - } else if (!trainLength.equals(other.trainLength)) - return false; - return true; - } - - private int getEqualsTolerance() { - if (origin != null) { - return origin.departureEqualityTolerance; - } else { - return Station.DEFAULT_DEPARTURE_EQUALITY_TOLERANCE; - } - } - - public String getCountdownText() { - StringBuilder builder = new StringBuilder(); - int secondsLeft = getMeanSecondsLeft(); - if (hasDeparted()) { - if (origin != null && origin.longStationLinger && beganAsDeparted) { - builder.append("At station"); - } else { - builder.append("Departed"); - } - } else { - builder.append(secondsLeft / 60); - builder.append("m, "); - builder.append(secondsLeft % 60); - builder.append("s"); - } - return builder.toString(); - } - - public String getUncertaintyText() { - if (hasDeparted()) { - return ""; - } else { - return "(±" + getUncertaintySeconds() + "s)"; - } - } - - public int getAlarmLeadTimeMinutes() { - return alarmLeadTimeMinutes.getValue(); - } - - public Observable getAlarmLeadTimeMinutesObservable() { - return alarmLeadTimeMinutes; - } - - public boolean isAlarmPending() { - return alarmPending.getValue(); - } - - public Observable getAlarmPendingObservable() { - return alarmPending; - } - - private PendingIntent getAlarmIntent(Context context) { - return PendingIntent.getBroadcast(context, 0, new Intent( - Constants.ACTION_ALARM, getStationPair().getUri()), - PendingIntent.FLAG_UPDATE_CURRENT); - } - - private long getAlarmClockTime() { - return getMeanEstimate() - alarmLeadTimeMinutes.getValue() * 60 * 1000; - } - - public int getSecondsUntilAlarm() { - return getMeanSecondsLeft() - getAlarmLeadTimeMinutes() * 60; - } - - public void setUpAlarm(int leadTimeMinutes) { - this.alarmLeadTimeMinutes.setValue(leadTimeMinutes); - this.alarmPending.setValue(true); - } - - public void updateAlarm(Context context, AlarmManager alarmManager) { - if (alarmManager == null) - return; - - if (isAlarmPending() && getAlarmLeadTimeMinutes() > 0) { - final PendingIntent alarmIntent = getAlarmIntent(context); - alarmManager.cancel(alarmIntent); - - long alarmTime = getAlarmClockTime(); - - alarmManager.set(AlarmManager.RTC_WAKEUP, alarmTime, alarmIntent); - - if (Log.isLoggable(Constants.TAG, Log.VERBOSE)) - Log.v(Constants.TAG, - "Scheduling alarm for " - + DateFormatUtils.format(alarmTime, "h:mm:ss")); - } - } - - public void cancelAlarm(Context context, AlarmManager alarmManager) { - alarmManager.cancel(getAlarmIntent(context)); - this.alarmPending.setValue(false); - } - - private PendingIntent notificationIntent; - - private PendingIntent getNotificationIntent(Context context) { - if (notificationIntent == null) { - Intent targetIntent = new Intent(Intent.ACTION_VIEW, - getStationPair().getUri()); - targetIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - notificationIntent = PendingIntent.getActivity(context, 0, - targetIntent, PendingIntent.FLAG_UPDATE_CURRENT); - } - return notificationIntent; - } - - public Notification createNotification(Context context) { - final int halfMinutes = (getMeanSecondsLeft() + 15) / 30; - float minutes = halfMinutes / 2f; - final String minutesText = (minutes < 1) ? "Less than one minute" - : (String.format("~%.1f minute", minutes) + ((minutes != 1.0) ? "s" - : "")); - - final Intent cancelAlarmIntent = new Intent(context, - BoardedDepartureService.class); - cancelAlarmIntent.putExtra("cancelNotifications", true); - Builder notificationBuilder = new NotificationCompat.Builder(context) - .setOngoing(true) - .setSmallIcon(R.drawable.ic_stat_notification) - .setContentTitle( - getOrigin().shortName + " to " - + getPassengerDestination().shortName) - .setContentIntent(getNotificationIntent(context)).setWhen(0); - if (android.os.Build.VERSION.SDK_INT >= 16) { - notificationBuilder.setPriority(NotificationCompat.PRIORITY_HIGH) - .setContentText(minutesText + " until departure"); - if (isAlarmPending()) { - notificationBuilder.addAction( - R.drawable.ic_action_cancel_alarm, - "Cancel alarm", - PendingIntent.getService(context, 0, cancelAlarmIntent, - PendingIntent.FLAG_UPDATE_CURRENT)).setSubText( - "Alarm " + getAlarmLeadTimeMinutes() - + " minutes before departure"); - } - } else if (isAlarmPending()) { - notificationBuilder.setContentText(minutesText - + " to departure (alarm at " + getAlarmLeadTimeMinutes() - + " min" + ((getAlarmLeadTimeMinutes() == 1) ? "" : "s") - + ")"); - } else { - notificationBuilder - .setContentText(minutesText + " until departure"); - } - - return notificationBuilder.build(); - } - - @Override - public String toString() { - java.text.DateFormat format = SimpleDateFormat.getTimeInstance(); - StringBuilder builder = new StringBuilder(); - builder.append(trainDestination); - if (requiresTransfer) { - builder.append(" (w/ xfer)"); - } - builder.append(", "); - builder.append(getCountdownText()); - builder.append(", "); - builder.append(format.format(new Date(getMeanEstimate()))); - return builder.toString(); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(origin.abbreviation); - dest.writeString(trainDestination.abbreviation); - dest.writeString(passengerDestination == null ? null - : passengerDestination.abbreviation); - dest.writeString(destinationColor); - dest.writeString(platform); - dest.writeString(direction); - dest.writeByte((byte) (bikeAllowed ? 1 : 0)); - dest.writeString(trainLength); - dest.writeByte((byte) (requiresTransfer ? 1 : 0)); - dest.writeInt(minutes); - dest.writeLong(minEstimate); - dest.writeLong(maxEstimate); - dest.writeLong(arrivalTimeOverride); - dest.writeInt(estimatedTripTime); - dest.writeInt(line.ordinal()); - dest.writeByte(beganAsDeparted ? (byte) 1 : (byte) 0); - dest.writeByte(bikeAllowed ? (byte) 1 : (byte) 0); - dest.writeByte(requiresTransfer ? (byte) 1 : (byte) 0); - dest.writeByte(transferScheduled ? (byte) 1 : (byte) 0); - } - - private void readFromParcel(Parcel in) { - origin = Station.getByAbbreviation(in.readString()); - trainDestination = Station.getByAbbreviation(in.readString()); - passengerDestination = Station.getByAbbreviation(in.readString()); - destinationColor = in.readString(); - platform = in.readString(); - direction = in.readString(); - bikeAllowed = in.readByte() != 0; - trainLength = in.readString(); - requiresTransfer = in.readByte() != 0; - minutes = in.readInt(); - minEstimate = in.readLong(); - maxEstimate = in.readLong(); - arrivalTimeOverride = in.readLong(); - estimatedTripTime = in.readInt(); - line = Line.values()[in.readInt()]; - beganAsDeparted = in.readByte() == (byte) 1; - bikeAllowed = in.readByte() == (byte) 1; - requiresTransfer = in.readByte() == (byte) 1; - transferScheduled = in.readByte() == (byte) 1; - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public Departure createFromParcel(Parcel in) { - return new Departure(in); - } - - public Departure[] newArray(int size) { - return new Departure[size]; - } - }; - - public void notifyAlarmHasBeenHandled() { - this.alarmPending.setValue(false); - } +package com.dougkeen.bart.model; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.apache.commons.lang3.time.DateFormatUtils; + +import android.app.AlarmManager; +import android.app.Notification; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.v4.app.NotificationCompat; +import android.support.v4.app.NotificationCompat.Builder; +import android.text.format.DateFormat; +import android.util.Log; + +import com.dougkeen.bart.BartRunnerApplication; +import com.dougkeen.bart.R; +import com.dougkeen.bart.services.BoardedDepartureService; +import com.dougkeen.util.Observable; + +public class Departure implements Parcelable, Comparable { + private static final int MINIMUM_MERGE_OVERLAP_MILLIS = 5000; + private static final int EXPIRE_MINUTES_AFTER_ARRIVAL = 1; + + public Departure() { + super(); + } + + public Departure(String destinationAbbr, String destinationColor, + String platform, String direction, boolean bikeAllowed, + String trainLength, int minutes) { + super(); + this.trainDestination = Station.getByAbbreviation(destinationAbbr); + this.destinationColor = destinationColor; + this.platform = platform; + this.direction = direction; + this.bikeAllowed = bikeAllowed; + this.trainLength = trainLength; + this.minutes = minutes; + } + + public Departure(Parcel in) { + readFromParcel(in); + } + + private Station origin; + private Station trainDestination; + private Station passengerDestination; + private Line line; + private String destinationColor; + private String platform; + private String direction; + private boolean bikeAllowed; + private String trainLength; + private boolean requiresTransfer; + private boolean transferScheduled; + + private int minutes; + + private long minEstimate; + private long maxEstimate; + + private int estimatedTripTime; + + private boolean beganAsDeparted; + + private long arrivalTimeOverride; + + private Observable alarmLeadTimeMinutes = new Observable( + 0); + private Observable alarmPending = new Observable(false); + + public Station getOrigin() { + return origin; + } + + public void setOrigin(Station origin) { + this.origin = origin; + } + + public Station getTrainDestination() { + return trainDestination; + } + + public void setTrainDestination(Station destination) { + this.trainDestination = destination; + } + + public String getTrainDestinationName() { + if (trainDestination != null) + return trainDestination.name; + return null; + } + + public String getTrainDestinationAbbreviation() { + if (trainDestination != null) + return trainDestination.abbreviation; + return null; + } + + public Station getPassengerDestination() { + return passengerDestination; + } + + public void setPassengerDestination(Station passengerDestination) { + this.passengerDestination = passengerDestination; + } + + public StationPair getStationPair() { + if (passengerDestination != null) { + return new StationPair(origin, passengerDestination); + } else { + return null; + } + } + + public Line getLine() { + return line; + } + + public void setLine(Line line) { + this.line = line; + } + + public String getTrainDestinationColor() { + return destinationColor; + } + + public void setTrainDestinationColor(String destinationColor) { + this.destinationColor = destinationColor; + } + + public String getPlatform() { + return platform; + } + + public void setPlatform(String platform) { + this.platform = platform; + } + + public String getDirection() { + return direction; + } + + public void setDirection(String direction) { + this.direction = direction; + } + + public boolean isBikeAllowed() { + return bikeAllowed; + } + + public void setBikeAllowed(boolean bikeAllowed) { + this.bikeAllowed = bikeAllowed; + } + + public String getTrainLength() { + return trainLength; + } + + public void setTrainLength(String trainLength) { + this.trainLength = trainLength; + } + + public String getTrainLengthText() { + return trainLength + " car train"; + } + + public boolean getRequiresTransfer() { + return requiresTransfer; + } + + public void setRequiresTransfer(boolean requiresTransfer) { + this.requiresTransfer = requiresTransfer; + } + + public boolean isTransferScheduled() { + return transferScheduled; + } + + public void setTransferScheduled(boolean transferScheduled) { + this.transferScheduled = transferScheduled; + } + + public int getMinutes() { + return minutes; + } + + public void setMinutes(int minutes) { + this.minutes = minutes; + if (minutes == 0) { + beganAsDeparted = true; + } + } + + public long getMinEstimate() { + return minEstimate; + } + + public void setMinEstimate(long minEstimate) { + this.minEstimate = minEstimate; + } + + public long getMaxEstimate() { + return maxEstimate; + } + + public void setMaxEstimate(long maxEstimate) { + this.maxEstimate = maxEstimate; + } + + public int getEstimatedTripTime() { + return estimatedTripTime; + } + + public void setEstimatedTripTime(int estimatedTripTime) { + this.estimatedTripTime = estimatedTripTime; + } + + public boolean hasEstimatedTripTime() { + return this.estimatedTripTime > 0; + } + + public boolean hasAnyArrivalEstimate() { + return this.estimatedTripTime > 0 || this.arrivalTimeOverride > 0; + } + + public int getUncertaintySeconds() { + return (int) (maxEstimate - minEstimate + 1000) / 2000; + } + + public int getMinSecondsLeft() { + return (int) ((getMinEstimate() - System.currentTimeMillis()) / 1000); + } + + public int getMaxSecondsLeft() { + return (int) ((getMaxEstimate() - System.currentTimeMillis()) / 1000); + } + + public int getMeanSecondsLeft() { + return (int) getMeanSecondsLeft(getMinEstimate(), getMaxEstimate()); + } + + public int getMeanSecondsLeft(long min, long max) { + return (int) ((getMeanEstimate(min, max) - System.currentTimeMillis()) / 1000); + } + + public long getMeanEstimate() { + return getMeanEstimate(getMinEstimate(), getMaxEstimate()); + } + + public long getMeanEstimate(long min, long max) { + return (min + max) / 2; + } + + public long getArrivalTimeOverride() { + return arrivalTimeOverride; + } + + public void setArrivalTimeOverride(long arrivalTimeOverride) { + this.arrivalTimeOverride = arrivalTimeOverride; + } + + public long getEstimatedArrivalTime() { + if (arrivalTimeOverride > 0) { + return arrivalTimeOverride; + } + return getMeanEstimate() + getEstimatedTripTime(); + } + + public long getEstimatedArrivalMinutesLeft() { + long millisLeft = getEstimatedArrivalTime() + - System.currentTimeMillis(); + if (millisLeft < 0) { + return -1; + } else { + // Add ~30s to emulate rounding + return (millisLeft + 29999) / (60 * 1000); + } + } + + public String getEstimatedArrivalMinutesLeftText(Context context) { + if (!hasAnyArrivalEstimate()) { + return "Estimated arrival unknown"; + } + long minutesLeft = getEstimatedArrivalMinutesLeft(); + if (minutesLeft < 0) { + return "Arrived at destination"; + } else if (minutesLeft == 0) { + return "Arrives ~" + getEstimatedArrivalTimeText(context) + + " (<1 min)"; + } else if (minutesLeft == 1) { + return "Arrives ~" + getEstimatedArrivalTimeText(context) + + " (1 min)"; + } else { + return "Arrives ~" + getEstimatedArrivalTimeText(context) + " (" + + minutesLeft + " mins)"; + } + } + + public String getEstimatedArrivalTimeText(Context context) { + if (getEstimatedTripTime() > 0 || arrivalTimeOverride > 0) { + return DateFormat.getTimeFormat(context).format( + new Date(getEstimatedArrivalTime())); + } else { + return ""; + } + } + + public boolean hasDeparted() { + return getMeanSecondsLeft() <= 0; + } + + public void calculateEstimates(long originalEstimateTime) { + setMinEstimate(originalEstimateTime + (getMinutes() * 60 * 1000) + - (30000)); + setMaxEstimate(getMinEstimate() + 60000); + } + + public void mergeEstimate(Departure departure) { + if (departure.hasDeparted() && origin.longStationLinger + && getMinEstimate() > 0 && !beganAsDeparted) { + /* + * This is probably not a true departure, but an indication that the + * train is in the station. Don't update the estimates. + */ + return; + } + + boolean wasDeparted = hasDeparted(); + if (!hasAnyArrivalEstimate() && departure.hasAnyArrivalEstimate()) { + setArrivalTimeOverride(departure.getArrivalTimeOverride()); + setEstimatedTripTime(departure.getEstimatedTripTime()); + } + + long newMin = Math.max(getMinEstimate(), departure.getMinEstimate()); + long newMax = Math.min(getMaxEstimate(), departure.getMaxEstimate()); + + if ((getMaxEstimate() - departure.getMinEstimate()) < MINIMUM_MERGE_OVERLAP_MILLIS + || departure.getMaxEstimate() - getMinEstimate() < MINIMUM_MERGE_OVERLAP_MILLIS) { + /* + * The estimate must have changed... just use the latest incoming + * values + */ + newMin = departure.getMinEstimate(); + newMax = departure.getMaxEstimate(); + } + + /* + * If the new departure would mark this as departed, and we have < 60 + * seconds left on a fairly accurate local estimate, ignore the incoming + * departure + */ + if (!wasDeparted && getMeanSecondsLeft(newMin, newMax) <= 0 + && getMeanSecondsLeft() < 60 && getUncertaintySeconds() < 30) { + Log.d(Constants.TAG, + "Skipping estimate merge, since it would make this departure show as 'departed' prematurely"); + return; + } + + if (newMax > newMin) { + // We must never have 0 or negative uncertainty + setMinEstimate(newMin); + setMaxEstimate(newMax); + } + } + + public boolean hasExpired() { + final long now = System.currentTimeMillis(); + return getMaxEstimate() < now + && getEstimatedArrivalTime() + EXPIRE_MINUTES_AFTER_ARRIVAL + * 60000 < now; + } + + public int compareTo(Departure another) { + return (this.getMeanSecondsLeft() > another.getMeanSecondsLeft()) ? 1 + : ((this.getMeanSecondsLeft() == another.getMeanSecondsLeft()) ? 0 + : -1); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (bikeAllowed ? 1231 : 1237); + result = prime + * result + + ((trainDestination == null) ? 0 : trainDestination.hashCode()); + result = prime + * result + + ((destinationColor == null) ? 0 : destinationColor.hashCode()); + result = prime * result + + ((direction == null) ? 0 : direction.hashCode()); + result = prime * result + ((line == null) ? 0 : line.hashCode()); + result = prime * result + (int) (maxEstimate ^ (maxEstimate >>> 32)); + result = prime * result + (int) (minEstimate ^ (minEstimate >>> 32)); + result = prime * result + minutes; + result = prime * result + + ((platform == null) ? 0 : platform.hashCode()); + result = prime * result + (requiresTransfer ? 1231 : 1237); + result = prime * result + + ((trainLength == null) ? 0 : trainLength.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Departure other = (Departure) obj; + if (bikeAllowed != other.bikeAllowed) + return false; + if (trainDestination != other.trainDestination) + return false; + if (destinationColor == null) { + if (other.destinationColor != null) + return false; + } else if (!destinationColor.equals(other.destinationColor)) + return false; + if (direction == null) { + if (other.direction != null) + return false; + } else if (!direction.equals(other.direction)) + return false; + if (line != other.line) + return false; + if (Math.abs(maxEstimate - other.maxEstimate) > getEqualsTolerance()) + return false; + if (platform == null) { + if (other.platform != null) + return false; + } else if (!platform.equals(other.platform)) + return false; + if (requiresTransfer != other.requiresTransfer) + return false; + if (trainLength == null) { + if (other.trainLength != null) + return false; + } else if (!trainLength.equals(other.trainLength)) + return false; + return true; + } + + private int getEqualsTolerance() { + if (origin != null) { + return origin.departureEqualityTolerance; + } else { + return Station.DEFAULT_DEPARTURE_EQUALITY_TOLERANCE; + } + } + + public String getCountdownText() { + StringBuilder builder = new StringBuilder(); + int secondsLeft = getMeanSecondsLeft(); + if (hasDeparted()) { + if (origin != null && origin.longStationLinger && beganAsDeparted) { + builder.append("At station"); + } else { + builder.append(BartRunnerApplication.getAppContext().getString( + R.string.leaving)); + } + } else { + builder.append(secondsLeft / 60); + builder.append("m, "); + builder.append(secondsLeft % 60); + builder.append("s"); + } + return builder.toString(); + } + + public String getUncertaintyText() { + if (hasDeparted()) { + return ""; + } else { + return "(±" + getUncertaintySeconds() + "s)"; + } + } + + public int getAlarmLeadTimeMinutes() { + return alarmLeadTimeMinutes.getValue(); + } + + public Observable getAlarmLeadTimeMinutesObservable() { + return alarmLeadTimeMinutes; + } + + public boolean isAlarmPending() { + return alarmPending.getValue(); + } + + public Observable getAlarmPendingObservable() { + return alarmPending; + } + + private PendingIntent getAlarmIntent(Context context) { + return PendingIntent.getBroadcast(context, 0, new Intent( + Constants.ACTION_ALARM, getStationPair().getUri()), + PendingIntent.FLAG_UPDATE_CURRENT); + } + + private long getAlarmClockTime() { + return getMeanEstimate() - alarmLeadTimeMinutes.getValue() * 60 * 1000; + } + + public int getSecondsUntilAlarm() { + return getMeanSecondsLeft() - getAlarmLeadTimeMinutes() * 60; + } + + public void setUpAlarm(int leadTimeMinutes) { + this.alarmLeadTimeMinutes.setValue(leadTimeMinutes); + this.alarmPending.setValue(true); + } + + public void updateAlarm(Context context, AlarmManager alarmManager) { + if (alarmManager == null) + return; + + if (isAlarmPending() && getAlarmLeadTimeMinutes() > 0) { + final PendingIntent alarmIntent = getAlarmIntent(context); + alarmManager.cancel(alarmIntent); + + long alarmTime = getAlarmClockTime(); + + alarmManager.set(AlarmManager.RTC_WAKEUP, alarmTime, alarmIntent); + + if (Log.isLoggable(Constants.TAG, Log.VERBOSE)) + Log.v(Constants.TAG, + "Scheduling alarm for " + + DateFormatUtils.format(alarmTime, "h:mm:ss")); + } + } + + public void cancelAlarm(Context context, AlarmManager alarmManager) { + alarmManager.cancel(getAlarmIntent(context)); + this.alarmPending.setValue(false); + } + + private PendingIntent notificationIntent; + + private PendingIntent getNotificationIntent(Context context) { + if (notificationIntent == null) { + Intent targetIntent = new Intent(Intent.ACTION_VIEW, + getStationPair().getUri()); + targetIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + notificationIntent = PendingIntent.getActivity(context, 0, + targetIntent, PendingIntent.FLAG_UPDATE_CURRENT); + } + return notificationIntent; + } + + public Notification createNotification(Context context) { + final int halfMinutes = (getMeanSecondsLeft() + 15) / 30; + float minutes = halfMinutes / 2f; + final String minutesText = (minutes < 1) ? "Less than one minute" + : (String.format("~%.1f minute", minutes) + ((minutes != 1.0) ? "s" + : "")); + + final Intent cancelAlarmIntent = new Intent(context, + BoardedDepartureService.class); + cancelAlarmIntent.putExtra("cancelNotifications", true); + Builder notificationBuilder = new NotificationCompat.Builder(context) + .setOngoing(true) + .setSmallIcon(R.drawable.ic_stat_notification) + .setContentTitle( + getOrigin().shortName + " to " + + getPassengerDestination().shortName) + .setContentIntent(getNotificationIntent(context)).setWhen(0); + if (android.os.Build.VERSION.SDK_INT >= 16) { + notificationBuilder.setPriority(NotificationCompat.PRIORITY_HIGH) + .setContentText(minutesText + " until departure"); + if (isAlarmPending()) { + notificationBuilder.addAction( + R.drawable.ic_action_cancel_alarm, + "Cancel alarm", + PendingIntent.getService(context, 0, cancelAlarmIntent, + PendingIntent.FLAG_UPDATE_CURRENT)).setSubText( + "Alarm " + getAlarmLeadTimeMinutes() + + " minutes before departure"); + } + } else if (isAlarmPending()) { + notificationBuilder.setContentText(minutesText + + " to departure (alarm at " + getAlarmLeadTimeMinutes() + + " min" + ((getAlarmLeadTimeMinutes() == 1) ? "" : "s") + + ")"); + } else { + notificationBuilder + .setContentText(minutesText + " until departure"); + } + + return notificationBuilder.build(); + } + + @Override + public String toString() { + java.text.DateFormat format = SimpleDateFormat.getTimeInstance(); + StringBuilder builder = new StringBuilder(); + builder.append(trainDestination); + if (requiresTransfer) { + builder.append(" (w/ xfer)"); + } + builder.append(", "); + builder.append(getCountdownText()); + builder.append(", "); + builder.append(format.format(new Date(getMeanEstimate()))); + return builder.toString(); + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(origin.abbreviation); + dest.writeString(trainDestination.abbreviation); + dest.writeString(passengerDestination == null ? null + : passengerDestination.abbreviation); + dest.writeString(destinationColor); + dest.writeString(platform); + dest.writeString(direction); + dest.writeByte((byte) (bikeAllowed ? 1 : 0)); + dest.writeString(trainLength); + dest.writeByte((byte) (requiresTransfer ? 1 : 0)); + dest.writeInt(minutes); + dest.writeLong(minEstimate); + dest.writeLong(maxEstimate); + dest.writeLong(arrivalTimeOverride); + dest.writeInt(estimatedTripTime); + dest.writeInt(line.ordinal()); + dest.writeByte(beganAsDeparted ? (byte) 1 : (byte) 0); + dest.writeByte(bikeAllowed ? (byte) 1 : (byte) 0); + dest.writeByte(requiresTransfer ? (byte) 1 : (byte) 0); + dest.writeByte(transferScheduled ? (byte) 1 : (byte) 0); + } + + private void readFromParcel(Parcel in) { + origin = Station.getByAbbreviation(in.readString()); + trainDestination = Station.getByAbbreviation(in.readString()); + passengerDestination = Station.getByAbbreviation(in.readString()); + destinationColor = in.readString(); + platform = in.readString(); + direction = in.readString(); + bikeAllowed = in.readByte() != 0; + trainLength = in.readString(); + requiresTransfer = in.readByte() != 0; + minutes = in.readInt(); + minEstimate = in.readLong(); + maxEstimate = in.readLong(); + arrivalTimeOverride = in.readLong(); + estimatedTripTime = in.readInt(); + line = Line.values()[in.readInt()]; + beganAsDeparted = in.readByte() == (byte) 1; + bikeAllowed = in.readByte() == (byte) 1; + requiresTransfer = in.readByte() == (byte) 1; + transferScheduled = in.readByte() == (byte) 1; + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public Departure createFromParcel(Parcel in) { + return new Departure(in); + } + + public Departure[] newArray(int size) { + return new Departure[size]; + } + }; + + public void notifyAlarmHasBeenHandled() { + this.alarmPending.setValue(false); + } } \ No newline at end of file diff --git a/src/com/dougkeen/bart/model/Line.java b/src/com/dougkeen/bart/model/Line.java index 1f9ae29..7bcd425 100644 --- a/src/com/dougkeen/bart/model/Line.java +++ b/src/com/dougkeen/bart/model/Line.java @@ -1,113 +1,115 @@ -package com.dougkeen.bart.model; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; - -public enum Line { - RED(false, Station.MLBR, Station.SBRN, Station.SSAN, Station.COLM, - Station.DALY, Station.BALB, Station.GLEN, Station._24TH, - Station._16TH, Station.CIVC, Station.POWL, Station.MONT, - Station.EMBR, Station.WOAK, Station._12TH, Station._19TH, - Station.MCAR, Station.ASHB, Station.DBRK, Station.NBRK, - Station.PLZA, Station.DELN, Station.RICH), - ORANGE(false, Station.FRMT, Station.UCTY, Station.SHAY, Station.HAYW, - Station.BAYF, Station.SANL, Station.COLS, Station.FTVL, - Station.LAKE, Station._12TH, Station._19TH, Station.MCAR, - Station.ASHB, Station.DBRK, Station.NBRK, Station.PLZA, - Station.DELN, Station.RICH), - YELLOW(false, Station.MLBR, Station.SFIA, Station.SBRN, Station.SSAN, - Station.COLM, Station.DALY, Station.BALB, Station.GLEN, - Station._24TH, Station._16TH, Station.CIVC, Station.POWL, - Station.MONT, Station.EMBR, Station.WOAK, Station._12TH, - Station._19TH, Station.MCAR, Station.ROCK, Station.ORIN, - Station.LAFY, Station.WCRK, Station.PHIL, Station.CONC, - Station.NCON, Station.PITT), - BLUE(true, Station.DALY, Station.BALB, Station.GLEN, Station._24TH, - Station._16TH, Station.CIVC, Station.POWL, Station.MONT, - Station.EMBR, Station.WOAK, Station.LAKE, Station.FTVL, - Station.COLS, Station.SANL, Station.BAYF, Station.CAST, - Station.WDUB, Station.DUBL), - GREEN(true, Station.DALY, Station.BALB, Station.GLEN, Station._24TH, - Station._16TH, Station.CIVC, Station.POWL, Station.MONT, - Station.EMBR, Station.WOAK, Station.LAKE, Station.FTVL, - Station.COLS, Station.SANL, Station.BAYF, Station.HAYW, - Station.SHAY, Station.UCTY, Station.FRMT), - YELLOW_ORANGE_SCHEDULED_TRANSFER(YELLOW, ORANGE, Station.MLBR, - Station.SFIA, Station.SBRN, Station.SSAN, Station.COLM, - Station.DALY, Station.BALB, Station.GLEN, Station._24TH, - Station._16TH, Station.CIVC, Station.POWL, Station.MONT, - Station.EMBR, Station.WOAK, Station.ASHB, Station.DBRK, - Station.NBRK, Station.PLZA, Station.DELN, Station.RICH); - - public final List stations; - - protected final boolean directionMayInvert; - - protected final boolean requiresTransfer; - - protected final Line transferLine1; - - protected final Line transferLine2; - - private Line(boolean directionMayInvert, Station... stationArray) { - this.requiresTransfer = false; - this.directionMayInvert = directionMayInvert; - stations = Arrays.asList(stationArray); - this.transferLine1 = null; - this.transferLine2 = null; - } - - private Line(Line transferLine1, Line transferLine2, - Station... stationArray) { - this.requiresTransfer = true; - this.directionMayInvert = false; - stations = Arrays.asList(stationArray); - this.transferLine1 = transferLine1; - this.transferLine2 = transferLine2; - } - - private Line(boolean directionMayInvert, Line transferLine1, - Line transferLine2, Station... stationArray) { - this.requiresTransfer = true; - this.directionMayInvert = directionMayInvert; - stations = Arrays.asList(stationArray); - this.transferLine1 = transferLine1; - this.transferLine2 = transferLine2; - } - - public static Collection getLinesForStation(Station station) { - Collection lines = new ArrayList(); - for (Line line : Line.values()) { - if (line.stations.contains(station)) { - lines.add(line); - } - } - return lines; - } - - public static Collection getLinesWithStations(Station station1, Station station2) { - Collection lines = new ArrayList(); - for (Line line : Line.values()) { - if (line.stations.contains(station1) && line.stations.contains(station2)) { - lines.add(line); - } - } - return lines; - } - - public static Set getPotentialDestinations(Station station) { - Set destinations = new TreeSet(); - - for (Line line : getLinesForStation(station)) { - destinations.addAll(line.stations); - } - - destinations.remove(station); - - return destinations; - } -} +package com.dougkeen.bart.model; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +public enum Line { + RED(false, Station.MLBR, Station.SBRN, Station.SSAN, Station.COLM, + Station.DALY, Station.BALB, Station.GLEN, Station._24TH, + Station._16TH, Station.CIVC, Station.POWL, Station.MONT, + Station.EMBR, Station.WOAK, Station._12TH, Station._19TH, + Station.MCAR, Station.ASHB, Station.DBRK, Station.NBRK, + Station.PLZA, Station.DELN, Station.RICH), + ORANGE(false, Station.FRMT, Station.UCTY, Station.SHAY, Station.HAYW, + Station.BAYF, Station.SANL, Station.COLS, Station.FTVL, + Station.LAKE, Station._12TH, Station._19TH, Station.MCAR, + Station.ASHB, Station.DBRK, Station.NBRK, Station.PLZA, + Station.DELN, Station.RICH), + YELLOW(false, Station.MLBR, Station.SFIA, Station.SBRN, Station.SSAN, + Station.COLM, Station.DALY, Station.BALB, Station.GLEN, + Station._24TH, Station._16TH, Station.CIVC, Station.POWL, + Station.MONT, Station.EMBR, Station.WOAK, Station._12TH, + Station._19TH, Station.MCAR, Station.ROCK, Station.ORIN, + Station.LAFY, Station.WCRK, Station.PHIL, Station.CONC, + Station.NCON, Station.PITT), + BLUE(true, Station.DALY, Station.BALB, Station.GLEN, Station._24TH, + Station._16TH, Station.CIVC, Station.POWL, Station.MONT, + Station.EMBR, Station.WOAK, Station.LAKE, Station.FTVL, + Station.COLS, Station.SANL, Station.BAYF, Station.CAST, + Station.WDUB, Station.DUBL), + GREEN(true, Station.DALY, Station.BALB, Station.GLEN, Station._24TH, + Station._16TH, Station.CIVC, Station.POWL, Station.MONT, + Station.EMBR, Station.WOAK, Station.LAKE, Station.FTVL, + Station.COLS, Station.SANL, Station.BAYF, Station.HAYW, + Station.SHAY, Station.UCTY, Station.FRMT), + YELLOW_ORANGE_SCHEDULED_TRANSFER(YELLOW, ORANGE, Station.MLBR, + Station.SFIA, Station.SBRN, Station.SSAN, Station.COLM, + Station.DALY, Station.BALB, Station.GLEN, Station._24TH, + Station._16TH, Station.CIVC, Station.POWL, Station.MONT, + Station.EMBR, Station.WOAK, Station.ASHB, Station.DBRK, + Station.NBRK, Station.PLZA, Station.DELN, Station.RICH); + + public final List stations; + + protected final boolean directionMayInvert; + + protected final boolean requiresTransfer; + + protected final Line transferLine1; + + protected final Line transferLine2; + + private Line(boolean directionMayInvert, Station... stationArray) { + this.requiresTransfer = false; + this.directionMayInvert = directionMayInvert; + stations = Arrays.asList(stationArray); + this.transferLine1 = null; + this.transferLine2 = null; + } + + private Line(Line transferLine1, Line transferLine2, + Station... stationArray) { + this.requiresTransfer = true; + this.directionMayInvert = false; + stations = Arrays.asList(stationArray); + this.transferLine1 = transferLine1; + this.transferLine2 = transferLine2; + } + + private Line(boolean directionMayInvert, Line transferLine1, + Line transferLine2, Station... stationArray) { + this.requiresTransfer = true; + this.directionMayInvert = directionMayInvert; + stations = Arrays.asList(stationArray); + this.transferLine1 = transferLine1; + this.transferLine2 = transferLine2; + } + + public static Collection getLinesForStation(Station station) { + Collection lines = new ArrayList(); + for (Line line : Line.values()) { + if (line.stations.contains(station)) { + lines.add(line); + } + } + return lines; + } + + public static Collection getLinesWithStations(Station station1, + Station station2) { + Collection lines = new ArrayList(); + for (Line line : Line.values()) { + if (line.stations.contains(station1) + && line.stations.contains(station2)) { + lines.add(line); + } + } + return lines; + } + + public static Set getPotentialDestinations(Station station) { + Set destinations = new TreeSet(); + + for (Line line : getLinesForStation(station)) { + destinations.addAll(line.stations); + } + + destinations.remove(station); + + return destinations; + } +} diff --git a/src/com/dougkeen/bart/model/RealTimeDepartures.java b/src/com/dougkeen/bart/model/RealTimeDepartures.java index 9513dbf..363095c 100644 --- a/src/com/dougkeen/bart/model/RealTimeDepartures.java +++ b/src/com/dougkeen/bart/model/RealTimeDepartures.java @@ -1,152 +1,152 @@ -package com.dougkeen.bart.model; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -public class RealTimeDepartures { - public RealTimeDepartures(Station origin, Station destination, - List routes) { - this.origin = origin; - this.destination = destination; - this.routes = routes; - this.unfilteredDepartures = new ArrayList(); - } - - private Station origin; - private Station destination; - private long time; - - private List departures; - - final private List unfilteredDepartures; - - private List routes; - - public Station getOrigin() { - return origin; - } - - public void setOrigin(Station origin) { - this.origin = origin; - } - - public Station getDestination() { - return destination; - } - - public void setDestination(Station destination) { - this.destination = destination; - } - - public long getTime() { - return time; - } - - public void setTime(long time) { - this.time = time; - } - - public List getDepartures() { - if (departures == null) { - departures = new ArrayList(); - } - return departures; - } - - public void setDepartures(List departures) { - this.departures = departures; - } - - public void includeTransferRoutes() { - routes.addAll(origin.getTransferRoutes(destination)); - rebuildFilteredDepaturesCollection(); - } - - public void includeDoubleTransferRoutes() { - routes.addAll(origin.getDoubleTransferRoutes(destination)); - rebuildFilteredDepaturesCollection(); - } - - private void rebuildFilteredDepaturesCollection() { - getDepartures().clear(); - for (Departure departure : unfilteredDepartures) { - addDepartureIfApplicable(departure); - } - } - - public void addDeparture(Departure departure) { - unfilteredDepartures.add(departure); - addDepartureIfApplicable(departure); - } - - private void addDepartureIfApplicable(Departure departure) { - Station destination = Station.getByAbbreviation(departure - .getTrainDestinationAbbreviation()); - if (departure.getLine() == null) - return; - for (Route route : routes) { - if (route.trainDestinationIsApplicable(destination, - departure.getLine())) { - departure.setRequiresTransfer(route.hasTransfer()); - departure - .setTransferScheduled(Line.YELLOW_ORANGE_SCHEDULED_TRANSFER - .equals(route.getDirectLine())); - getDepartures().add(departure); - departure.calculateEstimates(time); - return; - } - } - } - - public void sortDepartures() { - Collections.sort(getDepartures()); - } - - public void finalizeDeparturesList() { - boolean hasDirectRoute = false; - for (Departure departure : getDepartures()) { - if (!departure.getRequiresTransfer()) { - hasDirectRoute = true; - break; - } - } - if (hasDirectRoute) { - Iterator iterator = getDepartures().iterator(); - while (iterator.hasNext()) { - Departure departure = iterator.next(); - if (departure.getRequiresTransfer() - && (!departure.isTransferScheduled() || departure - .getTrainDestination().isBetween(getOrigin(), - getDestination(), departure.getLine()))) { - iterator.remove(); - } - } - } - sortDepartures(); - } - - public List getRoutes() { - return routes; - } - - public void setRoutes(List routes) { - this.routes = routes; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("RealTimeDepartures [origin="); - builder.append(origin); - builder.append(", destination="); - builder.append(destination); - builder.append(", time="); - builder.append(time); - builder.append(", departures="); - builder.append(departures); - builder.append("]"); - return builder.toString(); - } -} +package com.dougkeen.bart.model; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +public class RealTimeDepartures { + public RealTimeDepartures(Station origin, Station destination, + List routes) { + this.origin = origin; + this.destination = destination; + this.routes = routes; + this.unfilteredDepartures = new ArrayList(); + } + + private Station origin; + private Station destination; + private long time; + + private List departures; + + final private List unfilteredDepartures; + + private List routes; + + public Station getOrigin() { + return origin; + } + + public void setOrigin(Station origin) { + this.origin = origin; + } + + public Station getDestination() { + return destination; + } + + public void setDestination(Station destination) { + this.destination = destination; + } + + public long getTime() { + return time; + } + + public void setTime(long time) { + this.time = time; + } + + public List getDepartures() { + if (departures == null) { + departures = new ArrayList(); + } + return departures; + } + + public void setDepartures(List departures) { + this.departures = departures; + } + + public void includeTransferRoutes() { + routes.addAll(origin.getTransferRoutes(destination)); + rebuildFilteredDepaturesCollection(); + } + + public void includeDoubleTransferRoutes() { + routes.addAll(origin.getDoubleTransferRoutes(destination)); + rebuildFilteredDepaturesCollection(); + } + + private void rebuildFilteredDepaturesCollection() { + getDepartures().clear(); + for (Departure departure : unfilteredDepartures) { + addDepartureIfApplicable(departure); + } + } + + public void addDeparture(Departure departure) { + unfilteredDepartures.add(departure); + addDepartureIfApplicable(departure); + } + + private void addDepartureIfApplicable(Departure departure) { + Station destination = Station.getByAbbreviation(departure + .getTrainDestinationAbbreviation()); + if (departure.getLine() == null) + return; + for (Route route : routes) { + if (route.trainDestinationIsApplicable(destination, + departure.getLine())) { + departure.setRequiresTransfer(route.hasTransfer()); + departure + .setTransferScheduled(Line.YELLOW_ORANGE_SCHEDULED_TRANSFER + .equals(route.getDirectLine())); + getDepartures().add(departure); + departure.calculateEstimates(time); + return; + } + } + } + + public void sortDepartures() { + Collections.sort(getDepartures()); + } + + public void finalizeDeparturesList() { + boolean hasDirectRoute = false; + for (Departure departure : getDepartures()) { + if (!departure.getRequiresTransfer()) { + hasDirectRoute = true; + break; + } + } + if (hasDirectRoute) { + Iterator iterator = getDepartures().iterator(); + while (iterator.hasNext()) { + Departure departure = iterator.next(); + if (departure.getRequiresTransfer() + && (!departure.isTransferScheduled() || departure + .getTrainDestination().isBetween(getOrigin(), + getDestination(), departure.getLine()))) { + iterator.remove(); + } + } + } + sortDepartures(); + } + + public List getRoutes() { + return routes; + } + + public void setRoutes(List routes) { + this.routes = routes; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("RealTimeDepartures [origin="); + builder.append(origin); + builder.append(", destination="); + builder.append(destination); + builder.append(", time="); + builder.append(time); + builder.append(", departures="); + builder.append(departures); + builder.append("]"); + return builder.toString(); + } +} diff --git a/src/com/dougkeen/bart/model/Route.java b/src/com/dougkeen/bart/model/Route.java index 020557c..3b0e3f6 100644 --- a/src/com/dougkeen/bart/model/Route.java +++ b/src/com/dougkeen/bart/model/Route.java @@ -1,111 +1,111 @@ -package com.dougkeen.bart.model; - -import java.util.Collection; - -public class Route { - private Station origin; - private Station destination; - private Line directLine; - private Collection transferLines; - private boolean requiresTransfer; - private Station transferStation; - private String direction; - - public Station getOrigin() { - return origin; - } - - public void setOrigin(Station origin) { - this.origin = origin; - } - - public Station getDestination() { - return destination; - } - - public void setDestination(Station destination) { - this.destination = destination; - } - - public Line getDirectLine() { - return directLine; - } - - public void setDirectLine(Line line) { - this.directLine = line; - } - - public Collection getTransferLines() { - return transferLines; - } - - public void setTransferLines(Collection transferLines) { - this.transferLines = transferLines; - } - - public boolean hasTransfer() { - return requiresTransfer; - } - - public void setTransfer(boolean requiresTransfer) { - this.requiresTransfer = requiresTransfer; - } - - public Station getTransferStation() { - return transferStation; - } - - public void setTransferStation(Station transferStation) { - this.transferStation = transferStation; - } - - public String getDirection() { - return direction; - } - - public void setDirection(String direction) { - this.direction = direction; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("Route [origin="); - builder.append(origin); - builder.append(", destination="); - builder.append(destination); - builder.append(", line="); - builder.append(directLine); - builder.append(", requiresTransfer="); - builder.append(requiresTransfer); - builder.append(", transferStation="); - builder.append(transferStation); - builder.append(", direction="); - builder.append(direction); - builder.append("]"); - return builder.toString(); - } - - public boolean trainDestinationIsApplicable(Station lineDestination, - Line viaLine) { - Line routeLine = getDirectLine(); - if (routeLine.transferLine1 != null - && viaLine.equals(routeLine.transferLine1)) { - return true; - } else if (routeLine.transferLine2 != null - && viaLine.equals(routeLine.transferLine2)) { - return true; - } else if (requiresTransfer && transferLines != null - && !transferLines.isEmpty()) { - return transferLines.contains(viaLine); - } else { - int originIndex = viaLine.stations.indexOf(origin); - int routeDestinationIndex = viaLine.stations.indexOf(destination); - int lineDestinationIndex = viaLine.stations - .indexOf(lineDestination); - return routeDestinationIndex >= 0 - && ((originIndex <= routeDestinationIndex && routeDestinationIndex <= lineDestinationIndex) || (originIndex >= routeDestinationIndex && routeDestinationIndex >= lineDestinationIndex)); - } - } - -} +package com.dougkeen.bart.model; + +import java.util.Collection; + +public class Route { + private Station origin; + private Station destination; + private Line directLine; + private Collection transferLines; + private boolean requiresTransfer; + private Station transferStation; + private String direction; + + public Station getOrigin() { + return origin; + } + + public void setOrigin(Station origin) { + this.origin = origin; + } + + public Station getDestination() { + return destination; + } + + public void setDestination(Station destination) { + this.destination = destination; + } + + public Line getDirectLine() { + return directLine; + } + + public void setDirectLine(Line line) { + this.directLine = line; + } + + public Collection getTransferLines() { + return transferLines; + } + + public void setTransferLines(Collection transferLines) { + this.transferLines = transferLines; + } + + public boolean hasTransfer() { + return requiresTransfer; + } + + public void setTransfer(boolean requiresTransfer) { + this.requiresTransfer = requiresTransfer; + } + + public Station getTransferStation() { + return transferStation; + } + + public void setTransferStation(Station transferStation) { + this.transferStation = transferStation; + } + + public String getDirection() { + return direction; + } + + public void setDirection(String direction) { + this.direction = direction; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("Route [origin="); + builder.append(origin); + builder.append(", destination="); + builder.append(destination); + builder.append(", line="); + builder.append(directLine); + builder.append(", requiresTransfer="); + builder.append(requiresTransfer); + builder.append(", transferStation="); + builder.append(transferStation); + builder.append(", direction="); + builder.append(direction); + builder.append("]"); + return builder.toString(); + } + + public boolean trainDestinationIsApplicable(Station lineDestination, + Line viaLine) { + Line routeLine = getDirectLine(); + if (routeLine.transferLine1 != null + && viaLine.equals(routeLine.transferLine1)) { + return true; + } else if (routeLine.transferLine2 != null + && viaLine.equals(routeLine.transferLine2)) { + return true; + } else if (requiresTransfer && transferLines != null + && !transferLines.isEmpty()) { + return transferLines.contains(viaLine); + } else { + int originIndex = viaLine.stations.indexOf(origin); + int routeDestinationIndex = viaLine.stations.indexOf(destination); + int lineDestinationIndex = viaLine.stations + .indexOf(lineDestination); + return routeDestinationIndex >= 0 + && ((originIndex <= routeDestinationIndex && routeDestinationIndex <= lineDestinationIndex) || (originIndex >= routeDestinationIndex && routeDestinationIndex >= lineDestinationIndex)); + } + } + +} diff --git a/src/com/dougkeen/bart/model/ScheduleInformation.java b/src/com/dougkeen/bart/model/ScheduleInformation.java index 6bede36..5fb3b91 100644 --- a/src/com/dougkeen/bart/model/ScheduleInformation.java +++ b/src/com/dougkeen/bart/model/ScheduleInformation.java @@ -1,89 +1,89 @@ -package com.dougkeen.bart.model; - -import java.util.ArrayList; -import java.util.List; - -public class ScheduleInformation { - - public ScheduleInformation(Station origin, Station destination) { - super(); - this.origin = origin; - this.destination = destination; - } - - private Station origin; - private Station destination; - private long date; - private List trips; - - public Station getOrigin() { - return origin; - } - - public void setOrigin(Station origin) { - this.origin = origin; - } - - public Station getDestination() { - return destination; - } - - public void setDestination(Station destination) { - this.destination = destination; - } - - public long getDate() { - return date; - } - - public void setDate(long date) { - this.date = date; - } - - public List getTrips() { - if (trips == null) { - trips = new ArrayList(); - } - return trips; - } - - public void setTrips(List trips) { - this.trips = trips; - } - - public void addTrip(ScheduleItem trip) { - getTrips().add(trip); - } - - public long getLatestDepartureTime() { - if (getTrips().isEmpty()) - return -1; - else - return getTrips().get(getTrips().size() - 1).getDepartureTime(); - } - - private int aveTripLength = -1; - private int tripCount = 0; - - public int getAverageTripLength() { - if (aveTripLength < 0) { - int sum = 0; - for (ScheduleItem trip : getTrips()) { - int tripLength = trip.getTripLength(); - if (tripLength > 0) { - sum += tripLength; - tripCount++; - } - } - if (tripCount > 0) { - aveTripLength = sum / tripCount; - } - } - return aveTripLength; - } - - public int getTripCountForAverage() { - getAverageTripLength(); - return tripCount; - } +package com.dougkeen.bart.model; + +import java.util.ArrayList; +import java.util.List; + +public class ScheduleInformation { + + public ScheduleInformation(Station origin, Station destination) { + super(); + this.origin = origin; + this.destination = destination; + } + + private Station origin; + private Station destination; + private long date; + private List trips; + + public Station getOrigin() { + return origin; + } + + public void setOrigin(Station origin) { + this.origin = origin; + } + + public Station getDestination() { + return destination; + } + + public void setDestination(Station destination) { + this.destination = destination; + } + + public long getDate() { + return date; + } + + public void setDate(long date) { + this.date = date; + } + + public List getTrips() { + if (trips == null) { + trips = new ArrayList(); + } + return trips; + } + + public void setTrips(List trips) { + this.trips = trips; + } + + public void addTrip(ScheduleItem trip) { + getTrips().add(trip); + } + + public long getLatestDepartureTime() { + if (getTrips().isEmpty()) + return -1; + else + return getTrips().get(getTrips().size() - 1).getDepartureTime(); + } + + private int aveTripLength = -1; + private int tripCount = 0; + + public int getAverageTripLength() { + if (aveTripLength < 0) { + int sum = 0; + for (ScheduleItem trip : getTrips()) { + int tripLength = trip.getTripLength(); + if (tripLength > 0) { + sum += tripLength; + tripCount++; + } + } + if (tripCount > 0) { + aveTripLength = sum / tripCount; + } + } + return aveTripLength; + } + + public int getTripCountForAverage() { + getAverageTripLength(); + return tripCount; + } } \ No newline at end of file diff --git a/src/com/dougkeen/bart/model/ScheduleItem.java b/src/com/dougkeen/bart/model/ScheduleItem.java index 7d0ca96..d5144f5 100644 --- a/src/com/dougkeen/bart/model/ScheduleItem.java +++ b/src/com/dougkeen/bart/model/ScheduleItem.java @@ -1,115 +1,115 @@ -package com.dougkeen.bart.model; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; - -public class ScheduleItem { - - public ScheduleItem() { - super(); - } - - public ScheduleItem(Station origin, Station destination) { - super(); - this.origin = origin; - this.destination = destination; - } - - public static final int SCHEDULE_ITEM_DEPARTURE_EQUALS_TOLERANCE = 120000; - - private Station origin; - private Station destination; - private String fare; - private long departureTime; - private long arrivalTime; - private boolean bikesAllowed; - private String trainHeadStation; - - public Station getOrigin() { - return origin; - } - - public void setOrigin(Station origin) { - this.origin = origin; - } - - public Station getDestination() { - return destination; - } - - public void setDestination(Station destination) { - this.destination = destination; - } - - public String getFare() { - return fare; - } - - public void setFare(String fare) { - this.fare = fare; - } - - public long getDepartureTime() { - return departureTime; - } - - public void setDepartureTime(long departureTime) { - this.departureTime = departureTime; - } - - public long getArrivalTime() { - return arrivalTime; - } - - public void setArrivalTime(long arrivalTime) { - this.arrivalTime = arrivalTime; - } - - public int getTripLength() { - if (departureTime <= 0 || arrivalTime <= 0) { - return 0; - } else { - return (int) (arrivalTime - departureTime); - } - } - - public boolean isBikesAllowed() { - return bikesAllowed; - } - - public void setBikesAllowed(boolean bikesAllowed) { - this.bikesAllowed = bikesAllowed; - } - - public String getTrainHeadStation() { - return trainHeadStation; - } - - public void setTrainHeadStation(String trainHeadStation) { - this.trainHeadStation = trainHeadStation; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - DateFormat format = SimpleDateFormat.getTimeInstance(); - builder.append("ScheduleItem [origin="); - builder.append(origin); - builder.append(", destination="); - builder.append(destination); - builder.append(", fare="); - builder.append(fare); - builder.append(", departureTime="); - builder.append(format.format(new Date(departureTime))); - builder.append(", arrivalTime="); - builder.append(format.format(new Date(arrivalTime))); - builder.append(", bikesAllowed="); - builder.append(bikesAllowed); - builder.append(", trainHeadStation="); - builder.append(trainHeadStation); - builder.append("]"); - return builder.toString(); - } - -} +package com.dougkeen.bart.model; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class ScheduleItem { + + public ScheduleItem() { + super(); + } + + public ScheduleItem(Station origin, Station destination) { + super(); + this.origin = origin; + this.destination = destination; + } + + public static final int SCHEDULE_ITEM_DEPARTURE_EQUALS_TOLERANCE = 120000; + + private Station origin; + private Station destination; + private String fare; + private long departureTime; + private long arrivalTime; + private boolean bikesAllowed; + private String trainHeadStation; + + public Station getOrigin() { + return origin; + } + + public void setOrigin(Station origin) { + this.origin = origin; + } + + public Station getDestination() { + return destination; + } + + public void setDestination(Station destination) { + this.destination = destination; + } + + public String getFare() { + return fare; + } + + public void setFare(String fare) { + this.fare = fare; + } + + public long getDepartureTime() { + return departureTime; + } + + public void setDepartureTime(long departureTime) { + this.departureTime = departureTime; + } + + public long getArrivalTime() { + return arrivalTime; + } + + public void setArrivalTime(long arrivalTime) { + this.arrivalTime = arrivalTime; + } + + public int getTripLength() { + if (departureTime <= 0 || arrivalTime <= 0) { + return 0; + } else { + return (int) (arrivalTime - departureTime); + } + } + + public boolean isBikesAllowed() { + return bikesAllowed; + } + + public void setBikesAllowed(boolean bikesAllowed) { + this.bikesAllowed = bikesAllowed; + } + + public String getTrainHeadStation() { + return trainHeadStation; + } + + public void setTrainHeadStation(String trainHeadStation) { + this.trainHeadStation = trainHeadStation; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + DateFormat format = SimpleDateFormat.getTimeInstance(); + builder.append("ScheduleItem [origin="); + builder.append(origin); + builder.append(", destination="); + builder.append(destination); + builder.append(", fare="); + builder.append(fare); + builder.append(", departureTime="); + builder.append(format.format(new Date(departureTime))); + builder.append(", arrivalTime="); + builder.append(format.format(new Date(arrivalTime))); + builder.append(", bikesAllowed="); + builder.append(bikesAllowed); + builder.append(", trainHeadStation="); + builder.append(trainHeadStation); + builder.append("]"); + return builder.toString(); + } + +} diff --git a/src/com/dougkeen/bart/model/Station.java b/src/com/dougkeen/bart/model/Station.java index 3309e56..a85050b 100644 --- a/src/com/dougkeen/bart/model/Station.java +++ b/src/com/dougkeen/bart/model/Station.java @@ -1,287 +1,286 @@ -package com.dougkeen.bart.model; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import android.util.Log; - -public enum Station { - _12TH("12th", "12th St./Oakland City Center", "12th St Oak", false, false, - "bayf", "bayf"), - _16TH("16th", "16th St. Mission", "16th St", false, false), - _19TH("19th", "19th St./Oakland", "19th St Oak", false, false, "bayf", - "bayf"), - _24TH("24th", "24th St. Mission", "24th St", false, false), - ASHB("ashb", "Ashby", "Ashby", false, false, "mcar", "mcar"), - BALB("balb", "Balboa Park", "Balboa", false, false), - BAYF("bayf", "Bay Fair", "Bay Fair", true, false, "mcar", "mcar"), - CAST("cast", "Castro Valley", "Castro Vly", false, false, "bayf", "bayf"), - CIVC("civc", "Civic Center", "Civic Ctr", false, false), - COLS("cols", "Coliseum/Oakland Airport", "Coliseum/OAK", true, false, - "mcar", "mcar"), - COLM("colm", "Colma", "Colma", false, false, "balb", "balb"), - CONC("conc", "Concord", "Concord", false, false, "mcar", "mcar"), - DALY("daly", "Daly City", "Daly City", false, false), - DBRK("dbrk", "Downtown Berkeley", "Dtwn Berk", false, false, "mcar", - "mcar"), - DUBL("dubl", "Dublin/Pleasanton", "Dbln/Plsntn", false, true, "bayf", - "bayf", true, 719999), - DELN("deln", "El Cerrito del Norte", "El Cer/Norte", false, false, "mcar", - "mcar"), - PLZA("plza", "El Cerrito Plaza", "El Cer/Plz", false, false, "mcar", "mcar"), - EMBR("embr", "Embarcadero", "Embarcdro", false, false), - FRMT("frmt", "Fremont", "Fremont", true, true, "bayf", "bayf", true, 299999), - FTVL("ftvl", "Fruitvale", "Fruitvale", true, false, "mcar", "mcar"), - GLEN("glen", "Glen Park", "Glen Park", false, false), - HAYW("hayw", "Hayward", "Hayward", true, false, "bayf", "bayf"), - LAFY("lafy", "Lafayette", "Lafayette", false, false, "mcar", "mcar"), - LAKE("lake", "Lake Merritt", "Lk Merritt", true, false, "mcar", "mcar"), - MCAR("mcar", "MacArthur", "MacArthur", false, false, "bayf", "bayf"), - MLBR("mlbr", "Millbrae", "Millbrae", false, true, "balb", "balb", true, - 719999), - MONT("mont", "Montgomery St.", "Montgomery", false, false), - NBRK("nbrk", "North Berkeley", "N Berkeley", false, false, "mcar", "mcar"), - NCON("ncon", "North Concord/Martinez", "N Conc/Mrtnz", false, false, - "mcar", "mcar"), - ORIN("orin", "Orinda", "Orinda", false, false, "mcar", "mcar"), - PITT("pitt", "Pittsburg/Bay Point", "Pitt/Bay Pt", false, true, "mcar", - "mcar", true, 719999), - PHIL("phil", "Pleasant Hill", "Plsnt Hill", false, false, "mcar", "mcar"), - POWL("powl", "Powell St.", "Powell", false, false), - RICH("rich", "Richmond", "Richmond", false, true, "mcar", "mcar", true, - 299999), - ROCK("rock", "Rockridge", "Rockridge", false, false, "mcar", "mcar"), - SBRN("sbrn", "San Bruno", "San Bruno", false, false, "balb", "balb"), - SANL("sanl", "San Leandro", "San Leandro", true, false, "mcar", "mcar"), - SFIA("sfia", "SFO Airport", "SFO", false, false, "balb", "balb", true, - 719999), - SHAY("shay", "South Hayward", "S Hayward", true, false, "bayf", "bayf"), - SSAN("ssan", "South San Francisco", "S San Fran", false, false, "balb", - "balb"), - UCTY("ucty", "Union City", "Union City", true, false, "bayf", "bayf"), - WCRK("wcrk", "Walnut Creek", "Walnut Crk", false, false, "mcar", "mcar"), - WDUB("wdub", "West Dublin/Pleasanton", "W Dbln/Plsntn", false, false, - "bayf", "bayf"), - WOAK("woak", "West Oakland", "W Oakland", false, false), - SPCL("spcl", "Special", "Special", false, false); - - public final String abbreviation; - public final String name; - public final String shortName; - public final boolean transferFriendly; - public final boolean invertDirection; - protected final String inboundTransferStation; - protected final String outboundTransferStation; - public final boolean endOfLine; - public final boolean longStationLinger; - public final int departureEqualityTolerance; - - public final static int DEFAULT_DEPARTURE_EQUALITY_TOLERANCE = 119999; - - private Station(String abbreviation, String name, String shortName, - boolean invertDirection, boolean endOfLine) { - this(abbreviation, name, shortName, invertDirection, endOfLine, null, - null, false, DEFAULT_DEPARTURE_EQUALITY_TOLERANCE); - } - - private Station(String abbreviation, String name, String shortName, - boolean invertDirection, boolean endOfLine, String transferStation) { - this(abbreviation, name, shortName, invertDirection, endOfLine, - transferStation, null, false, - DEFAULT_DEPARTURE_EQUALITY_TOLERANCE); - } - - private Station(String abbreviation, String name, String shortName, - boolean invertDirection, boolean endOfLine, - String inboundTransferStation, String outboundTransferStation) { - this(abbreviation, name, shortName, invertDirection, endOfLine, - inboundTransferStation, outboundTransferStation, false, - DEFAULT_DEPARTURE_EQUALITY_TOLERANCE); - } - - private Station(String abbreviation, String name, String shortName, - boolean invertDirection, boolean endOfLine, - String inboundTransferStation, String outboundTransferStation, - boolean longStationLinger, int departureEqualityTolerance) { - this.abbreviation = abbreviation; - this.name = name; - this.shortName = shortName; - this.invertDirection = invertDirection; - this.inboundTransferStation = inboundTransferStation; - this.transferFriendly = outboundTransferStation != null; - this.outboundTransferStation = outboundTransferStation; - this.endOfLine = endOfLine; - this.longStationLinger = longStationLinger; - this.departureEqualityTolerance = departureEqualityTolerance; - } - - public static Station getByAbbreviation(String abbr) { - try { - if (abbr == null) { - return null; - } else if (Character.isDigit(abbr.charAt(0))) { - return Station.valueOf("_" + abbr.toUpperCase()); - } else { - return Station.valueOf(abbr.toUpperCase()); - } - } catch (IllegalArgumentException e) { - Log.e(Constants.TAG, "Could not find station for '" + abbr + "'", e); - return null; - } - } - - public Station getInboundTransferStation() { - return getByAbbreviation(inboundTransferStation); - } - - public Station getOutboundTransferStation() { - return getByAbbreviation(outboundTransferStation); - } - - public boolean isValidEndpointForDestination(Station dest, Station endpoint) { - for (Line line : Line.values()) { - int origIndex = line.stations.indexOf(this); - if (origIndex < 0) - continue; - int destIndex = line.stations.indexOf(dest); - if (destIndex < 0) - continue; - int endpointIndex = line.stations.indexOf(endpoint); - if (endpointIndex >= 0) - return true; - } - return false; - } - - public List getDirectRoutesForDestination(Station dest) { - return getDirectRoutesForDestination(this, dest, null, null); - } - - public List getDirectRoutesForDestination(Station origin, - Station dest, Station transferStation, - Collection transferLines) { - if (dest == null) - return null; - Boolean isNorth = null; - List returnList = new ArrayList(); - final Collection applicableLines = Line.getLinesWithStations( - this, dest); - if (transferLines != null && !transferLines.isEmpty()) { - for (Line transferLine : transferLines) { - int origIndex = transferLine.stations.indexOf(origin); - int destIndex = transferLine.stations.indexOf(origin - .getOutboundTransferStation()); - - isNorth = (origIndex < destIndex); - if (origin.invertDirection && transferLine.directionMayInvert) { - isNorth = !isNorth; - break; - } - } - } - for (Line line : applicableLines) { - if (transferLines == null || transferLines.isEmpty()) { - int origIndex = line.stations.indexOf(this); - int destIndex = line.stations.indexOf(dest); - - isNorth = (origIndex < destIndex); - if (line.directionMayInvert && this.invertDirection) { - isNorth = !isNorth; - } - } - Route route = new Route(); - route.setOrigin(origin); - route.setDirectLine(line); - if (this.equals(origin)) { - route.setDestination(dest); - } else { - // This must be the outbound transfer station - route.setDestination(origin.getOutboundTransferStation()); - route.setTransferLines(transferLines); - } - route.setDirection(isNorth ? "n" : "s"); - if (transferStation != null || line.requiresTransfer) { - route.setTransfer(true); - } else { - route.setTransfer(false); - } - - returnList.add(route); - } - return returnList; - } - - public List getTransferRoutes(Station dest) { - List returnList = new ArrayList(); - - if (dest.getInboundTransferStation() != null) { - // Try getting to the destination's inbound xfer station first - returnList.addAll(getDirectRoutesForDestination(this, - dest.getInboundTransferStation(), - dest.getInboundTransferStation(), null)); - } - - if (returnList.isEmpty() && outboundTransferStation != null) { - // Try getting from the outbound transfer station to the - // destination next - final Collection outboundTransferLines = Line - .getLinesWithStations(this, getOutboundTransferStation()); - final List routesForDestination = getOutboundTransferStation() - .getDirectRoutesForDestination(this, dest, - getOutboundTransferStation(), outboundTransferLines); - if (routesForDestination != null && !routesForDestination.isEmpty()) { - returnList.addAll(routesForDestination); - } - } - - if (returnList.isEmpty()) { - // Try getting from the outbound transfer station to the - // destination's inbound xfer station - final List routesForDestination = getDoubleTransferRoutes(dest); - if (routesForDestination != null && !routesForDestination.isEmpty()) { - returnList.addAll(routesForDestination); - } - } - - return returnList; - } - - public List getDoubleTransferRoutes(Station dest) { - if (getOutboundTransferStation() == null - || dest.getInboundTransferStation() == null) - return new ArrayList(); - - // Get routes from the outbound transfer station to the - // destination's inbound xfer station - return getOutboundTransferStation().getDirectRoutesForDestination(this, - dest.getInboundTransferStation(), getOutboundTransferStation(), - Line.getLinesWithStations(this, getOutboundTransferStation())); - } - - static public List getStationList() { - List list = new ArrayList(); - for (Station station : values()) { - if (!station.equals(Station.SPCL)) { - list.add(station); - } - } - return list; - } - - public String toString() { - return name; - } - - public boolean isBetween(Station origin, Station destination, Line line) { - int originIndex = line.stations.indexOf(origin); - int destinationIndex = line.stations.indexOf(destination); - int stationIndex = line.stations.indexOf(this); - if (originIndex < 0 || destinationIndex < 0 || stationIndex < 0) { - return false; - } - - return Math.abs(stationIndex - originIndex) < Math.abs(destinationIndex - - originIndex); - } -} +package com.dougkeen.bart.model; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import android.util.Log; + +public enum Station { + _12TH("12th", "12th St./Oakland City Center", "12th St Oak", false, false, + "bayf", "bayf"), + _16TH("16th", "16th St. Mission", "16th St", false, false), + _19TH("19th", "19th St./Oakland", "19th St Oak", false, false, "bayf", + "bayf"), + _24TH("24th", "24th St. Mission", "24th St", false, false), + ASHB("ashb", "Ashby", "Ashby", false, false, "mcar", "mcar"), + BALB("balb", "Balboa Park", "Balboa", false, false), + BAYF("bayf", "Bay Fair", "Bay Fair", true, false, "mcar", "mcar"), + CAST("cast", "Castro Valley", "Castro Vly", false, false, "bayf", "bayf"), + CIVC("civc", "Civic Center", "Civic Ctr", false, false), + COLS("cols", "Coliseum/Oakland Airport", "Coliseum/OAK", true, false, + "mcar", "mcar"), + COLM("colm", "Colma", "Colma", false, false, "balb", "balb"), + CONC("conc", "Concord", "Concord", false, false, "mcar", "mcar"), + DALY("daly", "Daly City", "Daly City", false, false), + DBRK("dbrk", "Downtown Berkeley", "Dtwn Berk", false, false, "mcar", "mcar"), + DUBL("dubl", "Dublin/Pleasanton", "Dbln/Plsntn", false, true, "bayf", + "bayf", true, 719999), + DELN("deln", "El Cerrito del Norte", "El Cer/Norte", false, false, "mcar", + "mcar"), + PLZA("plza", "El Cerrito Plaza", "El Cer/Plz", false, false, "mcar", "mcar"), + EMBR("embr", "Embarcadero", "Embarcdro", false, false), + FRMT("frmt", "Fremont", "Fremont", true, true, "bayf", "bayf", true, 299999), + FTVL("ftvl", "Fruitvale", "Fruitvale", true, false, "mcar", "mcar"), + GLEN("glen", "Glen Park", "Glen Park", false, false), + HAYW("hayw", "Hayward", "Hayward", true, false, "bayf", "bayf"), + LAFY("lafy", "Lafayette", "Lafayette", false, false, "mcar", "mcar"), + LAKE("lake", "Lake Merritt", "Lk Merritt", true, false, "mcar", "mcar"), + MCAR("mcar", "MacArthur", "MacArthur", false, false, "bayf", "bayf"), + MLBR("mlbr", "Millbrae", "Millbrae", false, true, "balb", "balb", true, + 719999), + MONT("mont", "Montgomery St.", "Montgomery", false, false), + NBRK("nbrk", "North Berkeley", "N Berkeley", false, false, "mcar", "mcar"), + NCON("ncon", "North Concord/Martinez", "N Conc/Mrtnz", false, false, + "mcar", "mcar"), + ORIN("orin", "Orinda", "Orinda", false, false, "mcar", "mcar"), + PITT("pitt", "Pittsburg/Bay Point", "Pitt/Bay Pt", false, true, "mcar", + "mcar", true, 719999), + PHIL("phil", "Pleasant Hill", "Plsnt Hill", false, false, "mcar", "mcar"), + POWL("powl", "Powell St.", "Powell", false, false), + RICH("rich", "Richmond", "Richmond", false, true, "mcar", "mcar", true, + 299999), + ROCK("rock", "Rockridge", "Rockridge", false, false, "mcar", "mcar"), + SBRN("sbrn", "San Bruno", "San Bruno", false, false, "balb", "balb"), + SANL("sanl", "San Leandro", "San Leandro", true, false, "mcar", "mcar"), + SFIA("sfia", "SFO Airport", "SFO", false, false, "balb", "balb", true, + 719999), + SHAY("shay", "South Hayward", "S Hayward", true, false, "bayf", "bayf"), + SSAN("ssan", "South San Francisco", "S San Fran", false, false, "balb", + "balb"), + UCTY("ucty", "Union City", "Union City", true, false, "bayf", "bayf"), + WCRK("wcrk", "Walnut Creek", "Walnut Crk", false, false, "mcar", "mcar"), + WDUB("wdub", "West Dublin/Pleasanton", "W Dbln/Plsntn", false, false, + "bayf", "bayf"), + WOAK("woak", "West Oakland", "W Oakland", false, false), + SPCL("spcl", "Special", "Special", false, false); + + public final String abbreviation; + public final String name; + public final String shortName; + public final boolean transferFriendly; + public final boolean invertDirection; + protected final String inboundTransferStation; + protected final String outboundTransferStation; + public final boolean endOfLine; + public final boolean longStationLinger; + public final int departureEqualityTolerance; + + public final static int DEFAULT_DEPARTURE_EQUALITY_TOLERANCE = 119999; + + private Station(String abbreviation, String name, String shortName, + boolean invertDirection, boolean endOfLine) { + this(abbreviation, name, shortName, invertDirection, endOfLine, null, + null, false, DEFAULT_DEPARTURE_EQUALITY_TOLERANCE); + } + + private Station(String abbreviation, String name, String shortName, + boolean invertDirection, boolean endOfLine, String transferStation) { + this(abbreviation, name, shortName, invertDirection, endOfLine, + transferStation, null, false, + DEFAULT_DEPARTURE_EQUALITY_TOLERANCE); + } + + private Station(String abbreviation, String name, String shortName, + boolean invertDirection, boolean endOfLine, + String inboundTransferStation, String outboundTransferStation) { + this(abbreviation, name, shortName, invertDirection, endOfLine, + inboundTransferStation, outboundTransferStation, false, + DEFAULT_DEPARTURE_EQUALITY_TOLERANCE); + } + + private Station(String abbreviation, String name, String shortName, + boolean invertDirection, boolean endOfLine, + String inboundTransferStation, String outboundTransferStation, + boolean longStationLinger, int departureEqualityTolerance) { + this.abbreviation = abbreviation; + this.name = name; + this.shortName = shortName; + this.invertDirection = invertDirection; + this.inboundTransferStation = inboundTransferStation; + this.transferFriendly = outboundTransferStation != null; + this.outboundTransferStation = outboundTransferStation; + this.endOfLine = endOfLine; + this.longStationLinger = longStationLinger; + this.departureEqualityTolerance = departureEqualityTolerance; + } + + public static Station getByAbbreviation(String abbr) { + try { + if (abbr == null) { + return null; + } else if (Character.isDigit(abbr.charAt(0))) { + return Station.valueOf("_" + abbr.toUpperCase()); + } else { + return Station.valueOf(abbr.toUpperCase()); + } + } catch (IllegalArgumentException e) { + Log.e(Constants.TAG, "Could not find station for '" + abbr + "'", e); + return null; + } + } + + public Station getInboundTransferStation() { + return getByAbbreviation(inboundTransferStation); + } + + public Station getOutboundTransferStation() { + return getByAbbreviation(outboundTransferStation); + } + + public boolean isValidEndpointForDestination(Station dest, Station endpoint) { + for (Line line : Line.values()) { + int origIndex = line.stations.indexOf(this); + if (origIndex < 0) + continue; + int destIndex = line.stations.indexOf(dest); + if (destIndex < 0) + continue; + int endpointIndex = line.stations.indexOf(endpoint); + if (endpointIndex >= 0) + return true; + } + return false; + } + + public List getDirectRoutesForDestination(Station dest) { + return getDirectRoutesForDestination(this, dest, null, null); + } + + public List getDirectRoutesForDestination(Station origin, + Station dest, Station transferStation, + Collection transferLines) { + if (dest == null) + return null; + Boolean isNorth = null; + List returnList = new ArrayList(); + final Collection applicableLines = Line.getLinesWithStations( + this, dest); + if (transferLines != null && !transferLines.isEmpty()) { + for (Line transferLine : transferLines) { + int origIndex = transferLine.stations.indexOf(origin); + int destIndex = transferLine.stations.indexOf(origin + .getOutboundTransferStation()); + + isNorth = (origIndex < destIndex); + if (origin.invertDirection && transferLine.directionMayInvert) { + isNorth = !isNorth; + break; + } + } + } + for (Line line : applicableLines) { + if (transferLines == null || transferLines.isEmpty()) { + int origIndex = line.stations.indexOf(this); + int destIndex = line.stations.indexOf(dest); + + isNorth = (origIndex < destIndex); + if (line.directionMayInvert && this.invertDirection) { + isNorth = !isNorth; + } + } + Route route = new Route(); + route.setOrigin(origin); + route.setDirectLine(line); + if (this.equals(origin)) { + route.setDestination(dest); + } else { + // This must be the outbound transfer station + route.setDestination(origin.getOutboundTransferStation()); + route.setTransferLines(transferLines); + } + route.setDirection(isNorth ? "n" : "s"); + if (transferStation != null || line.requiresTransfer) { + route.setTransfer(true); + } else { + route.setTransfer(false); + } + + returnList.add(route); + } + return returnList; + } + + public List getTransferRoutes(Station dest) { + List returnList = new ArrayList(); + + if (dest.getInboundTransferStation() != null) { + // Try getting to the destination's inbound xfer station first + returnList.addAll(getDirectRoutesForDestination(this, + dest.getInboundTransferStation(), + dest.getInboundTransferStation(), null)); + } + + if (returnList.isEmpty() && outboundTransferStation != null) { + // Try getting from the outbound transfer station to the + // destination next + final Collection outboundTransferLines = Line + .getLinesWithStations(this, getOutboundTransferStation()); + final List routesForDestination = getOutboundTransferStation() + .getDirectRoutesForDestination(this, dest, + getOutboundTransferStation(), outboundTransferLines); + if (routesForDestination != null && !routesForDestination.isEmpty()) { + returnList.addAll(routesForDestination); + } + } + + if (returnList.isEmpty()) { + // Try getting from the outbound transfer station to the + // destination's inbound xfer station + final List routesForDestination = getDoubleTransferRoutes(dest); + if (routesForDestination != null && !routesForDestination.isEmpty()) { + returnList.addAll(routesForDestination); + } + } + + return returnList; + } + + public List getDoubleTransferRoutes(Station dest) { + if (getOutboundTransferStation() == null + || dest.getInboundTransferStation() == null) + return new ArrayList(); + + // Get routes from the outbound transfer station to the + // destination's inbound xfer station + return getOutboundTransferStation().getDirectRoutesForDestination(this, + dest.getInboundTransferStation(), getOutboundTransferStation(), + Line.getLinesWithStations(this, getOutboundTransferStation())); + } + + static public List getStationList() { + List list = new ArrayList(); + for (Station station : values()) { + if (!station.equals(Station.SPCL)) { + list.add(station); + } + } + return list; + } + + public String toString() { + return name; + } + + public boolean isBetween(Station origin, Station destination, Line line) { + int originIndex = line.stations.indexOf(origin); + int destinationIndex = line.stations.indexOf(destination); + int stationIndex = line.stations.indexOf(this); + if (originIndex < 0 || destinationIndex < 0 || stationIndex < 0) { + return false; + } + + return Math.abs(stationIndex - originIndex) < Math.abs(destinationIndex + - originIndex); + } +} diff --git a/src/com/dougkeen/bart/model/StationPair.java b/src/com/dougkeen/bart/model/StationPair.java index 3496f0a..c38a0f1 100644 --- a/src/com/dougkeen/bart/model/StationPair.java +++ b/src/com/dougkeen/bart/model/StationPair.java @@ -1,151 +1,151 @@ -package com.dougkeen.bart.model; - -import org.apache.commons.lang3.ObjectUtils; - -import android.database.Cursor; -import android.net.Uri; -import android.os.Parcel; -import android.os.Parcelable; - -import com.dougkeen.bart.data.CursorUtils; -import com.dougkeen.bart.data.RoutesColumns; - -public class StationPair implements Parcelable { - public StationPair(Station origin, Station destination) { - super(); - this.origin = origin; - this.destination = destination; - } - - public StationPair(Long id, Station origin, Station destination) { - super(); - this.origin = origin; - this.destination = destination; - this.id = id; - } - - public StationPair(Parcel in) { - readFromParcel(in); - } - - public static StationPair createFromCursor(Cursor cursor) { - StationPair pair = new StationPair( - Station.getByAbbreviation(CursorUtils.getString(cursor, - RoutesColumns.FROM_STATION)), - Station.getByAbbreviation(CursorUtils.getString(cursor, - RoutesColumns.TO_STATION))); - pair.id = CursorUtils.getLong(cursor, RoutesColumns._ID); - pair.fare = CursorUtils.getString(cursor, RoutesColumns.FARE); - pair.fareLastUpdated = CursorUtils.getLong(cursor, - RoutesColumns.FARE_LAST_UPDATED); - return pair; - } - - private Long id; - private Station origin; - private Station destination; - private String fare; - private Long fareLastUpdated; - - public Long getId() { - return id; - } - - public Station getOrigin() { - return origin; - } - - public Station getDestination() { - return destination; - } - - public String getFare() { - return fare; - } - - public void setFare(String fare) { - this.fare = fare; - } - - public Long getFareLastUpdated() { - return fareLastUpdated; - } - - public void setFareLastUpdated(Long fareLastUpdated) { - this.fareLastUpdated = fareLastUpdated; - } - - public boolean isBetweenStations(Station station1, Station station2) { - return (origin.equals(station1) && destination.equals(station2)) - || (origin.equals(station2) && destination.equals(station1)); - } - - public Uri getUri() { - if (getOrigin() != null && getDestination() != null) { - return Constants.ARBITRARY_ROUTE_CONTENT_URI_ROOT.buildUpon() - .appendPath(getOrigin().abbreviation) - .appendPath(getDestination().abbreviation).build(); - } else { - return null; - } - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((destination == null) ? 0 : destination.hashCode()); - result = prime * result + ((origin == null) ? 0 : origin.hashCode()); - return result; - } - - public boolean fareEquals(StationPair other) { - if (other == null) - return false; - return ObjectUtils.equals(getFare(), other.getFare()) - && ObjectUtils.equals(getFareLastUpdated(), - other.getFareLastUpdated()); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - StationPair other = (StationPair) obj; - if (destination != other.destination) - return false; - if (origin != other.origin) - return false; - return true; - } - - @Override - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(origin.abbreviation); - dest.writeString(destination.abbreviation); - } - - private void readFromParcel(Parcel in) { - origin = Station.getByAbbreviation(in.readString()); - destination = Station.getByAbbreviation(in.readString()); - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public StationPair createFromParcel(Parcel in) { - return new StationPair(in); - } - - public StationPair[] newArray(int size) { - return new StationPair[size]; - } - }; +package com.dougkeen.bart.model; + +import org.apache.commons.lang3.ObjectUtils; + +import android.database.Cursor; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +import com.dougkeen.bart.data.CursorUtils; +import com.dougkeen.bart.data.RoutesColumns; + +public class StationPair implements Parcelable { + public StationPair(Station origin, Station destination) { + super(); + this.origin = origin; + this.destination = destination; + } + + public StationPair(Long id, Station origin, Station destination) { + super(); + this.origin = origin; + this.destination = destination; + this.id = id; + } + + public StationPair(Parcel in) { + readFromParcel(in); + } + + public static StationPair createFromCursor(Cursor cursor) { + StationPair pair = new StationPair( + Station.getByAbbreviation(CursorUtils.getString(cursor, + RoutesColumns.FROM_STATION)), + Station.getByAbbreviation(CursorUtils.getString(cursor, + RoutesColumns.TO_STATION))); + pair.id = CursorUtils.getLong(cursor, RoutesColumns._ID); + pair.fare = CursorUtils.getString(cursor, RoutesColumns.FARE); + pair.fareLastUpdated = CursorUtils.getLong(cursor, + RoutesColumns.FARE_LAST_UPDATED); + return pair; + } + + private Long id; + private Station origin; + private Station destination; + private String fare; + private Long fareLastUpdated; + + public Long getId() { + return id; + } + + public Station getOrigin() { + return origin; + } + + public Station getDestination() { + return destination; + } + + public String getFare() { + return fare; + } + + public void setFare(String fare) { + this.fare = fare; + } + + public Long getFareLastUpdated() { + return fareLastUpdated; + } + + public void setFareLastUpdated(Long fareLastUpdated) { + this.fareLastUpdated = fareLastUpdated; + } + + public boolean isBetweenStations(Station station1, Station station2) { + return (origin.equals(station1) && destination.equals(station2)) + || (origin.equals(station2) && destination.equals(station1)); + } + + public Uri getUri() { + if (getOrigin() != null && getDestination() != null) { + return Constants.ARBITRARY_ROUTE_CONTENT_URI_ROOT.buildUpon() + .appendPath(getOrigin().abbreviation) + .appendPath(getDestination().abbreviation).build(); + } else { + return null; + } + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((destination == null) ? 0 : destination.hashCode()); + result = prime * result + ((origin == null) ? 0 : origin.hashCode()); + return result; + } + + public boolean fareEquals(StationPair other) { + if (other == null) + return false; + return ObjectUtils.equals(getFare(), other.getFare()) + && ObjectUtils.equals(getFareLastUpdated(), + other.getFareLastUpdated()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + StationPair other = (StationPair) obj; + if (destination != other.destination) + return false; + if (origin != other.origin) + return false; + return true; + } + + @Override + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(origin.abbreviation); + dest.writeString(destination.abbreviation); + } + + private void readFromParcel(Parcel in) { + origin = Station.getByAbbreviation(in.readString()); + destination = Station.getByAbbreviation(in.readString()); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public StationPair createFromParcel(Parcel in) { + return new StationPair(in); + } + + public StationPair[] newArray(int size) { + return new StationPair[size]; + } + }; } \ No newline at end of file diff --git a/src/com/dougkeen/bart/model/TextProvider.java b/src/com/dougkeen/bart/model/TextProvider.java index 7272567..c659498 100644 --- a/src/com/dougkeen/bart/model/TextProvider.java +++ b/src/com/dougkeen/bart/model/TextProvider.java @@ -1,8 +1,7 @@ -package com.dougkeen.bart.model; - - -public interface TextProvider { - - String getText(long tickNumber); - -} +package com.dougkeen.bart.model; + +public interface TextProvider { + + String getText(long tickNumber); + +} diff --git a/src/com/dougkeen/bart/networktasks/EtdContentHandler.java b/src/com/dougkeen/bart/networktasks/EtdContentHandler.java index d8d7e49..3826095 100644 --- a/src/com/dougkeen/bart/networktasks/EtdContentHandler.java +++ b/src/com/dougkeen/bart/networktasks/EtdContentHandler.java @@ -1,122 +1,122 @@ -package com.dougkeen.bart.networktasks; - -import java.util.Arrays; -import java.util.Date; -import java.util.List; - -import org.apache.commons.lang3.StringUtils; -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -import android.util.Log; - -import com.dougkeen.bart.model.Constants; -import com.dougkeen.bart.model.Departure; -import com.dougkeen.bart.model.Line; -import com.dougkeen.bart.model.RealTimeDepartures; -import com.dougkeen.bart.model.Route; -import com.dougkeen.bart.model.Station; - -public class EtdContentHandler extends DefaultHandler { - public EtdContentHandler(Station origin, Station destination, - List routes) { - super(); - realTimeDepartures = new RealTimeDepartures(origin, destination, routes); - } - - private final static List TAGS = Arrays.asList("date", "time", - "abbreviation", "minutes", "platform", "direction", "length", - "color", "hexcolor", "bikeflag"); - - private RealTimeDepartures realTimeDepartures; - - public RealTimeDepartures getRealTimeDepartures() { - return realTimeDepartures; - } - - private String date; - private String currentDestination; - private String currentValue; - private Departure currentDeparture; - private boolean isParsingTag; - - @Override - public void characters(char[] ch, int start, int length) - throws SAXException { - if (isParsingTag) { - currentValue = new String(ch, start, length); - } - } - - @Override - public void startElement(String uri, String localName, String qName, - Attributes attributes) throws SAXException { - if (TAGS.contains(localName)) { - isParsingTag = true; - } - if (localName.equals("estimate")) { - currentDeparture = new Departure(); - currentDeparture.setTrainDestination(Station - .getByAbbreviation(currentDestination)); - currentDeparture.setOrigin(realTimeDepartures.getOrigin()); - } - } - - @Override - public void endElement(String uri, String localName, String qName) - throws SAXException { - if (localName.equals("date")) { - date = currentValue; - } else if (localName.equals("time")) { - realTimeDepartures.setTime(Date.parse(date + " " + currentValue)); - } else if (localName.equals("abbreviation")) { - currentDestination = currentValue; - } else if (localName.equals("minutes")) { - if (StringUtils.isNumeric(currentValue)) { - currentDeparture.setMinutes(Integer.parseInt(currentValue)); - } else { - currentDeparture.setMinutes(0); - } - } else if (localName.equals("platform")) { - currentDeparture.setPlatform(currentValue); - } else if (localName.equals("direction")) { - currentDeparture.setDirection(currentValue); - } else if (localName.equals("length")) { - currentDeparture.setTrainLength(currentValue); - } else if (localName.equals("color")) { - try { - if (currentValue.equalsIgnoreCase("WHITE")) { - for (Line line : Line.values()) { - if (line.stations.indexOf(currentDeparture - .getTrainDestination()) >= 0 - && line.stations.indexOf(realTimeDepartures - .getDestination()) >= 0) { - currentDeparture.setLine(line); - break; - } - } - } else { - currentDeparture.setLine(Line.valueOf(currentValue)); - } - } catch (IllegalArgumentException e) { - Log.w(Constants.TAG, "There is no line called '" + currentValue - + "'"); - } - } else if (localName.equals("hexcolor")) { - currentDeparture.setTrainDestinationColor("#ff" - + currentValue.substring(1)); - } else if (localName.equals("bikeflag")) { - currentDeparture.setBikeAllowed(currentValue.equalsIgnoreCase("1")); - } else if (localName.equals("estimate")) { - realTimeDepartures.addDeparture(currentDeparture); - currentDeparture = null; - } else if (localName.equals("etd")) { - currentDestination = null; - } else if (localName.equals("station")) { - realTimeDepartures.finalizeDeparturesList(); - } - isParsingTag = false; - currentValue = null; - } -} +package com.dougkeen.bart.networktasks; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import android.util.Log; + +import com.dougkeen.bart.model.Constants; +import com.dougkeen.bart.model.Departure; +import com.dougkeen.bart.model.Line; +import com.dougkeen.bart.model.RealTimeDepartures; +import com.dougkeen.bart.model.Route; +import com.dougkeen.bart.model.Station; + +public class EtdContentHandler extends DefaultHandler { + public EtdContentHandler(Station origin, Station destination, + List routes) { + super(); + realTimeDepartures = new RealTimeDepartures(origin, destination, routes); + } + + private final static List TAGS = Arrays.asList("date", "time", + "abbreviation", "minutes", "platform", "direction", "length", + "color", "hexcolor", "bikeflag"); + + private RealTimeDepartures realTimeDepartures; + + public RealTimeDepartures getRealTimeDepartures() { + return realTimeDepartures; + } + + private String date; + private String currentDestination; + private String currentValue; + private Departure currentDeparture; + private boolean isParsingTag; + + @Override + public void characters(char[] ch, int start, int length) + throws SAXException { + if (isParsingTag) { + currentValue = new String(ch, start, length); + } + } + + @Override + public void startElement(String uri, String localName, String qName, + Attributes attributes) throws SAXException { + if (TAGS.contains(localName)) { + isParsingTag = true; + } + if (localName.equals("estimate")) { + currentDeparture = new Departure(); + currentDeparture.setTrainDestination(Station + .getByAbbreviation(currentDestination)); + currentDeparture.setOrigin(realTimeDepartures.getOrigin()); + } + } + + @Override + public void endElement(String uri, String localName, String qName) + throws SAXException { + if (localName.equals("date")) { + date = currentValue; + } else if (localName.equals("time")) { + realTimeDepartures.setTime(Date.parse(date + " " + currentValue)); + } else if (localName.equals("abbreviation")) { + currentDestination = currentValue; + } else if (localName.equals("minutes")) { + if (StringUtils.isNumeric(currentValue)) { + currentDeparture.setMinutes(Integer.parseInt(currentValue)); + } else { + currentDeparture.setMinutes(0); + } + } else if (localName.equals("platform")) { + currentDeparture.setPlatform(currentValue); + } else if (localName.equals("direction")) { + currentDeparture.setDirection(currentValue); + } else if (localName.equals("length")) { + currentDeparture.setTrainLength(currentValue); + } else if (localName.equals("color")) { + try { + if (currentValue.equalsIgnoreCase("WHITE")) { + for (Line line : Line.values()) { + if (line.stations.indexOf(currentDeparture + .getTrainDestination()) >= 0 + && line.stations.indexOf(realTimeDepartures + .getDestination()) >= 0) { + currentDeparture.setLine(line); + break; + } + } + } else { + currentDeparture.setLine(Line.valueOf(currentValue)); + } + } catch (IllegalArgumentException e) { + Log.w(Constants.TAG, "There is no line called '" + currentValue + + "'"); + } + } else if (localName.equals("hexcolor")) { + currentDeparture.setTrainDestinationColor("#ff" + + currentValue.substring(1)); + } else if (localName.equals("bikeflag")) { + currentDeparture.setBikeAllowed(currentValue.equalsIgnoreCase("1")); + } else if (localName.equals("estimate")) { + realTimeDepartures.addDeparture(currentDeparture); + currentDeparture = null; + } else if (localName.equals("etd")) { + currentDestination = null; + } else if (localName.equals("station")) { + realTimeDepartures.finalizeDeparturesList(); + } + isParsingTag = false; + currentValue = null; + } +} diff --git a/src/com/dougkeen/bart/networktasks/FareContentHandler.java b/src/com/dougkeen/bart/networktasks/FareContentHandler.java index fa1c0fb..54e7b72 100644 --- a/src/com/dougkeen/bart/networktasks/FareContentHandler.java +++ b/src/com/dougkeen/bart/networktasks/FareContentHandler.java @@ -1,46 +1,46 @@ -package com.dougkeen.bart.networktasks; - -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -public class FareContentHandler extends DefaultHandler { - public FareContentHandler() { - super(); - } - - private String currentValue; - private boolean isParsingTag; - private String fare; - - public String getFare() { - return fare; - } - - @Override - public void characters(char[] ch, int start, int length) - throws SAXException { - if (isParsingTag) { - currentValue = new String(ch, start, length); - } - } - - @Override - public void startElement(String uri, String localName, String qName, - Attributes attributes) throws SAXException { - if (localName.equals("fare")) { - isParsingTag = true; - } - } - - @Override - public void endElement(String uri, String localName, String qName) - throws SAXException { - if (localName.equals("fare")) { - fare = "$" + currentValue; - } - isParsingTag = false; - currentValue = null; - } - -} +package com.dougkeen.bart.networktasks; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +public class FareContentHandler extends DefaultHandler { + public FareContentHandler() { + super(); + } + + private String currentValue; + private boolean isParsingTag; + private String fare; + + public String getFare() { + return fare; + } + + @Override + public void characters(char[] ch, int start, int length) + throws SAXException { + if (isParsingTag) { + currentValue = new String(ch, start, length); + } + } + + @Override + public void startElement(String uri, String localName, String qName, + Attributes attributes) throws SAXException { + if (localName.equals("fare")) { + isParsingTag = true; + } + } + + @Override + public void endElement(String uri, String localName, String qName) + throws SAXException { + if (localName.equals("fare")) { + fare = "$" + currentValue; + } + isParsingTag = false; + currentValue = null; + } + +} diff --git a/src/com/dougkeen/bart/networktasks/GetRealTimeDeparturesTask.java b/src/com/dougkeen/bart/networktasks/GetRealTimeDeparturesTask.java index 9e1f003..36d42b9 100644 --- a/src/com/dougkeen/bart/networktasks/GetRealTimeDeparturesTask.java +++ b/src/com/dougkeen/bart/networktasks/GetRealTimeDeparturesTask.java @@ -1,153 +1,153 @@ -package com.dougkeen.bart.networktasks; - -import java.io.IOException; -import java.io.StringWriter; -import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; -import java.util.List; - -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpUriRequest; - -import android.os.AsyncTask; -import android.util.Log; -import android.util.Xml; - -import com.dougkeen.bart.model.Constants; -import com.dougkeen.bart.model.RealTimeDepartures; -import com.dougkeen.bart.model.Route; -import com.dougkeen.bart.model.StationPair; - -public abstract class GetRealTimeDeparturesTask extends - AsyncTask { - - private final static String ETD_URL = "http://api.bart.gov/api/etd.aspx?cmd=etd&key=" - + Constants.API_KEY + "&orig=%1$s&dir=%2$s"; - private final static String ETD_URL_NO_DIRECTION = "http://api.bart.gov/api/etd.aspx?cmd=etd&key=" - + Constants.API_KEY + "&orig=%1$s"; - private final static int MAX_ATTEMPTS = 5; - - private Exception mException; - - private List mRoutes; - - private final boolean ignoreDirection; - - public GetRealTimeDeparturesTask(boolean ignoreDirection) { - super(); - this.ignoreDirection = ignoreDirection; - } - - @Override - protected RealTimeDepartures doInBackground(StationPair... paramsArray) { - // Always expect one param - StationPair params = paramsArray[0]; - - mRoutes = params.getOrigin().getDirectRoutesForDestination( - params.getDestination()); - - boolean hasDirectLine = false; - for (Route route : mRoutes) { - if (!route.hasTransfer()) { - hasDirectLine = true; - break; - } - } - - if (mRoutes.isEmpty() - || (params.getOrigin().transferFriendly && !hasDirectLine)) { - mRoutes.addAll(params.getOrigin().getTransferRoutes( - params.getDestination())); - } - - if (!isCancelled()) { - return getDeparturesFromNetwork(params, 0); - } else { - return null; - } - } - - private RealTimeDepartures getDeparturesFromNetwork(StationPair params, - int attemptNumber) { - String xml = null; - try { - String url; - if (ignoreDirection || params.getOrigin().endOfLine) { - url = String.format(ETD_URL_NO_DIRECTION, - params.getOrigin().abbreviation); - } else { - url = String.format(ETD_URL, params.getOrigin().abbreviation, - mRoutes.get(0).getDirection()); - } - - HttpUriRequest request = new HttpGet(url); - - EtdContentHandler handler = new EtdContentHandler( - params.getOrigin(), params.getDestination(), mRoutes); - if (isCancelled()) { - return null; - } - - HttpResponse response = NetworkUtils.executeWithRecovery(request); - - if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { - throw new IOException("Server returned " - + response.getStatusLine().toString()); - } - - StringWriter writer = new StringWriter(); - IOUtils.copy(response.getEntity().getContent(), writer, "UTF-8"); - - xml = writer.toString(); - if (xml.length() == 0) { - throw new IOException("Server returned blank xml document"); - } - - try { - Xml.parse(xml, handler); - } catch (Exception e) { - mException = new IOException("Server returned malformed xml: " - + xml); - return null; - } - final RealTimeDepartures realTimeDepartures = handler - .getRealTimeDepartures(); - return realTimeDepartures; - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } catch (IOException e) { - if (attemptNumber < MAX_ATTEMPTS - 1) { - try { - Log.w(Constants.TAG, - "Attempt to contact server failed... retrying in 3s", - e); - Thread.sleep(3000); - } catch (InterruptedException interrupt) { - // Ignore... just go on to next attempt - } - return getDeparturesFromNetwork(params, attemptNumber + 1); - } else { - mException = new Exception("Could not contact BART system", e); - return null; - } - } - } - - @Override - protected void onPostExecute(RealTimeDepartures result) { - if (result != null) { - onResult(result); - } else { - onError(mException); - } - } - - public abstract void onResult(RealTimeDepartures result); - - public abstract void onError(Exception exception); -} +package com.dougkeen.bart.networktasks; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.util.List; + +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpUriRequest; + +import android.os.AsyncTask; +import android.util.Log; +import android.util.Xml; + +import com.dougkeen.bart.model.Constants; +import com.dougkeen.bart.model.RealTimeDepartures; +import com.dougkeen.bart.model.Route; +import com.dougkeen.bart.model.StationPair; + +public abstract class GetRealTimeDeparturesTask extends + AsyncTask { + + private final static String ETD_URL = "http://api.bart.gov/api/etd.aspx?cmd=etd&key=" + + Constants.API_KEY + "&orig=%1$s&dir=%2$s"; + private final static String ETD_URL_NO_DIRECTION = "http://api.bart.gov/api/etd.aspx?cmd=etd&key=" + + Constants.API_KEY + "&orig=%1$s"; + private final static int MAX_ATTEMPTS = 5; + + private Exception mException; + + private List mRoutes; + + private final boolean ignoreDirection; + + public GetRealTimeDeparturesTask(boolean ignoreDirection) { + super(); + this.ignoreDirection = ignoreDirection; + } + + @Override + protected RealTimeDepartures doInBackground(StationPair... paramsArray) { + // Always expect one param + StationPair params = paramsArray[0]; + + mRoutes = params.getOrigin().getDirectRoutesForDestination( + params.getDestination()); + + boolean hasDirectLine = false; + for (Route route : mRoutes) { + if (!route.hasTransfer()) { + hasDirectLine = true; + break; + } + } + + if (mRoutes.isEmpty() + || (params.getOrigin().transferFriendly && !hasDirectLine)) { + mRoutes.addAll(params.getOrigin().getTransferRoutes( + params.getDestination())); + } + + if (!isCancelled()) { + return getDeparturesFromNetwork(params, 0); + } else { + return null; + } + } + + private RealTimeDepartures getDeparturesFromNetwork(StationPair params, + int attemptNumber) { + String xml = null; + try { + String url; + if (ignoreDirection || params.getOrigin().endOfLine) { + url = String.format(ETD_URL_NO_DIRECTION, + params.getOrigin().abbreviation); + } else { + url = String.format(ETD_URL, params.getOrigin().abbreviation, + mRoutes.get(0).getDirection()); + } + + HttpUriRequest request = new HttpGet(url); + + EtdContentHandler handler = new EtdContentHandler( + params.getOrigin(), params.getDestination(), mRoutes); + if (isCancelled()) { + return null; + } + + HttpResponse response = NetworkUtils.executeWithRecovery(request); + + if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { + throw new IOException("Server returned " + + response.getStatusLine().toString()); + } + + StringWriter writer = new StringWriter(); + IOUtils.copy(response.getEntity().getContent(), writer, "UTF-8"); + + xml = writer.toString(); + if (xml.length() == 0) { + throw new IOException("Server returned blank xml document"); + } + + try { + Xml.parse(xml, handler); + } catch (Exception e) { + mException = new IOException("Server returned malformed xml: " + + xml); + return null; + } + final RealTimeDepartures realTimeDepartures = handler + .getRealTimeDepartures(); + return realTimeDepartures; + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } catch (IOException e) { + if (attemptNumber < MAX_ATTEMPTS - 1) { + try { + Log.w(Constants.TAG, + "Attempt to contact server failed... retrying in 3s", + e); + Thread.sleep(3000); + } catch (InterruptedException interrupt) { + // Ignore... just go on to next attempt + } + return getDeparturesFromNetwork(params, attemptNumber + 1); + } else { + mException = new Exception("Could not contact BART system", e); + return null; + } + } + } + + @Override + protected void onPostExecute(RealTimeDepartures result) { + if (result != null) { + onResult(result); + } else { + onError(mException); + } + } + + public abstract void onResult(RealTimeDepartures result); + + public abstract void onError(Exception exception); +} diff --git a/src/com/dougkeen/bart/networktasks/GetRouteFareTask.java b/src/com/dougkeen/bart/networktasks/GetRouteFareTask.java index 6b98bfe..363d9e2 100644 --- a/src/com/dougkeen/bart/networktasks/GetRouteFareTask.java +++ b/src/com/dougkeen/bart/networktasks/GetRouteFareTask.java @@ -1,125 +1,125 @@ -package com.dougkeen.bart.networktasks; - -import java.io.IOException; -import java.io.StringWriter; -import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; - -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpUriRequest; -import org.xml.sax.SAXException; - -import android.os.AsyncTask; -import android.util.Log; -import android.util.Xml; - -import com.dougkeen.bart.model.Constants; -import com.dougkeen.bart.model.Station; - -public abstract class GetRouteFareTask extends - AsyncTask { - - private final static int MAX_ATTEMPTS = 5; - private final static String FARE_URL = "http://api.bart.gov/api/sched.aspx?cmd=fare&date=today&key=" - + Constants.API_KEY + "&orig=%1$s&dest=%2$s"; - - private Exception mException; - - private String fare; - - @Override - protected String doInBackground(Params... paramsArray) { - Params params = paramsArray[0]; - - if (!isCancelled()) { - return getFareFromNetwork(params, 0); - } else { - return null; - } - } - - private String getFareFromNetwork(Params params, int attemptNumber) { - String xml = null; - - try { - HttpUriRequest request = new HttpGet( - String.format(FARE_URL, params.origin.abbreviation, - params.destination.abbreviation)); - - FareContentHandler handler = new FareContentHandler(); - if (isCancelled()) { - return null; - } - - HttpResponse response = NetworkUtils.executeWithRecovery(request); - - if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { - throw new IOException("Server returned " - + response.getStatusLine().toString()); - } - - StringWriter writer = new StringWriter(); - IOUtils.copy(response.getEntity().getContent(), writer, "UTF-8"); - - xml = writer.toString(); - if (xml.length() == 0) { - throw new IOException("Server returned blank xml document"); - } - - Xml.parse(xml, handler); - fare = handler.getFare(); - return fare; - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } catch (IOException e) { - if (attemptNumber < MAX_ATTEMPTS - 1) { - try { - Log.w(Constants.TAG, - "Attempt to contact server failed... retrying in 3s", - e); - Thread.sleep(3000); - } catch (InterruptedException interrupt) { - // Ignore... just go on to next attempt - } - return getFareFromNetwork(params, attemptNumber + 1); - } else { - mException = new Exception("Could not contact BART system", e); - return null; - } - } catch (SAXException e) { - mException = new Exception( - "Could not understand response from BART system: " + xml, e); - return null; - } - } - - public static class Params { - public Params(Station origin, Station destination) { - super(); - this.origin = origin; - this.destination = destination; - } - - public final Station origin; - public final Station destination; - } - - @Override - protected void onPostExecute(String result) { - if (result != null) { - onResult(fare); - } else { - onError(mException); - } - } - - public abstract void onResult(String fare); - - public abstract void onError(Exception exception); - +package com.dougkeen.bart.networktasks; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; + +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpUriRequest; +import org.xml.sax.SAXException; + +import android.os.AsyncTask; +import android.util.Log; +import android.util.Xml; + +import com.dougkeen.bart.model.Constants; +import com.dougkeen.bart.model.Station; + +public abstract class GetRouteFareTask extends + AsyncTask { + + private final static int MAX_ATTEMPTS = 5; + private final static String FARE_URL = "http://api.bart.gov/api/sched.aspx?cmd=fare&date=today&key=" + + Constants.API_KEY + "&orig=%1$s&dest=%2$s"; + + private Exception mException; + + private String fare; + + @Override + protected String doInBackground(Params... paramsArray) { + Params params = paramsArray[0]; + + if (!isCancelled()) { + return getFareFromNetwork(params, 0); + } else { + return null; + } + } + + private String getFareFromNetwork(Params params, int attemptNumber) { + String xml = null; + + try { + HttpUriRequest request = new HttpGet( + String.format(FARE_URL, params.origin.abbreviation, + params.destination.abbreviation)); + + FareContentHandler handler = new FareContentHandler(); + if (isCancelled()) { + return null; + } + + HttpResponse response = NetworkUtils.executeWithRecovery(request); + + if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { + throw new IOException("Server returned " + + response.getStatusLine().toString()); + } + + StringWriter writer = new StringWriter(); + IOUtils.copy(response.getEntity().getContent(), writer, "UTF-8"); + + xml = writer.toString(); + if (xml.length() == 0) { + throw new IOException("Server returned blank xml document"); + } + + Xml.parse(xml, handler); + fare = handler.getFare(); + return fare; + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } catch (IOException e) { + if (attemptNumber < MAX_ATTEMPTS - 1) { + try { + Log.w(Constants.TAG, + "Attempt to contact server failed... retrying in 3s", + e); + Thread.sleep(3000); + } catch (InterruptedException interrupt) { + // Ignore... just go on to next attempt + } + return getFareFromNetwork(params, attemptNumber + 1); + } else { + mException = new Exception("Could not contact BART system", e); + return null; + } + } catch (SAXException e) { + mException = new Exception( + "Could not understand response from BART system: " + xml, e); + return null; + } + } + + public static class Params { + public Params(Station origin, Station destination) { + super(); + this.origin = origin; + this.destination = destination; + } + + public final Station origin; + public final Station destination; + } + + @Override + protected void onPostExecute(String result) { + if (result != null) { + onResult(fare); + } else { + onError(mException); + } + } + + public abstract void onResult(String fare); + + public abstract void onError(Exception exception); + } \ No newline at end of file diff --git a/src/com/dougkeen/bart/networktasks/GetScheduleInformationTask.java b/src/com/dougkeen/bart/networktasks/GetScheduleInformationTask.java index 7774385..5012195 100644 --- a/src/com/dougkeen/bart/networktasks/GetScheduleInformationTask.java +++ b/src/com/dougkeen/bart/networktasks/GetScheduleInformationTask.java @@ -1,118 +1,118 @@ -package com.dougkeen.bart.networktasks; - -import java.io.IOException; -import java.io.StringWriter; -import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; - -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpUriRequest; -import org.xml.sax.SAXException; - -import android.os.AsyncTask; -import android.util.Log; -import android.util.Xml; - -import com.dougkeen.bart.model.Constants; -import com.dougkeen.bart.model.ScheduleInformation; -import com.dougkeen.bart.model.StationPair; - -public abstract class GetScheduleInformationTask extends - AsyncTask { - - private final static String SCHED_URL = "http://api.bart.gov/api/sched.aspx?cmd=depart&key=" - + Constants.API_KEY + "&orig=%1$s&dest=%2$s&b=1&a=4"; - - private final static int MAX_ATTEMPTS = 5; - - private Exception mException; - - @Override - protected ScheduleInformation doInBackground(StationPair... paramsArray) { - // Always expect one param - StationPair params = paramsArray[0]; - - if (!isCancelled()) { - return getScheduleFromNetwork(params, 0); - } else { - return null; - } - } - - private ScheduleInformation getScheduleFromNetwork(StationPair params, - int attemptNumber) { - String xml = null; - try { - String url = String.format(SCHED_URL, - params.getOrigin().abbreviation, - params.getDestination().abbreviation); - - HttpUriRequest request = new HttpGet(url); - - if (isCancelled()) { - return null; - } - - ScheduleContentHandler handler = new ScheduleContentHandler( - params.getOrigin(), params.getDestination()); - - HttpResponse response = NetworkUtils.executeWithRecovery(request); - - if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { - throw new IOException("Server returned " - + response.getStatusLine().toString()); - } - - StringWriter writer = new StringWriter(); - IOUtils.copy(response.getEntity().getContent(), writer, "UTF-8"); - - xml = writer.toString(); - if (xml.length() == 0) { - throw new IOException("Server returned blank xml document"); - } - - Xml.parse(xml, handler); - final ScheduleInformation schedule = handler.getSchedule(); - return schedule; - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } catch (IOException e) { - if (attemptNumber < MAX_ATTEMPTS - 1) { - try { - Log.w(Constants.TAG, - "Attempt to contact server failed... retrying in 3s", - e); - Thread.sleep(3000); - } catch (InterruptedException interrupt) { - // Ignore... just go on to next attempt - } - return getScheduleFromNetwork(params, attemptNumber + 1); - } else { - mException = new Exception("Could not contact BART system", e); - return null; - } - } catch (SAXException e) { - mException = new Exception( - "Could not understand response from BART system: " + xml, e); - return null; - } - } - - @Override - protected void onPostExecute(ScheduleInformation result) { - if (result != null) { - onResult(result); - } else { - onError(mException); - } - } - - public abstract void onResult(ScheduleInformation result); - - public abstract void onError(Exception exception); -} +package com.dougkeen.bart.networktasks; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; + +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpUriRequest; +import org.xml.sax.SAXException; + +import android.os.AsyncTask; +import android.util.Log; +import android.util.Xml; + +import com.dougkeen.bart.model.Constants; +import com.dougkeen.bart.model.ScheduleInformation; +import com.dougkeen.bart.model.StationPair; + +public abstract class GetScheduleInformationTask extends + AsyncTask { + + private final static String SCHED_URL = "http://api.bart.gov/api/sched.aspx?cmd=depart&key=" + + Constants.API_KEY + "&orig=%1$s&dest=%2$s&b=1&a=4"; + + private final static int MAX_ATTEMPTS = 5; + + private Exception mException; + + @Override + protected ScheduleInformation doInBackground(StationPair... paramsArray) { + // Always expect one param + StationPair params = paramsArray[0]; + + if (!isCancelled()) { + return getScheduleFromNetwork(params, 0); + } else { + return null; + } + } + + private ScheduleInformation getScheduleFromNetwork(StationPair params, + int attemptNumber) { + String xml = null; + try { + String url = String.format(SCHED_URL, + params.getOrigin().abbreviation, + params.getDestination().abbreviation); + + HttpUriRequest request = new HttpGet(url); + + if (isCancelled()) { + return null; + } + + ScheduleContentHandler handler = new ScheduleContentHandler( + params.getOrigin(), params.getDestination()); + + HttpResponse response = NetworkUtils.executeWithRecovery(request); + + if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { + throw new IOException("Server returned " + + response.getStatusLine().toString()); + } + + StringWriter writer = new StringWriter(); + IOUtils.copy(response.getEntity().getContent(), writer, "UTF-8"); + + xml = writer.toString(); + if (xml.length() == 0) { + throw new IOException("Server returned blank xml document"); + } + + Xml.parse(xml, handler); + final ScheduleInformation schedule = handler.getSchedule(); + return schedule; + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } catch (IOException e) { + if (attemptNumber < MAX_ATTEMPTS - 1) { + try { + Log.w(Constants.TAG, + "Attempt to contact server failed... retrying in 3s", + e); + Thread.sleep(3000); + } catch (InterruptedException interrupt) { + // Ignore... just go on to next attempt + } + return getScheduleFromNetwork(params, attemptNumber + 1); + } else { + mException = new Exception("Could not contact BART system", e); + return null; + } + } catch (SAXException e) { + mException = new Exception( + "Could not understand response from BART system: " + xml, e); + return null; + } + } + + @Override + protected void onPostExecute(ScheduleInformation result) { + if (result != null) { + onResult(result); + } else { + onError(mException); + } + } + + public abstract void onResult(ScheduleInformation result); + + public abstract void onError(Exception exception); +} diff --git a/src/com/dougkeen/bart/networktasks/NetworkUtils.java b/src/com/dougkeen/bart/networktasks/NetworkUtils.java index 6d06981..6d8914f 100644 --- a/src/com/dougkeen/bart/networktasks/NetworkUtils.java +++ b/src/com/dougkeen/bart/networktasks/NetworkUtils.java @@ -1,38 +1,40 @@ -package com.dougkeen.bart.networktasks; - -import java.io.IOException; - -import org.apache.http.HttpResponse; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.conn.params.ConnManagerParams; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; - -public class NetworkUtils { - - public static HttpClient getHttpClient() { - HttpClient client = new DefaultHttpClient(); - final HttpParams params = client.getParams(); - HttpConnectionParams.setConnectionTimeout(params, - NetworkUtils.CONNECTION_TIMEOUT_MILLIS); - HttpConnectionParams.setSoTimeout(params, NetworkUtils.CONNECTION_TIMEOUT_MILLIS); - ConnManagerParams.setTimeout(params, NetworkUtils.CONNECTION_TIMEOUT_MILLIS); - return client; - } - - public static HttpResponse executeWithRecovery(final HttpUriRequest request) - throws IOException, ClientProtocolException { - try { - return getHttpClient().execute(request); - } catch (IllegalStateException e) { - // try again... this is a rare error - return getHttpClient().execute(request); - } - } - - static final int CONNECTION_TIMEOUT_MILLIS = 10000; - -} +package com.dougkeen.bart.networktasks; + +import java.io.IOException; + +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.conn.params.ConnManagerParams; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.params.HttpConnectionParams; +import org.apache.http.params.HttpParams; + +public class NetworkUtils { + + public static HttpClient getHttpClient() { + HttpClient client = new DefaultHttpClient(); + final HttpParams params = client.getParams(); + HttpConnectionParams.setConnectionTimeout(params, + NetworkUtils.CONNECTION_TIMEOUT_MILLIS); + HttpConnectionParams.setSoTimeout(params, + NetworkUtils.CONNECTION_TIMEOUT_MILLIS); + ConnManagerParams.setTimeout(params, + NetworkUtils.CONNECTION_TIMEOUT_MILLIS); + return client; + } + + public static HttpResponse executeWithRecovery(final HttpUriRequest request) + throws IOException, ClientProtocolException { + try { + return getHttpClient().execute(request); + } catch (IllegalStateException e) { + // try again... this is a rare error + return getHttpClient().execute(request); + } + } + + static final int CONNECTION_TIMEOUT_MILLIS = 10000; + +} diff --git a/src/com/dougkeen/bart/networktasks/ScheduleContentHandler.java b/src/com/dougkeen/bart/networktasks/ScheduleContentHandler.java index 074fa88..412c618 100644 --- a/src/com/dougkeen/bart/networktasks/ScheduleContentHandler.java +++ b/src/com/dougkeen/bart/networktasks/ScheduleContentHandler.java @@ -1,167 +1,167 @@ -package com.dougkeen.bart.networktasks; - -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.List; -import java.util.TimeZone; - -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -import android.util.Log; - -import com.dougkeen.bart.model.Constants; -import com.dougkeen.bart.model.ScheduleInformation; -import com.dougkeen.bart.model.ScheduleItem; -import com.dougkeen.bart.model.Station; - -public class ScheduleContentHandler extends DefaultHandler { - public ScheduleContentHandler(Station origin, Station destination) { - super(); - schedule = new ScheduleInformation(origin, destination); - } - - private final static List TAGS = Arrays.asList("date", "time", - "trip", "leg"); - - private final static DateFormat TRIP_DATE_FORMAT; - private final static DateFormat REQUEST_DATE_FORMAT; - - private final static TimeZone PACIFIC_TIME = TimeZone - .getTimeZone("America/Los_Angeles"); - - static { - TRIP_DATE_FORMAT = new SimpleDateFormat("MM/dd/yyyy h:mm a"); - REQUEST_DATE_FORMAT = new SimpleDateFormat("MMM d, yyyy h:mm a"); - - TRIP_DATE_FORMAT.setTimeZone(PACIFIC_TIME); - REQUEST_DATE_FORMAT.setTimeZone(PACIFIC_TIME); - } - - private ScheduleInformation schedule; - - public ScheduleInformation getSchedule() { - return schedule; - } - - private String currentValue; - private boolean isParsingTag; - - private String requestDate; - private String requestTime; - - private ScheduleItem currentTrip; - - @Override - public void characters(char[] ch, int start, int length) - throws SAXException { - if (isParsingTag) { - currentValue = new String(ch, start, length); - } - } - - @Override - public void startElement(String uri, String localName, String qName, - Attributes attributes) throws SAXException { - if (TAGS.contains(localName)) { - isParsingTag = true; - } - final int numberOfAttributes = attributes.getLength(); - if (localName.equals("trip")) { - currentTrip = new ScheduleItem(); - String originDate = null; - String originTime = null; - String destinationDate = null; - String destinationTime = null; - for (int i = 0; i < numberOfAttributes; i++) { - if (attributes.getLocalName(i).equalsIgnoreCase("origin")) { - currentTrip.setOrigin(Station.getByAbbreviation(attributes - .getValue(i))); - } else if (attributes.getLocalName(i).equalsIgnoreCase( - "destination")) { - currentTrip.setDestination(Station - .getByAbbreviation(attributes.getValue(i))); - } else if (attributes.getLocalName(i).equalsIgnoreCase("fare")) { - currentTrip.setFare(attributes.getValue(i)); - } else if (attributes.getLocalName(i).equalsIgnoreCase( - "origTimeMin")) { - originTime = attributes.getValue(i); - } else if (attributes.getLocalName(i).equalsIgnoreCase( - "origTimeDate")) { - originDate = attributes.getValue(i); - } else if (attributes.getLocalName(i).equalsIgnoreCase( - "destTimeMin")) { - destinationTime = attributes.getValue(i); - } else if (attributes.getLocalName(i).equalsIgnoreCase( - "destTimeDate")) { - destinationDate = attributes.getValue(i); - } else if (attributes.getLocalName(i).equalsIgnoreCase( - "bikeFlag")) { - currentTrip.setBikesAllowed(attributes.getValue(i).equals( - "1")); - } - } - - long departTime = parseDate(TRIP_DATE_FORMAT, originDate, - originTime); - if (departTime > 0) - currentTrip.setDepartureTime(departTime); - - long arriveTime = parseDate(TRIP_DATE_FORMAT, destinationDate, - destinationTime); - if (arriveTime > 0) - currentTrip.setArrivalTime(arriveTime); - - schedule.addTrip(currentTrip); - } - if (localName.equals("leg")) { - String legNumber = null; - for (int i = 0; i < numberOfAttributes; i++) { - if (attributes.getLocalName(i).equals("order")) { - legNumber = attributes.getValue(i); - } else if (attributes.getLocalName(i) - .equals("trainHeadStation") && "1".equals(legNumber)) { - currentTrip.setTrainHeadStation(attributes.getValue(i) - .toLowerCase()); - } - } - } - } - - private long parseDate(DateFormat format, String dateString, - String timeString) { - if (dateString == null || timeString == null) { - return -1; - } - try { - return format.parse(dateString + " " + timeString).getTime(); - } catch (ParseException e) { - Log.e(Constants.TAG, "Unable to parse datetime '" + dateString - + " " + timeString + "'", e); - return -1; - } - } - - @Override - public void endElement(String uri, String localName, String qName) - throws SAXException { - if (localName.equals("date")) { - requestDate = currentValue; - } else if (localName.equals("time")) { - requestTime = currentValue; - } - isParsingTag = false; - currentValue = null; - } - - @Override - public void endDocument() { - long date = parseDate(REQUEST_DATE_FORMAT, requestDate, requestTime); - if (date > 0) { - schedule.setDate(date); - } - } -} +package com.dougkeen.bart.networktasks; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.List; +import java.util.TimeZone; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import android.util.Log; + +import com.dougkeen.bart.model.Constants; +import com.dougkeen.bart.model.ScheduleInformation; +import com.dougkeen.bart.model.ScheduleItem; +import com.dougkeen.bart.model.Station; + +public class ScheduleContentHandler extends DefaultHandler { + public ScheduleContentHandler(Station origin, Station destination) { + super(); + schedule = new ScheduleInformation(origin, destination); + } + + private final static List TAGS = Arrays.asList("date", "time", + "trip", "leg"); + + private final static DateFormat TRIP_DATE_FORMAT; + private final static DateFormat REQUEST_DATE_FORMAT; + + private final static TimeZone PACIFIC_TIME = TimeZone + .getTimeZone("America/Los_Angeles"); + + static { + TRIP_DATE_FORMAT = new SimpleDateFormat("MM/dd/yyyy h:mm a"); + REQUEST_DATE_FORMAT = new SimpleDateFormat("MMM d, yyyy h:mm a"); + + TRIP_DATE_FORMAT.setTimeZone(PACIFIC_TIME); + REQUEST_DATE_FORMAT.setTimeZone(PACIFIC_TIME); + } + + private ScheduleInformation schedule; + + public ScheduleInformation getSchedule() { + return schedule; + } + + private String currentValue; + private boolean isParsingTag; + + private String requestDate; + private String requestTime; + + private ScheduleItem currentTrip; + + @Override + public void characters(char[] ch, int start, int length) + throws SAXException { + if (isParsingTag) { + currentValue = new String(ch, start, length); + } + } + + @Override + public void startElement(String uri, String localName, String qName, + Attributes attributes) throws SAXException { + if (TAGS.contains(localName)) { + isParsingTag = true; + } + final int numberOfAttributes = attributes.getLength(); + if (localName.equals("trip")) { + currentTrip = new ScheduleItem(); + String originDate = null; + String originTime = null; + String destinationDate = null; + String destinationTime = null; + for (int i = 0; i < numberOfAttributes; i++) { + if (attributes.getLocalName(i).equalsIgnoreCase("origin")) { + currentTrip.setOrigin(Station.getByAbbreviation(attributes + .getValue(i))); + } else if (attributes.getLocalName(i).equalsIgnoreCase( + "destination")) { + currentTrip.setDestination(Station + .getByAbbreviation(attributes.getValue(i))); + } else if (attributes.getLocalName(i).equalsIgnoreCase("fare")) { + currentTrip.setFare(attributes.getValue(i)); + } else if (attributes.getLocalName(i).equalsIgnoreCase( + "origTimeMin")) { + originTime = attributes.getValue(i); + } else if (attributes.getLocalName(i).equalsIgnoreCase( + "origTimeDate")) { + originDate = attributes.getValue(i); + } else if (attributes.getLocalName(i).equalsIgnoreCase( + "destTimeMin")) { + destinationTime = attributes.getValue(i); + } else if (attributes.getLocalName(i).equalsIgnoreCase( + "destTimeDate")) { + destinationDate = attributes.getValue(i); + } else if (attributes.getLocalName(i).equalsIgnoreCase( + "bikeFlag")) { + currentTrip.setBikesAllowed(attributes.getValue(i).equals( + "1")); + } + } + + long departTime = parseDate(TRIP_DATE_FORMAT, originDate, + originTime); + if (departTime > 0) + currentTrip.setDepartureTime(departTime); + + long arriveTime = parseDate(TRIP_DATE_FORMAT, destinationDate, + destinationTime); + if (arriveTime > 0) + currentTrip.setArrivalTime(arriveTime); + + schedule.addTrip(currentTrip); + } + if (localName.equals("leg")) { + String legNumber = null; + for (int i = 0; i < numberOfAttributes; i++) { + if (attributes.getLocalName(i).equals("order")) { + legNumber = attributes.getValue(i); + } else if (attributes.getLocalName(i) + .equals("trainHeadStation") && "1".equals(legNumber)) { + currentTrip.setTrainHeadStation(attributes.getValue(i) + .toLowerCase()); + } + } + } + } + + private long parseDate(DateFormat format, String dateString, + String timeString) { + if (dateString == null || timeString == null) { + return -1; + } + try { + return format.parse(dateString + " " + timeString).getTime(); + } catch (ParseException e) { + Log.e(Constants.TAG, "Unable to parse datetime '" + dateString + + " " + timeString + "'", e); + return -1; + } + } + + @Override + public void endElement(String uri, String localName, String qName) + throws SAXException { + if (localName.equals("date")) { + requestDate = currentValue; + } else if (localName.equals("time")) { + requestTime = currentValue; + } + isParsingTag = false; + currentValue = null; + } + + @Override + public void endDocument() { + long date = parseDate(REQUEST_DATE_FORMAT, requestDate, requestTime); + if (date > 0) { + schedule.setDate(date); + } + } +} diff --git a/src/com/dougkeen/bart/receivers/AlarmBroadcastReceiver.java b/src/com/dougkeen/bart/receivers/AlarmBroadcastReceiver.java index 0ecd8f3..4fb6826 100644 --- a/src/com/dougkeen/bart/receivers/AlarmBroadcastReceiver.java +++ b/src/com/dougkeen/bart/receivers/AlarmBroadcastReceiver.java @@ -24,7 +24,8 @@ public class AlarmBroadcastReceiver extends BroadcastReceiver { application.setPlayAlarmRingtone(true); - Intent targetIntent = new Intent(Intent.ACTION_VIEW, boardedDeparture.getStationPair().getUri()); + Intent targetIntent = new Intent(Intent.ACTION_VIEW, boardedDeparture + .getStationPair().getUri()); targetIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(targetIntent);