diff --git a/AndroidManifest.xml b/AndroidManifest.xml index d71fdbe..7ed173a 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -61,10 +61,22 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/com/dougkeen/bart/AlarmBroadcastReceiver.java b/src/com/dougkeen/bart/AlarmBroadcastReceiver.java new file mode 100644 index 0000000..554adce --- /dev/null +++ b/src/com/dougkeen/bart/AlarmBroadcastReceiver.java @@ -0,0 +1,22 @@ +package com.dougkeen.bart; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +public class AlarmBroadcastReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + WakeLocker.acquire(context); + + Intent targetIntent = new Intent(Intent.ACTION_VIEW, intent.getData()); + targetIntent.putExtra("boardedDeparture", intent.getExtras() + .getParcelable("departure")); + targetIntent.putExtra("isAlarm", true); + targetIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + context.startActivity(targetIntent); + } + +} diff --git a/src/com/dougkeen/bart/NotificationService.java b/src/com/dougkeen/bart/NotificationService.java new file mode 100644 index 0000000..223c6fb --- /dev/null +++ b/src/com/dougkeen/bart/NotificationService.java @@ -0,0 +1,258 @@ +package com.dougkeen.bart; + +import java.util.List; + +import android.app.AlarmManager; +import android.app.IntentService; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.SystemClock; +import android.support.v4.app.NotificationCompat; +import android.support.v4.app.NotificationCompat.Builder; + +import com.dougkeen.bart.EtdService.EtdServiceBinder; +import com.dougkeen.bart.EtdService.EtdServiceListener; +import com.dougkeen.bart.model.Constants; +import com.dougkeen.bart.model.Departure; +import com.dougkeen.bart.model.StationPair; + +public class NotificationService extends IntentService implements + EtdServiceListener { + + private static final int DEPARTURE_NOTIFICATION_ID = 123; + private boolean mBound = false; + private EtdService mEtdService; + private Departure mDeparture; + private StationPair mStationPair; + private NotificationManager mNotificationManager; + private AlarmManager mAlarmManager; + private PendingIntent mNotificationIntent; + private PendingIntent mAlarmPendingIntent; + private int mAlertLeadTime; + private Handler mHandler; + + public NotificationService() { + super("BartRunnerNotificationService"); + } + + private final ServiceConnection mConnection = new ServiceConnection() { + @Override + public void onServiceDisconnected(ComponentName name) { + mEtdService = null; + mBound = false; + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + mEtdService = ((EtdServiceBinder) service).getService(); + if (getStationPair() != null) { + mEtdService.registerListener(NotificationService.this); + } + mBound = true; + } + }; + + @Override + public void onCreate() { + bindService(new Intent(this, EtdService.class), mConnection, + Context.BIND_AUTO_CREATE); + mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + mAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); + mHandler = new Handler(); + super.onCreate(); + } + + @Override + public void onDestroy() { + if (mBound) + unbindService(mConnection); + super.onDestroy(); + } + + @Override + protected void onHandleIntent(Intent intent) { + Bundle bundle = intent.getExtras(); + Departure departure = (Departure) bundle.getParcelable("departure"); + mStationPair = (StationPair) bundle.getParcelable("stationPair"); + mAlertLeadTime = bundle.getInt("alertLeadTime"); + + if (mEtdService != null && mDeparture != null + && !mDeparture.equals(departure)) { + mEtdService.unregisterListener(this); + } + + mDeparture = departure; + + if (getStationPair() != null && mEtdService != null) { + mEtdService.registerListener(this); + } + + updateNotification(); + + Intent targetIntent = new Intent(Intent.ACTION_VIEW, + mStationPair.getUri()); + targetIntent.putExtra("boardedDeparture", mDeparture); + targetIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + mNotificationIntent = PendingIntent.getActivity(this, 0, targetIntent, + PendingIntent.FLAG_UPDATE_CURRENT); + + setAlarm(); + + pollDepartureStatus(); + } + + private void refreshAlarmPendingIntent() { + final Intent alarmIntent = new Intent(Constants.ACTION_ALARM, + getStationPair().getUri()); + alarmIntent.putExtra("departure", mDeparture); + mAlarmPendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, + PendingIntent.FLAG_UPDATE_CURRENT); + } + + private void setAlarm() { + cancelAlarm(); + + if (mAlertLeadTime > 0) { + long alertTime = getAlertClockTime(); + if (alertTime > System.currentTimeMillis()) { + refreshAlarmPendingIntent(); + mAlarmManager.set(AlarmManager.RTC_WAKEUP, alertTime, + mAlarmPendingIntent); + } + } + } + + private void triggerAlarmImmediately() { + cancelAlarm(); + refreshAlarmPendingIntent(); + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + 100, mAlarmPendingIntent); + } + + private long getAlertClockTime() { + return mDeparture.getMeanEstimate() - mAlertLeadTime * 60 * 1000; + } + + private void cancelAlarm() { + mAlarmManager.cancel(mAlarmPendingIntent); + } + + @Override + public void onETDChanged(List departures) { + for (Departure departure : departures) { + if (departure.equals(mDeparture) + && (mDeparture.getMeanSecondsLeft() != departure + .getMeanSecondsLeft() || mDeparture + .getUncertaintySeconds() != departure + .getUncertaintySeconds())) { + long initialAlertClockTime = getAlertClockTime(); + + mDeparture.mergeEstimate(departure); + + final long now = System.currentTimeMillis(); + if (initialAlertClockTime > now + && getAlertClockTime() <= System.currentTimeMillis()) { + // Alert time was changed to the past + triggerAlarmImmediately(); + } else if (getAlertClockTime() > System.currentTimeMillis()) { + // Alert time is still in the future + setAlarm(); + } + + break; + } + } + } + + @Override + public void onError(String errorMessage) { + // Do nothing + } + + @Override + public void onRequestStarted() { + // Do nothing + } + + @Override + public void onRequestEnded() { + // Do nothing + } + + @Override + public StationPair getStationPair() { + return mStationPair; + } + + private long mNextScheduledCheckClockTime = 0; + + private void pollDepartureStatus() { + if (mDeparture.hasDeparted()) { + shutDown(); + } + + updateNotification(); + + final int pollIntervalMillis = getPollIntervalMillis(); + final long scheduledCheckClockTime = System.currentTimeMillis() + + pollIntervalMillis; + if (mNextScheduledCheckClockTime < scheduledCheckClockTime) { + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + pollDepartureStatus(); + } + }, pollIntervalMillis); + mNextScheduledCheckClockTime = scheduledCheckClockTime; + } + } + + private void shutDown() { + mEtdService.unregisterListener(this); + mNotificationManager.cancel(DEPARTURE_NOTIFICATION_ID); + cancelAlarm(); + stopSelf(); + } + + private void updateNotification() { + Builder notificationBuilder = new NotificationCompat.Builder(this); + notificationBuilder.setOngoing(true); + notificationBuilder.setSmallIcon(R.drawable.icon); + final int minutes = mDeparture.getMeanSecondsLeft() / 60; + final String minutesText = (minutes == 0) ? "Less than one minute" + : (minutes + " minute" + ((minutes != 1) ? "s" : "")); + notificationBuilder.setContentTitle(mStationPair.getDestination().name); + notificationBuilder.setContentText(minutesText + " until departure"); + notificationBuilder.setContentIntent(mNotificationIntent); + mNotificationManager.notify(DEPARTURE_NOTIFICATION_ID, + notificationBuilder.getNotification()); + } + + private int getPollIntervalMillis() { + final int secondsToAlarm = mDeparture.getMeanSecondsLeft() + - mAlertLeadTime * 60; + + if (secondsToAlarm < -20) { + /* Alarm should have already gone off by now */ + shutDown(); + return 10000000; // Arbitrarily large number + } + + if (secondsToAlarm > 10 * 60) { + return 60 * 1000; + } else if (secondsToAlarm > 5 * 60) { + return 60 * 1000; + } else if (secondsToAlarm > 3 * 60) { + return 30 * 1000; + } else { + return 10 * 1000; + } + } +} diff --git a/src/com/dougkeen/bart/TrainAlertDialogFragment.java b/src/com/dougkeen/bart/TrainAlertDialogFragment.java index 9386d46..13604b2 100644 --- a/src/com/dougkeen/bart/TrainAlertDialogFragment.java +++ b/src/com/dougkeen/bart/TrainAlertDialogFragment.java @@ -4,6 +4,7 @@ import net.simonvt.widget.NumberPicker; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; +import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.os.Bundle; @@ -12,15 +13,18 @@ import android.support.v4.app.FragmentActivity; import com.WazaBe.HoloEverywhere.AlertDialog; import com.dougkeen.bart.model.Departure; +import com.dougkeen.bart.model.StationPair; public class TrainAlertDialogFragment extends DialogFragment { - private static final String KEY_LAST_ALERT_DELAY = "lastAlertDelay"; + private static final String KEY_LAST_ALERT_LEAD_TIME = "lastAlertLeadTime"; private Departure mDeparture; + private StationPair mStationPair; - public TrainAlertDialogFragment(Departure mDeparture) { + public TrainAlertDialogFragment(Departure departure, StationPair stationPair) { super(); - this.mDeparture = mDeparture; + this.mDeparture = departure; + this.mStationPair = stationPair; } @Override @@ -29,7 +33,7 @@ public class TrainAlertDialogFragment extends DialogFragment { SharedPreferences preferences = getActivity().getPreferences( Context.MODE_PRIVATE); - int lastAlertDelay = preferences.getInt(KEY_LAST_ALERT_DELAY, 5); + int lastAlertLeadTime = preferences.getInt(KEY_LAST_ALERT_LEAD_TIME, 5); NumberPicker numberPicker = (NumberPicker) getDialog().findViewById( R.id.numberPicker); @@ -44,8 +48,8 @@ public class TrainAlertDialogFragment extends DialogFragment { numberPicker.setMaxValue(maxValue); numberPicker.setDisplayedValues(displayedValues); - if (maxValue >= lastAlertDelay) { - numberPicker.setValue(lastAlertDelay); + if (maxValue >= lastAlertLeadTime) { + numberPicker.setValue(lastAlertLeadTime); } else if (maxValue >= 5) { numberPicker.setValue(5); } else if (maxValue >= 3) { @@ -70,12 +74,22 @@ public class TrainAlertDialogFragment extends DialogFragment { int which) { NumberPicker numberPicker = (NumberPicker) getDialog() .findViewById(R.id.numberPicker); + final int alertLeadTime = numberPicker + .getValue(); + // Save most recent selection Editor editor = getActivity().getPreferences( Context.MODE_PRIVATE).edit(); - editor.putInt(KEY_LAST_ALERT_DELAY, - numberPicker.getValue()); + editor.putInt(KEY_LAST_ALERT_LEAD_TIME, + alertLeadTime); editor.commit(); + + Intent intent = new Intent(getActivity(), + NotificationService.class); + intent.putExtra("departure", mDeparture); + intent.putExtra("stationPair", mStationPair); + intent.putExtra("alertLeadTime", alertLeadTime); + getActivity().startService(intent); } }) .setNegativeButton(R.string.skip_alert, diff --git a/src/com/dougkeen/bart/ViewDeparturesActivity.java b/src/com/dougkeen/bart/ViewDeparturesActivity.java index 62b69b6..19698e7 100644 --- a/src/com/dougkeen/bart/ViewDeparturesActivity.java +++ b/src/com/dougkeen/bart/ViewDeparturesActivity.java @@ -13,7 +13,6 @@ import android.net.Uri; import android.os.Bundle; import android.os.IBinder; import android.os.Parcelable; -import android.os.PowerManager; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; @@ -21,6 +20,7 @@ import android.text.format.DateFormat; import android.text.util.Linkify; import android.util.Log; import android.view.View; +import android.view.WindowManager; import android.widget.AdapterView; import android.widget.ImageView; import android.widget.ListAdapter; @@ -62,8 +62,6 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements private TextView mEmptyView; private ProgressBar mProgress; - private PowerManager.WakeLock mWakeLock; - private ActionMode mActionMode; private EtdService mEtdService; @@ -73,6 +71,7 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setContentView(R.layout.departures); final Intent intent = getIntent(); @@ -92,6 +91,7 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements .getString("origin")); mDestination = Station.getByAbbreviation(savedInstanceState .getString("destination")); + setListTitle(); } else { getSupportLoaderManager().initLoader(LOADER_ID, null, new LoaderCallbacks() { @@ -117,9 +117,7 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements mDestination = Station.getByAbbreviation(cursor .getString(1)); cursor.close(); - ((TextView) findViewById(R.id.listTitle)) - .setText(mOrigin.name + " to " - + mDestination.name); + setListTitle(); if (mBound && mEtdService != null) mEtdService .registerListener(ViewDeparturesActivity.this); @@ -141,6 +139,13 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements mDeparturesAdapter = new DepartureArrayAdapter(this, R.layout.departure_listing); + + if (intent.getExtras() != null + && intent.getExtras().containsKey("boardedDeparture")) { + mBoardedDeparture = (Departure) intent.getExtras().getParcelable( + "boardedDeparture"); + } + if (savedInstanceState != null) { if (savedInstanceState.containsKey("departures")) { for (Parcelable departure : savedInstanceState @@ -186,6 +191,11 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements getSupportActionBar().setDisplayHomeAsUpEnabled(true); } + private void setListTitle() { + ((TextView) findViewById(R.id.listTitle)).setText(mOrigin.name + " to " + + mDestination.name); + } + @SuppressWarnings("unchecked") private AdapterView getListView() { return (AdapterView) findViewById(android.R.id.list); @@ -225,6 +235,7 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements if (mBound) unbindService(mConnection); Ticker.getInstance().stopTicking(); + WakeLocker.release(); } @Override @@ -254,14 +265,10 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus) { - PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); - mWakeLock = powerManager - .newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, - "ViewDeparturesActivity"); - mWakeLock.acquire(); + getWindow() + .addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + Ticker.getInstance().startTicking(); refreshBoardedDeparture(); - } else if (mWakeLock != null) { - mWakeLock.release(); } } @@ -276,7 +283,10 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements public boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); if (itemId == android.R.id.home) { - finish(); + Intent intent = new Intent(Intent.ACTION_VIEW, + Constants.FAVORITE_CONTENT_URI); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); return true; } else if (itemId == R.id.view_on_bart_site_button) { startActivity(new Intent( @@ -381,8 +391,9 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements // Don't prompt for alert if train is about to leave if (mBoardedDeparture.getMeanSecondsLeft() / 60 > 1) { - new TrainAlertDialogFragment(mBoardedDeparture).show( - getSupportFragmentManager(), "dialog"); + new TrainAlertDialogFragment(mBoardedDeparture, + getStationPair()).show(getSupportFragmentManager(), + "dialog"); } mode.finish(); @@ -440,7 +451,6 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements } else { final DepartureArrayAdapter listAdapter = getListAdapter(); listAdapter.clear(); - // addAll() method isn't available until API level 11 for (Departure departure : departures) { listAdapter.add(departure); } @@ -449,7 +459,7 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements if (mBoardedDeparture != null) { for (Departure departure : departures) { if (departure.equals(mBoardedDeparture)) { - mBoardedDeparture = departure; + mBoardedDeparture.mergeEstimate(departure); refreshBoardedDeparture(); break; } diff --git a/src/com/dougkeen/bart/WakeLocker.java b/src/com/dougkeen/bart/WakeLocker.java new file mode 100644 index 0000000..f08d48b --- /dev/null +++ b/src/com/dougkeen/bart/WakeLocker.java @@ -0,0 +1,29 @@ +package com.dougkeen.bart; + +import android.content.Context; +import android.os.PowerManager; + +import com.dougkeen.bart.model.Constants; + +public abstract class WakeLocker { + private static PowerManager.WakeLock wakeLock; + + public static void acquire(Context ctx) { + if (wakeLock != null) + wakeLock.release(); + + PowerManager pm = (PowerManager) ctx + .getSystemService(Context.POWER_SERVICE); + wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK + | PowerManager.ACQUIRE_CAUSES_WAKEUP + | PowerManager.ON_AFTER_RELEASE + | PowerManager.SCREEN_DIM_WAKE_LOCK, Constants.TAG); + wakeLock.acquire(); + } + + public static void release() { + if (wakeLock != null) + wakeLock.release(); + wakeLock = null; + } +} \ No newline at end of file diff --git a/src/com/dougkeen/bart/model/Constants.java b/src/com/dougkeen/bart/model/Constants.java index 0a47659..479a077 100644 --- a/src/com/dougkeen/bart/model/Constants.java +++ b/src/com/dougkeen/bart/model/Constants.java @@ -14,6 +14,7 @@ public class Constants { .parse("content://" + AUTHORITY + "/route"); public static final String MAP_URL = "http://m.bart.gov/images/global/system-map29.gif"; - public static final String TAG = "BartRunner"; + 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 f26a696..0f79e4c 100644 --- a/src/com/dougkeen/bart/model/Departure.java +++ b/src/com/dougkeen/bart/model/Departure.java @@ -7,6 +7,7 @@ import android.content.Context; import android.os.Parcel; import android.os.Parcelable; import android.text.format.DateFormat; +import android.util.Log; public class Departure implements Parcelable, Comparable { private static final int MINIMUM_MERGE_OVERLAP_MILLIS = 10000; @@ -206,11 +207,19 @@ public class Departure implements Parcelable, Comparable { } public int getMeanSecondsLeft() { - return (int) ((getMeanEstimate() - System.currentTimeMillis()) / 1000); + return (int) getMeanSecondsLeft(getMinEstimate(), getMaxEstimate()); + } + + public int getMeanSecondsLeft(long min, long max) { + return (int) ((getMeanEstimate(min, max) - System.currentTimeMillis()) / 1000); } public long getMeanEstimate() { - return (getMinEstimate() + getMaxEstimate()) / 2; + return getMeanEstimate(getMinEstimate(), getMaxEstimate()); + } + + public long getMeanEstimate(long min, long max) { + return (min + max) / 2; } public long getArrivalTimeOverride() { @@ -280,15 +289,25 @@ public class Departure implements Parcelable, Comparable { 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. + /* + * 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()); + } + 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 + /* + * The estimate must have changed... just use the latest incoming + * values + */ setMinEstimate(departure.getMinEstimate()); setMaxEstimate(departure.getMaxEstimate()); return; @@ -298,15 +317,23 @@ public class Departure implements Parcelable, Comparable { departure.getMinEstimate()); final long newMax = Math.min(getMaxEstimate(), departure.getMaxEstimate()); + + /* + * If the new departure would mark this as departed, and we have < 1 + * minute 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 can never have 0 or negative uncertainty setMinEstimate(newMin); setMaxEstimate(newMax); } - - if (!hasAnyArrivalEstimate() && departure.hasAnyArrivalEstimate()) { - setArrivalTimeOverride(departure.getArrivalTimeOverride()); - setEstimatedTripTime(departure.getEstimatedTripTime()); - } } public int compareTo(Departure another) { @@ -435,6 +462,7 @@ public class Departure implements Parcelable, Comparable { } public void writeToParcel(Parcel dest, int flags) { + dest.writeString(origin.abbreviation); dest.writeString(destination.abbreviation); dest.writeString(destinationColor); dest.writeString(platform); @@ -445,9 +473,15 @@ public class Departure implements Parcelable, Comparable { dest.writeInt(minutes); dest.writeLong(minEstimate); dest.writeLong(maxEstimate); + dest.writeLong(arrivalTimeOverride); + dest.writeInt(estimatedTripTime); + dest.writeInt(line.ordinal()); + dest.writeBooleanArray(new boolean[] { beganAsDeparted, bikeAllowed, + requiresTransfer, transferScheduled }); } private void readFromParcel(Parcel in) { + origin = Station.getByAbbreviation(in.readString()); destination = Station.getByAbbreviation(in.readString()); destinationColor = in.readString(); platform = in.readString(); @@ -458,6 +492,15 @@ public class Departure implements Parcelable, Comparable { minutes = in.readInt(); minEstimate = in.readLong(); maxEstimate = in.readLong(); + arrivalTimeOverride = in.readLong(); + estimatedTripTime = in.readInt(); + line = Line.values()[in.readInt()]; + boolean[] bools = new boolean[4]; + in.readBooleanArray(bools); + beganAsDeparted = bools[0]; + bikeAllowed = bools[1]; + requiresTransfer = bools[2]; + transferScheduled = bools[3]; } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { diff --git a/src/com/dougkeen/bart/model/Station.java b/src/com/dougkeen/bart/model/Station.java index 9874fe7..3368d5e 100644 --- a/src/com/dougkeen/bart/model/Station.java +++ b/src/com/dougkeen/bart/model/Station.java @@ -64,7 +64,7 @@ public enum Station { public final boolean longStationLinger; public final int departureEqualityTolerance; - public final static int DEFAULT_DEPARTURE_EQUALITY_TOLERANCE = 59999; + public final static int DEFAULT_DEPARTURE_EQUALITY_TOLERANCE = 119999; private Station(String abbreviation, String name, boolean invertDirection, boolean endOfLine) { diff --git a/src/com/dougkeen/bart/model/StationPair.java b/src/com/dougkeen/bart/model/StationPair.java index 31ed9f0..01e0180 100644 --- a/src/com/dougkeen/bart/model/StationPair.java +++ b/src/com/dougkeen/bart/model/StationPair.java @@ -1,12 +1,20 @@ package com.dougkeen.bart.model; -public class StationPair { +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +public class StationPair implements Parcelable { public StationPair(Station origin, Station destination) { super(); this.origin = origin; this.destination = destination; } + public StationPair(Parcel in) { + readFromParcel(in); + } + private Station origin; private Station destination; @@ -23,6 +31,16 @@ public class StationPair { || (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; @@ -49,4 +67,28 @@ public class StationPair { 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