diff --git a/res/layout/your_train.xml b/res/layout/your_train.xml index a906d13..a1f54de 100644 --- a/res/layout/your_train.xml +++ b/res/layout/your_train.xml @@ -2,6 +2,18 @@ + + + - - - BART Runner - Favorite routes - No favorite routes have been added yet - Loading, please wait… - Add a route - Origin - Destination - Save - The origin and destination stations must be - different - You must select a destination station - You must select an origin station - Please wait while real time departure data is - loaded - No departure data is currently available for this - route. Note that this route may require a non-standard transfer due to - a temporary change in service. Check for service advisories posted at - http://m.bart.gov/schedules/advisories/ - View - View departures - Missing/inaccurate departure? Feature request? Please report to bartrunner@dougkeen.com - Delete - Yes - Cancel - View details on BART site - Could not connect to BART services. Please try - again later. - Also add return route - View system map - System map - Departures - OK - Quick departure lookup - I will board this train - Departure options - Your train - Set up departure alarm - Your train is leaving soon! - Silence alarm - Cancel alarm - Set alarm - Leaving - - \ No newline at end of file + + + + BART Runner + Favorite routes + No favorite routes have been added yet + Loading, please wait… + Add a route + Origin + Destination + Save + The origin and destination stations must be + different + You must select a destination station + You must select an origin station + Please wait while real time departure data is + loaded + No departure data is currently available for this + route. Note that this route may require a non-standard transfer due to + a temporary change in service. Check for service advisories posted at + http://m.bart.gov/schedules/advisories/ + View + View departures + Missing/inaccurate departure? Feature request? Please report to bartrunner@dougkeen.com + Delete + Yes + Cancel + View details on BART site + Could not connect to BART services. Please try + again later. + Also add return route + View system map + System map + Departures + OK + Quick departure lookup + I will board this train + Departure options + Your train + Set up departure alarm + Your train is leaving soon! + Silence alarm + Cancel alarm + Set alarm + Leaving + Departed + \ No newline at end of file diff --git a/src/com/dougkeen/bart/activities/ViewDeparturesActivity.java b/src/com/dougkeen/bart/activities/ViewDeparturesActivity.java index 93d7269..48286e4 100644 --- a/src/com/dougkeen/bart/activities/ViewDeparturesActivity.java +++ b/src/com/dougkeen/bart/activities/ViewDeparturesActivity.java @@ -176,7 +176,7 @@ public class ViewDeparturesActivity extends SActivity implements && mSelectedDeparture != null) { ((Checkable) findViewById(R.id.yourTrainSection)) .setChecked(true); - startYourTrainActionMode(bartRunnerApplication); + startYourTrainActionMode(); } } setListAdapter(mDeparturesAdapter); @@ -245,9 +245,7 @@ public class ViewDeparturesActivity extends SActivity implements tryToPlayRingtone(alarmSound); } final Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE); - if (vibrator.hasVibrator()) { - vibrator.vibrate(new long[] { 0, 500, 500 }, 1); - } + vibrator.vibrate(new long[] { 0, 500, 500 }, 1); mHandler.postDelayed(new Runnable() { @Override public void run() { @@ -276,9 +274,7 @@ public class ViewDeparturesActivity extends SActivity implements application.setAlarmSounding(false); application.setAlarmMediaPlayer(null); final Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE); - if (vibrator.hasVibrator()) { - vibrator.cancel(); - } + vibrator.cancel(); try { if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.stop(); @@ -363,7 +359,7 @@ public class ViewDeparturesActivity extends SActivity implements @Override public void onClick(View v) { ((Checkable) v).setChecked(true); - startYourTrainActionMode((BartRunnerApplication) getApplication()); + startYourTrainActionMode(); } }; @@ -472,28 +468,28 @@ public class ViewDeparturesActivity extends SActivity implements } private void refreshBoardedDeparture(boolean animate) { - final Departure boardedDeparture = ((BartRunnerApplication) getApplication()) - .getBoardedDeparture(); + final Departure boardedDeparture = getBoardedDeparture(); int currentVisibility = mYourTrainSection.getVisibility(); - final boolean boardedDepartureDoesNotApply = boardedDeparture == null - || boardedDeparture.getStationPair() == null - || !boardedDeparture.getStationPair().equals(getStationPair()); - - if (boardedDepartureDoesNotApply) { + if (!doesDepartureApply(boardedDeparture)) { if (currentVisibility != View.GONE) { hideYourTrainSection(); } return; } - mYourTrainSection.updateFromDeparture(boardedDeparture); + mYourTrainSection.updateFromBoardedDeparture(); if (currentVisibility != View.VISIBLE) { showYourTrainSection(animate); } } + private boolean doesDepartureApply(final Departure departure) { + return departure != null && departure.getStationPair() != null + && departure.getStationPair().equals(getStationPair()); + } + private void setBoardedDeparture(Departure selectedDeparture) { final BartRunnerApplication application = (BartRunnerApplication) getApplication(); selectedDeparture.setPassengerDestination(mDestination); @@ -546,14 +542,13 @@ public class ViewDeparturesActivity extends SActivity implements } - private void startYourTrainActionMode(BartRunnerApplication application) { + private void startYourTrainActionMode() { if (mActionMode == null) mActionMode = startActionMode(new YourTrainActionMode()); mActionMode.setTitle(R.string.your_train); - if (application.getBoardedDeparture() != null - && application.getBoardedDeparture().isAlarmPending()) { - int leadTime = application.getBoardedDeparture() - .getAlarmLeadTimeMinutes(); + Departure boardedDeparture = getBoardedDeparture(); + if (boardedDeparture != null && boardedDeparture.isAlarmPending()) { + int leadTime = boardedDeparture.getAlarmLeadTimeMinutes(); mActionMode.setSubtitle(getAlarmSubtitle(leadTime)); } else { mActionMode.setSubtitle(null); @@ -579,9 +574,7 @@ public class ViewDeparturesActivity extends SActivity implements .findItem(R.id.cancel_alarm_button); final MenuItem setAlarmButton = menu .findItem(R.id.set_alarm_button); - final BartRunnerApplication application = (BartRunnerApplication) getApplication(); - final Departure boardedDeparture = application - .getBoardedDeparture(); + final Departure boardedDeparture = getBoardedDeparture(); if (boardedDeparture == null) { mode.finish(); @@ -652,10 +645,8 @@ public class ViewDeparturesActivity extends SActivity implements public boolean onActionItemClicked(ActionMode mode, MenuItem item) { final int itemId = item.getItemId(); if (itemId == R.id.set_alarm_button) { - BartRunnerApplication application = (BartRunnerApplication) getApplication(); - // Don't prompt for alarm if train is about to leave - if (application.getBoardedDeparture().getMeanSecondsLeft() > 60) { + if (getBoardedDeparture().getMeanSecondsLeft() > 60) { new TrainAlarmDialogFragment() .show(getSupportFragmentManager() .beginTransaction()); @@ -680,9 +671,7 @@ public class ViewDeparturesActivity extends SActivity implements public void onDestroyActionMode(ActionMode mode) { ((Checkable) findViewById(R.id.yourTrainSection)).setChecked(false); - final BartRunnerApplication application = (BartRunnerApplication) getApplication(); - final Departure boardedDeparture = application - .getBoardedDeparture(); + final Departure boardedDeparture = getBoardedDeparture(); if (boardedDeparture != null) { boardedDeparture.getAlarmPendingObservable() .unregisterObserver(mAlarmPendingObserver); @@ -719,10 +708,18 @@ public class ViewDeparturesActivity extends SActivity implements Ticker.getInstance().startTicking( ViewDeparturesActivity.this); + Departure boardedDeparture = getBoardedDeparture(); + boolean boardedDepartureFound = false; + // Merge lists if (mDeparturesAdapter.getCount() > 0) { int adapterIndex = -1; for (Departure departure : departures) { + if (!boardedDepartureFound + && departure.equals(boardedDeparture)) { + boardedDepartureFound = true; + } + adapterIndex++; Departure existingDeparture = null; if (adapterIndex < mDeparturesAdapter.getCount()) { @@ -755,6 +752,11 @@ public class ViewDeparturesActivity extends SActivity implements } } + if (doesDepartureApply(boardedDeparture) + && !boardedDepartureFound) { + boardedDeparture.setListedInETDs(false); + } + refreshBoardedDeparture(true); getListAdapter().notifyDataSetChanged(); @@ -771,8 +773,7 @@ public class ViewDeparturesActivity extends SActivity implements if (isDepartureActionModeActive() && mSelectedDeparture != null) { targetDeparture = mSelectedDeparture; } else { - targetDeparture = ((BartRunnerApplication) getApplication()) - .getBoardedDeparture(); + targetDeparture = getBoardedDeparture(); } for (int i = getListAdapter().getCount() - 1; i >= 0; i--) { if (getListAdapter().getItem(i).equals(targetDeparture)) { @@ -851,4 +852,8 @@ public class ViewDeparturesActivity extends SActivity implements && !mActionMode.getTitle().equals( getString(R.string.your_train)); } + + private Departure getBoardedDeparture() { + return ((BartRunnerApplication) getApplication()).getBoardedDeparture(); + } } diff --git a/src/com/dougkeen/bart/controls/YourTrainLayout.java b/src/com/dougkeen/bart/controls/YourTrainLayout.java index 2b7a748..4cfa2ca 100644 --- a/src/com/dougkeen/bart/controls/YourTrainLayout.java +++ b/src/com/dougkeen/bart/controls/YourTrainLayout.java @@ -1,5 +1,6 @@ package com.dougkeen.bart.controls; +import android.app.Activity; import android.content.Context; import android.graphics.Color; import android.graphics.drawable.GradientDrawable; @@ -11,9 +12,11 @@ import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; +import com.dougkeen.bart.BartRunnerApplication; import com.dougkeen.bart.R; import com.dougkeen.bart.model.Departure; import com.dougkeen.bart.model.TextProvider; +import com.dougkeen.util.Observer; public class YourTrainLayout extends RelativeLayout implements Checkable { @@ -38,6 +41,28 @@ public class YourTrainLayout extends RelativeLayout implements Checkable { private boolean mChecked; + private Departure mDeparture; + + private final Observer mAlarmLeadObserver = new Observer() { + @Override + public void onUpdate(Integer newValue) { + final Activity context = (Activity) getContext(); + if (context != null) { + context.runOnUiThread(mUpdateAlarmIndicatorRunnable); + } + } + }; + + private final Observer mAlarmPendingObserver = new Observer() { + @Override + public void onUpdate(Boolean newValue) { + final Activity context = (Activity) getContext(); + if (context != null) { + context.runOnUiThread(mUpdateAlarmIndicatorRunnable); + } + } + }; + @Override public boolean isChecked() { return mChecked; @@ -64,7 +89,27 @@ public class YourTrainLayout extends RelativeLayout implements Checkable { setChecked(!isChecked()); } - public void updateFromDeparture(final Departure boardedDeparture) { + public void updateFromBoardedDeparture() { + final Departure boardedDeparture = ((BartRunnerApplication) ((Activity) getContext()) + .getApplication()).getBoardedDeparture(); + if (boardedDeparture == null) + return; + + if (!boardedDeparture.equals(mDeparture)) { + if (mDeparture != null) { + mDeparture.getAlarmLeadTimeMinutesObservable() + .unregisterObserver(mAlarmLeadObserver); + mDeparture.getAlarmPendingObservable().unregisterObserver( + mAlarmPendingObserver); + } + boardedDeparture.getAlarmLeadTimeMinutesObservable() + .registerObserver(mAlarmLeadObserver); + boardedDeparture.getAlarmPendingObservable().registerObserver( + mAlarmPendingObserver); + } + + mDeparture = boardedDeparture; + ((TextView) findViewById(R.id.yourTrainDestinationText)) .setText(boardedDeparture.getTrainDestination().toString()); @@ -88,6 +133,9 @@ public class YourTrainLayout extends RelativeLayout implements Checkable { ((ImageView) findViewById(R.id.yourTrainXferIcon)) .setVisibility(View.INVISIBLE); } + + updateAlarmIndicator(); + CountdownTextView departureCountdown = (CountdownTextView) findViewById(R.id.yourTrainDepartureCountdown); CountdownTextView arrivalCountdown = (CountdownTextView) findViewById(R.id.yourTrainArrivalCountdown); @@ -95,7 +143,7 @@ public class YourTrainLayout extends RelativeLayout implements Checkable { @Override public String getText(long tickNumber) { if (boardedDeparture.hasDeparted()) { - return getContext().getString(R.string.leaving); + return boardedDeparture.getCountdownText(); } else { return "Leaves in " + boardedDeparture.getCountdownText() + " " + boardedDeparture.getUncertaintyText(); @@ -117,4 +165,21 @@ public class YourTrainLayout extends RelativeLayout implements Checkable { setBackground(); } + + private void updateAlarmIndicator() { + if (!mDeparture.isAlarmPending()) { + findViewById(R.id.alarmText).setVisibility(GONE); + } else { + findViewById(R.id.alarmText).setVisibility(VISIBLE); + ((TextView) findViewById(R.id.alarmText)).setText(String + .valueOf(mDeparture.getAlarmLeadTimeMinutes())); + } + } + + private final Runnable mUpdateAlarmIndicatorRunnable = new Runnable() { + @Override + public void run() { + updateAlarmIndicator(); + } + }; } diff --git a/src/com/dougkeen/bart/model/Departure.java b/src/com/dougkeen/bart/model/Departure.java index 794aa9c..fc99f5f 100644 --- a/src/com/dougkeen/bart/model/Departure.java +++ b/src/com/dougkeen/bart/model/Departure.java @@ -74,6 +74,8 @@ public class Departure implements Parcelable, Comparable { 0); private Observable alarmPending = new Observable(false); + private boolean listedInETDs = true; + public Station getOrigin() { return origin; } @@ -463,9 +465,12 @@ public class Departure implements Parcelable, Comparable { if (hasDeparted()) { if (origin != null && origin.longStationLinger && beganAsDeparted) { builder.append("At station"); - } else { + } else if (isListedInETDs()) { builder.append(BartRunnerApplication.getAppContext().getString( R.string.leaving)); + } else { + builder.append(BartRunnerApplication.getAppContext().getString( + R.string.departed)); } } else { builder.append(secondsLeft / 60); @@ -484,6 +489,14 @@ public class Departure implements Parcelable, Comparable { } } + public boolean isListedInETDs() { + return listedInETDs; + } + + public void setListedInETDs(boolean listedInETDs) { + this.listedInETDs = listedInETDs; + } + public int getAlarmLeadTimeMinutes() { return alarmLeadTimeMinutes.getValue(); } diff --git a/src/com/dougkeen/bart/services/BoardedDepartureService.java b/src/com/dougkeen/bart/services/BoardedDepartureService.java index 733e3f3..c7c766d 100644 --- a/src/com/dougkeen/bart/services/BoardedDepartureService.java +++ b/src/com/dougkeen/bart/services/BoardedDepartureService.java @@ -136,6 +136,8 @@ public class BoardedDepartureService extends Service implements if (intent.getBooleanExtra("clearBoardedDeparture", false)) { application.setBoardedDeparture(null); shutDown(false); + } else { + updateNotification(); } return; } @@ -232,6 +234,19 @@ public class BoardedDepartureService extends Service implements return; } + if (mEtdService != null) { + /* + * Make sure we're still listening for ETD changes (in case weak ref + * was garbage collected). Not a huge fan of this approach, but I + * think I'd rather keep the weak references to avoid memory leaks + * than move to soft references or some other form of stronger + * reference. Besides, registerListener() should only result in a + * few constant-time map operations, so there shouldn't be a big + * performance hit. + */ + mEtdService.registerListener(this, false); + } + boardedDeparture.updateAlarm(getApplicationContext(), mAlarmManager); updateNotification();