Refactored boarded departure alarms/notifications
Departure single click now selects boarded departure "Your train" now has its own context menu/action bar
@ -67,7 +67,7 @@
|
|||||||
android:label="BartRunner data provider" />
|
android:label="BartRunner data provider" />
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".services.NotificationService"
|
android:name=".services.BoardedDepartureService"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
<service
|
<service
|
||||||
android:name=".services.EtdService"
|
android:name=".services.EtdService"
|
||||||
|
BIN
res/drawable-hdpi/ic_action_add_alarm.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
res/drawable-hdpi/ic_action_alarm.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
res/drawable-ldpi/ic_action_add_alarm.png
Normal file
After Width: | Height: | Size: 472 B |
BIN
res/drawable-ldpi/ic_action_alarm.png
Normal file
After Width: | Height: | Size: 608 B |
BIN
res/drawable-mdpi/ic_action_add_alarm.png
Normal file
After Width: | Height: | Size: 671 B |
BIN
res/drawable-mdpi/ic_action_alarm.png
Normal file
After Width: | Height: | Size: 874 B |
BIN
res/drawable-xhdpi/ic_action_add_alarm.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
res/drawable-xhdpi/ic_action_alarm.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
@ -14,81 +14,14 @@
|
|||||||
android:paddingRight="5dp"
|
android:paddingRight="5dp"
|
||||||
android:textSize="24dp" />
|
android:textSize="24dp" />
|
||||||
|
|
||||||
<RelativeLayout
|
<com.dougkeen.bart.controls.YourTrainLayout
|
||||||
android:id="@+id/yourTrainSection"
|
android:id="@+id/yourTrainSection"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_horizontal"
|
android:layout_gravity="center_horizontal"
|
||||||
android:background="#222"
|
|
||||||
android:padding="10dp"
|
android:padding="10dp"
|
||||||
android:visibility="gone" >
|
android:visibility="gone" >
|
||||||
|
</com.dougkeen.bart.controls.YourTrainLayout>
|
||||||
<TextView
|
|
||||||
android:id="@+id/yourTrainHeader"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentTop="true"
|
|
||||||
android:layout_centerHorizontal="true"
|
|
||||||
android:text="@string/your_train"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:textSize="20dp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/yourTrainDestinationColorBar"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentLeft="true"
|
|
||||||
android:layout_below="@id/yourTrainHeader"
|
|
||||||
android:src="@drawable/basic_rectangle" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/yourTrainDestinationText"
|
|
||||||
style="@style/DepartureDestinationText"
|
|
||||||
android:layout_below="@id/yourTrainHeader"
|
|
||||||
android:layout_toRightOf="@id/yourTrainDestinationColorBar"
|
|
||||||
android:ellipsize="marquee"
|
|
||||||
android:singleLine="true" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/yourTrainBikeIcon"
|
|
||||||
style="@style/BikeIcon"
|
|
||||||
android:layout_below="@id/yourTrainHeader"
|
|
||||||
android:layout_toRightOf="@id/yourTrainDestinationText"
|
|
||||||
android:src="@drawable/bike" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/yourTrainXferIcon"
|
|
||||||
style="@style/XferIcon"
|
|
||||||
android:layout_below="@id/yourTrainBikeIcon"
|
|
||||||
android:layout_toRightOf="@id/yourTrainDestinationText"
|
|
||||||
android:src="@drawable/xfer" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/yourTrainTrainLengthText"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@id/yourTrainDestinationText"
|
|
||||||
android:layout_toRightOf="@id/yourTrainDestinationColorBar"
|
|
||||||
android:paddingLeft="5dp" />
|
|
||||||
|
|
||||||
<com.dougkeen.bart.controls.CountdownTextView
|
|
||||||
android:id="@+id/yourTrainDepartureCountdown"
|
|
||||||
style="@style/DepartureCountdownText"
|
|
||||||
android:layout_alignLeft="@id/yourTrainSection"
|
|
||||||
android:layout_alignRight="@id/yourTrainSection"
|
|
||||||
android:layout_below="@id/yourTrainTrainLengthText"
|
|
||||||
bart:tickInterval="1" />
|
|
||||||
|
|
||||||
<com.dougkeen.bart.controls.CountdownTextView
|
|
||||||
android:id="@+id/yourTrainArrivalCountdown"
|
|
||||||
style="@style/DepartureCountdownText"
|
|
||||||
android:layout_alignLeft="@id/yourTrainSection"
|
|
||||||
android:layout_alignRight="@id/yourTrainSection"
|
|
||||||
android:layout_below="@id/yourTrainDepartureCountdown"
|
|
||||||
android:ellipsize="end"
|
|
||||||
bart:tickInterval="5" />
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@android:id/progress"
|
android:id="@android:id/progress"
|
||||||
|
71
res/layout/your_train.xml
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:bart="http://schemas.android.com/apk/res/com.dougkeen.bart" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/yourTrainHeader"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:text="@string/your_train"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:textSize="20dp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/yourTrainDestinationColorBar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:layout_below="@id/yourTrainHeader"
|
||||||
|
android:src="@drawable/basic_rectangle" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/yourTrainDestinationText"
|
||||||
|
style="@style/DepartureDestinationText"
|
||||||
|
android:layout_below="@id/yourTrainHeader"
|
||||||
|
android:layout_toRightOf="@id/yourTrainDestinationColorBar"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:singleLine="true" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/yourTrainBikeIcon"
|
||||||
|
style="@style/BikeIcon"
|
||||||
|
android:layout_below="@id/yourTrainHeader"
|
||||||
|
android:layout_toRightOf="@id/yourTrainDestinationText"
|
||||||
|
android:src="@drawable/bike" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/yourTrainXferIcon"
|
||||||
|
style="@style/XferIcon"
|
||||||
|
android:layout_below="@id/yourTrainBikeIcon"
|
||||||
|
android:layout_toRightOf="@id/yourTrainDestinationText"
|
||||||
|
android:src="@drawable/xfer" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/yourTrainTrainLengthText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/yourTrainDestinationText"
|
||||||
|
android:layout_toRightOf="@id/yourTrainDestinationColorBar"
|
||||||
|
android:paddingLeft="5dp" />
|
||||||
|
|
||||||
|
<com.dougkeen.bart.controls.CountdownTextView
|
||||||
|
android:id="@+id/yourTrainDepartureCountdown"
|
||||||
|
style="@style/DepartureCountdownText"
|
||||||
|
android:layout_alignLeft="@id/yourTrainSection"
|
||||||
|
android:layout_alignRight="@id/yourTrainSection"
|
||||||
|
android:layout_below="@id/yourTrainTrainLengthText"
|
||||||
|
bart:tickInterval="1" />
|
||||||
|
|
||||||
|
<com.dougkeen.bart.controls.CountdownTextView
|
||||||
|
android:id="@+id/yourTrainArrivalCountdown"
|
||||||
|
style="@style/DepartureCountdownText"
|
||||||
|
android:layout_alignLeft="@id/yourTrainSection"
|
||||||
|
android:layout_alignRight="@id/yourTrainSection"
|
||||||
|
android:layout_below="@id/yourTrainDepartureCountdown"
|
||||||
|
android:ellipsize="end"
|
||||||
|
bart:tickInterval="5" />
|
||||||
|
|
||||||
|
</merge>
|
@ -1,12 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/cancel_alarm_button"
|
|
||||||
android:icon="@drawable/ic_action_cancel_alarm"
|
|
||||||
android:showAsAction="always|withText"
|
|
||||||
android:title="Cancel alarm"
|
|
||||||
android:visible="false"/>
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/view_on_bart_site_button"
|
android:id="@+id/view_on_bart_site_button"
|
||||||
android:icon="@drawable/ic_action_web"
|
android:icon="@drawable/ic_action_web"
|
||||||
|
22
res/menu/your_train_context_menu.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/cancel_alarm_button"
|
||||||
|
android:icon="@drawable/ic_action_cancel_alarm"
|
||||||
|
android:showAsAction="always|withText"
|
||||||
|
android:title="@string/cancel_alarm"
|
||||||
|
android:visible="false"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/set_alarm_button"
|
||||||
|
android:icon="@drawable/ic_action_alarm"
|
||||||
|
android:showAsAction="always|withText"
|
||||||
|
android:title="@string/set_alarm"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/delete"
|
||||||
|
android:icon="@drawable/ic_action_delete"
|
||||||
|
android:showAsAction="always|withText"
|
||||||
|
android:title="@string/delete">
|
||||||
|
</item>
|
||||||
|
|
||||||
|
</menu>
|
@ -3,5 +3,6 @@
|
|||||||
|
|
||||||
<color name="black">#FF000000</color>
|
<color name="black">#FF000000</color>
|
||||||
<color name="blue_selection">#FF2A7998</color>
|
<color name="blue_selection">#FF2A7998</color>
|
||||||
|
<color name="gray">#FF222222</color>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
@ -4,7 +4,7 @@
|
|||||||
<string name="app_name">BART Runner</string>
|
<string name="app_name">BART Runner</string>
|
||||||
<string name="favorite_routes">Favorite routes</string>
|
<string name="favorite_routes">Favorite routes</string>
|
||||||
<string name="empty_favorites_list_message">No favorite routes have been added yet</string>
|
<string name="empty_favorites_list_message">No favorite routes have been added yet</string>
|
||||||
<string name="loading_message">Loading, please wait...</string>
|
<string name="loading_message">Loading, please wait…</string>
|
||||||
<string name="add_route">Add a route</string>
|
<string name="add_route">Add a route</string>
|
||||||
<string name="origin">Origin</string>
|
<string name="origin">Origin</string>
|
||||||
<string name="destination">Destination</string>
|
<string name="destination">Destination</string>
|
||||||
@ -37,9 +37,10 @@
|
|||||||
<string name="getting_on_this_train">I will board this train</string>
|
<string name="getting_on_this_train">I will board this train</string>
|
||||||
<string name="departure_options">Departure options</string>
|
<string name="departure_options">Departure options</string>
|
||||||
<string name="your_train">Your train</string>
|
<string name="your_train">Your train</string>
|
||||||
<string name="skip_alert">Skip alert</string>
|
<string name="set_up_departure_alarm">Set up departure alarm</string>
|
||||||
<string name="set_up_departure_alert">Set up departure alert</string>
|
<string name="train_alarm_text">Your train is leaving soon!</string>
|
||||||
<string name="train_alert_text">Your train is leaving soon!</string>
|
|
||||||
<string name="silence_alarm">Silence alarm</string>
|
<string name="silence_alarm">Silence alarm</string>
|
||||||
|
<string name="cancel_alarm">Cancel alarm</string>
|
||||||
|
<string name="set_alarm">Set alarm</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
@ -17,7 +17,6 @@ import android.util.Log;
|
|||||||
|
|
||||||
import com.dougkeen.bart.model.Constants;
|
import com.dougkeen.bart.model.Constants;
|
||||||
import com.dougkeen.bart.model.Departure;
|
import com.dougkeen.bart.model.Departure;
|
||||||
import com.dougkeen.util.Observable;
|
|
||||||
|
|
||||||
public class BartRunnerApplication extends Application {
|
public class BartRunnerApplication extends Application {
|
||||||
private static final int FIVE_MINUTES = 5 * 60 * 1000;
|
private static final int FIVE_MINUTES = 5 * 60 * 1000;
|
||||||
@ -26,8 +25,6 @@ public class BartRunnerApplication extends Application {
|
|||||||
|
|
||||||
private Departure mBoardedDeparture;
|
private Departure mBoardedDeparture;
|
||||||
|
|
||||||
private Observable<Boolean> mAlarmPending = new Observable<Boolean>(false);
|
|
||||||
|
|
||||||
private boolean mPlayAlarmRingtone;
|
private boolean mPlayAlarmRingtone;
|
||||||
|
|
||||||
private boolean mAlarmSounding;
|
private boolean mAlarmSounding;
|
||||||
@ -84,24 +81,42 @@ public class BartRunnerApplication extends Application {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (mBoardedDeparture != null && mBoardedDeparture.hasExpired()) {
|
||||||
|
setBoardedDeparture(null);
|
||||||
|
}
|
||||||
return mBoardedDeparture;
|
return mBoardedDeparture;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBoardedDeparture(Departure boardedDeparture) {
|
public void setBoardedDeparture(Departure boardedDeparture) {
|
||||||
if (!ObjectUtils.equals(boardedDeparture, mBoardedDeparture)
|
if (!ObjectUtils.equals(boardedDeparture, mBoardedDeparture)
|
||||||
|| ObjectUtils.compare(mBoardedDeparture, boardedDeparture) != 0) {
|
|| ObjectUtils.compare(mBoardedDeparture, boardedDeparture) != 0) {
|
||||||
// Cancel any pending alarms for the current departure
|
if (this.mBoardedDeparture != null) {
|
||||||
if (this.mBoardedDeparture != null
|
this.mBoardedDeparture.getAlarmLeadTimeMinutesObservable()
|
||||||
&& this.mBoardedDeparture.isAlarmPending()) {
|
.unregisterAllObservers();
|
||||||
this.mBoardedDeparture.cancelAlarm(this,
|
this.mBoardedDeparture.getAlarmPendingObservable()
|
||||||
(AlarmManager) getSystemService(Context.ALARM_SERVICE));
|
.unregisterAllObservers();
|
||||||
|
|
||||||
|
// Cancel any pending alarms for the current departure
|
||||||
|
if (this.mBoardedDeparture.isAlarmPending()) {
|
||||||
|
this.mBoardedDeparture
|
||||||
|
.cancelAlarm(
|
||||||
|
this,
|
||||||
|
(AlarmManager) getSystemService(Context.ALARM_SERVICE));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mBoardedDeparture = boardedDeparture;
|
this.mBoardedDeparture = boardedDeparture;
|
||||||
|
|
||||||
if (mBoardedDeparture != null) {
|
File cachedDepartureFile = new File(getCacheDir(), CACHE_FILE_NAME);
|
||||||
File cachedDepartureFile = new File(getCacheDir(),
|
if (mBoardedDeparture == null) {
|
||||||
CACHE_FILE_NAME);
|
try {
|
||||||
|
cachedDepartureFile.delete();
|
||||||
|
} catch (SecurityException anotherException) {
|
||||||
|
Log.w(Constants.TAG,
|
||||||
|
"Couldn't delete lastBoardedDeparture file",
|
||||||
|
anotherException);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
FileOutputStream fileOutputStream = null;
|
FileOutputStream fileOutputStream = null;
|
||||||
try {
|
try {
|
||||||
fileOutputStream = new FileOutputStream(cachedDepartureFile);
|
fileOutputStream = new FileOutputStream(cachedDepartureFile);
|
||||||
@ -134,17 +149,4 @@ public class BartRunnerApplication extends Application {
|
|||||||
public void setAlarmMediaPlayer(MediaPlayer alarmMediaPlayer) {
|
public void setAlarmMediaPlayer(MediaPlayer alarmMediaPlayer) {
|
||||||
this.mAlarmMediaPlayer = alarmMediaPlayer;
|
this.mAlarmMediaPlayer = alarmMediaPlayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAlarmPending() {
|
|
||||||
return mAlarmPending.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Observable<Boolean> getAlarmPendingObservable() {
|
|
||||||
return mAlarmPending;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAlarmPending(boolean alarmPending) {
|
|
||||||
this.mAlarmPending.setValue(alarmPending);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -13,12 +13,13 @@ import android.support.v4.app.FragmentActivity;
|
|||||||
import com.WazaBe.HoloEverywhere.AlertDialog;
|
import com.WazaBe.HoloEverywhere.AlertDialog;
|
||||||
import com.dougkeen.bart.BartRunnerApplication;
|
import com.dougkeen.bart.BartRunnerApplication;
|
||||||
import com.dougkeen.bart.R;
|
import com.dougkeen.bart.R;
|
||||||
|
import com.dougkeen.bart.model.Departure;
|
||||||
|
|
||||||
public class TrainAlertDialogFragment extends DialogFragment {
|
public class TrainAlarmDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
private static final String KEY_LAST_ALERT_LEAD_TIME = "lastAlertLeadTime";
|
private static final String KEY_LAST_ALARM_LEAD_TIME = "lastAlarmLeadTime";
|
||||||
|
|
||||||
public TrainAlertDialogFragment() {
|
public TrainAlarmDialogFragment() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,7 +29,7 @@ public class TrainAlertDialogFragment extends DialogFragment {
|
|||||||
|
|
||||||
SharedPreferences preferences = getActivity().getPreferences(
|
SharedPreferences preferences = getActivity().getPreferences(
|
||||||
Context.MODE_PRIVATE);
|
Context.MODE_PRIVATE);
|
||||||
int lastAlertLeadTime = preferences.getInt(KEY_LAST_ALERT_LEAD_TIME, 5);
|
int lastAlarmLeadTime = preferences.getInt(KEY_LAST_ALARM_LEAD_TIME, 5);
|
||||||
|
|
||||||
NumberPicker numberPicker = (NumberPicker) getDialog().findViewById(
|
NumberPicker numberPicker = (NumberPicker) getDialog().findViewById(
|
||||||
R.id.numberPicker);
|
R.id.numberPicker);
|
||||||
@ -36,8 +37,8 @@ public class TrainAlertDialogFragment extends DialogFragment {
|
|||||||
BartRunnerApplication application = (BartRunnerApplication) getActivity()
|
BartRunnerApplication application = (BartRunnerApplication) getActivity()
|
||||||
.getApplication();
|
.getApplication();
|
||||||
|
|
||||||
final int maxValue = application.getBoardedDeparture()
|
final Departure boardedDeparture = application.getBoardedDeparture();
|
||||||
.getMeanSecondsLeft() / 60;
|
final int maxValue = boardedDeparture.getMeanSecondsLeft() / 60;
|
||||||
|
|
||||||
String[] displayedValues = new String[maxValue];
|
String[] displayedValues = new String[maxValue];
|
||||||
for (int i = 1; i <= maxValue; i++) {
|
for (int i = 1; i <= maxValue; i++) {
|
||||||
@ -47,8 +48,10 @@ public class TrainAlertDialogFragment extends DialogFragment {
|
|||||||
numberPicker.setMaxValue(maxValue);
|
numberPicker.setMaxValue(maxValue);
|
||||||
numberPicker.setDisplayedValues(displayedValues);
|
numberPicker.setDisplayedValues(displayedValues);
|
||||||
|
|
||||||
if (maxValue >= lastAlertLeadTime) {
|
if (boardedDeparture.isAlarmPending()) {
|
||||||
numberPicker.setValue(lastAlertLeadTime);
|
numberPicker.setValue(boardedDeparture.getAlarmLeadTimeMinutes());
|
||||||
|
} else if (maxValue >= lastAlarmLeadTime) {
|
||||||
|
numberPicker.setValue(lastAlarmLeadTime);
|
||||||
} else if (maxValue >= 5) {
|
} else if (maxValue >= 5) {
|
||||||
numberPicker.setValue(5);
|
numberPicker.setValue(5);
|
||||||
} else if (maxValue >= 3) {
|
} else if (maxValue >= 3) {
|
||||||
@ -63,9 +66,9 @@ public class TrainAlertDialogFragment extends DialogFragment {
|
|||||||
final FragmentActivity activity = getActivity();
|
final FragmentActivity activity = getActivity();
|
||||||
|
|
||||||
return new AlertDialog.Builder(activity)
|
return new AlertDialog.Builder(activity)
|
||||||
.setTitle(R.string.set_up_departure_alert)
|
.setTitle(R.string.set_up_departure_alarm)
|
||||||
.setCancelable(true)
|
.setCancelable(true)
|
||||||
.setView(R.layout.train_alert_dialog)
|
.setView(R.layout.train_alarm_dialog)
|
||||||
.setPositiveButton(R.string.ok,
|
.setPositiveButton(R.string.ok,
|
||||||
new DialogInterface.OnClickListener() {
|
new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
@ -73,23 +76,23 @@ public class TrainAlertDialogFragment extends DialogFragment {
|
|||||||
int which) {
|
int which) {
|
||||||
NumberPicker numberPicker = (NumberPicker) getDialog()
|
NumberPicker numberPicker = (NumberPicker) getDialog()
|
||||||
.findViewById(R.id.numberPicker);
|
.findViewById(R.id.numberPicker);
|
||||||
final int alertLeadTime = numberPicker
|
final int alarmLeadTime = numberPicker
|
||||||
.getValue();
|
.getValue();
|
||||||
|
|
||||||
// Save most recent selection
|
// Save most recent selection
|
||||||
Editor editor = getActivity().getPreferences(
|
Editor editor = getActivity().getPreferences(
|
||||||
Context.MODE_PRIVATE).edit();
|
Context.MODE_PRIVATE).edit();
|
||||||
editor.putInt(KEY_LAST_ALERT_LEAD_TIME,
|
editor.putInt(KEY_LAST_ALARM_LEAD_TIME,
|
||||||
alertLeadTime);
|
alarmLeadTime);
|
||||||
editor.commit();
|
editor.commit();
|
||||||
|
|
||||||
((BartRunnerApplication) getActivity()
|
((BartRunnerApplication) getActivity()
|
||||||
.getApplication())
|
.getApplication())
|
||||||
.getBoardedDeparture().setUpAlarm(
|
.getBoardedDeparture().setUpAlarm(
|
||||||
alertLeadTime);
|
alarmLeadTime);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.setNegativeButton(R.string.skip_alert,
|
.setNegativeButton(R.string.cancel,
|
||||||
new DialogInterface.OnClickListener() {
|
new DialogInterface.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog,
|
public void onClick(DialogInterface dialog,
|
||||||
int whichButton) {
|
int whichButton) {
|
@ -10,8 +10,6 @@ import android.content.DialogInterface;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.ServiceConnection;
|
import android.content.ServiceConnection;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.drawable.GradientDrawable;
|
|
||||||
import android.media.MediaPlayer;
|
import android.media.MediaPlayer;
|
||||||
import android.media.RingtoneManager;
|
import android.media.RingtoneManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@ -28,7 +26,7 @@ import android.util.Log;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.ImageView;
|
import android.widget.Checkable;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@ -41,19 +39,18 @@ import com.actionbarsherlock.view.MenuInflater;
|
|||||||
import com.actionbarsherlock.view.MenuItem;
|
import com.actionbarsherlock.view.MenuItem;
|
||||||
import com.dougkeen.bart.BartRunnerApplication;
|
import com.dougkeen.bart.BartRunnerApplication;
|
||||||
import com.dougkeen.bart.R;
|
import com.dougkeen.bart.R;
|
||||||
import com.dougkeen.bart.controls.CountdownTextView;
|
|
||||||
import com.dougkeen.bart.controls.Ticker;
|
import com.dougkeen.bart.controls.Ticker;
|
||||||
|
import com.dougkeen.bart.controls.YourTrainLayout;
|
||||||
import com.dougkeen.bart.data.DepartureArrayAdapter;
|
import com.dougkeen.bart.data.DepartureArrayAdapter;
|
||||||
import com.dougkeen.bart.data.RoutesColumns;
|
import com.dougkeen.bart.data.RoutesColumns;
|
||||||
import com.dougkeen.bart.model.Constants;
|
import com.dougkeen.bart.model.Constants;
|
||||||
import com.dougkeen.bart.model.Departure;
|
import com.dougkeen.bart.model.Departure;
|
||||||
import com.dougkeen.bart.model.Station;
|
import com.dougkeen.bart.model.Station;
|
||||||
import com.dougkeen.bart.model.StationPair;
|
import com.dougkeen.bart.model.StationPair;
|
||||||
import com.dougkeen.bart.model.TextProvider;
|
import com.dougkeen.bart.services.BoardedDepartureService;
|
||||||
import com.dougkeen.bart.services.EtdService;
|
import com.dougkeen.bart.services.EtdService;
|
||||||
import com.dougkeen.bart.services.EtdService.EtdServiceBinder;
|
import com.dougkeen.bart.services.EtdService.EtdServiceBinder;
|
||||||
import com.dougkeen.bart.services.EtdService.EtdServiceListener;
|
import com.dougkeen.bart.services.EtdService.EtdServiceListener;
|
||||||
import com.dougkeen.bart.services.NotificationService;
|
|
||||||
import com.dougkeen.util.Observer;
|
import com.dougkeen.util.Observer;
|
||||||
import com.dougkeen.util.WakeLocker;
|
import com.dougkeen.util.WakeLocker;
|
||||||
|
|
||||||
@ -98,6 +95,8 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements
|
|||||||
|
|
||||||
final Uri uri = mUri;
|
final Uri uri = mUri;
|
||||||
|
|
||||||
|
final BartRunnerApplication bartRunnerApplication = (BartRunnerApplication) getApplication();
|
||||||
|
|
||||||
if (savedInstanceState != null
|
if (savedInstanceState != null
|
||||||
&& savedInstanceState.containsKey("origin")
|
&& savedInstanceState.containsKey("origin")
|
||||||
&& savedInstanceState.containsKey("destination")) {
|
&& savedInstanceState.containsKey("destination")) {
|
||||||
@ -166,41 +165,41 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements
|
|||||||
mSelectedDeparture = (Departure) savedInstanceState
|
mSelectedDeparture = (Departure) savedInstanceState
|
||||||
.getParcelable("selectedDeparture");
|
.getParcelable("selectedDeparture");
|
||||||
}
|
}
|
||||||
if (savedInstanceState.getBoolean("hasActionMode")
|
if (savedInstanceState.getBoolean("hasDepartureActionMode")
|
||||||
&& mSelectedDeparture != null) {
|
&& mSelectedDeparture != null) {
|
||||||
startDepartureActionMode();
|
startDepartureActionMode();
|
||||||
}
|
}
|
||||||
|
if (savedInstanceState.getBoolean("hasYourTrainActionMode")
|
||||||
|
&& mSelectedDeparture != null) {
|
||||||
|
((Checkable) findViewById(R.id.yourTrainSection))
|
||||||
|
.setChecked(true);
|
||||||
|
startYourTrainActionMode(bartRunnerApplication);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setListAdapter(mDeparturesAdapter);
|
setListAdapter(mDeparturesAdapter);
|
||||||
final ListView listView = getListView();
|
final ListView listView = getListView();
|
||||||
listView.setEmptyView(findViewById(android.R.id.empty));
|
listView.setEmptyView(findViewById(android.R.id.empty));
|
||||||
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
|
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
|
||||||
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
listView.setOnItemClickListener(mListItemClickListener);
|
||||||
@Override
|
listView.setOnItemLongClickListener(mListItemLongClickListener);
|
||||||
public void onItemClick(AdapterView<?> adapterView, View view,
|
|
||||||
int position, long id) {
|
|
||||||
mSelectedDeparture = (Departure) getListAdapter().getItem(
|
|
||||||
position);
|
|
||||||
view.setSelected(true);
|
|
||||||
startDepartureActionMode();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
findViewById(R.id.missingDepartureText).setVisibility(View.VISIBLE);
|
findViewById(R.id.missingDepartureText).setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
findViewById(R.id.yourTrainSection).setOnClickListener(
|
||||||
|
mYourTrainSectionClickListener);
|
||||||
|
|
||||||
refreshBoardedDeparture();
|
refreshBoardedDeparture();
|
||||||
|
|
||||||
getSupportActionBar().setHomeButtonEnabled(true);
|
getSupportActionBar().setHomeButtonEnabled(true);
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
final BartRunnerApplication bartRunnerApplication = (BartRunnerApplication) getApplication();
|
|
||||||
if (bartRunnerApplication.shouldPlayAlarmRingtone()) {
|
if (bartRunnerApplication.shouldPlayAlarmRingtone()) {
|
||||||
soundTheAlarm();
|
soundTheAlarm();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bartRunnerApplication.isAlarmSounding()) {
|
if (bartRunnerApplication.isAlarmSounding()) {
|
||||||
Builder builder = new AlertDialog.Builder(this);
|
Builder builder = new AlertDialog.Builder(this);
|
||||||
builder.setMessage(R.string.train_alert_text)
|
builder.setMessage(R.string.train_alarm_text)
|
||||||
.setCancelable(false)
|
.setCancelable(false)
|
||||||
.setNeutralButton(R.string.silence_alarm,
|
.setNeutralButton(R.string.silence_alarm,
|
||||||
new DialogInterface.OnClickListener() {
|
new DialogInterface.OnClickListener() {
|
||||||
@ -217,19 +216,19 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements
|
|||||||
private void soundTheAlarm() {
|
private void soundTheAlarm() {
|
||||||
final BartRunnerApplication application = (BartRunnerApplication) getApplication();
|
final BartRunnerApplication application = (BartRunnerApplication) getApplication();
|
||||||
|
|
||||||
Uri alertSound = RingtoneManager
|
Uri alarmSound = RingtoneManager
|
||||||
.getDefaultUri(RingtoneManager.TYPE_ALARM);
|
.getDefaultUri(RingtoneManager.TYPE_ALARM);
|
||||||
|
|
||||||
if (alertSound == null || !tryToPlayRingtone(alertSound)) {
|
if (alarmSound == null || !tryToPlayRingtone(alarmSound)) {
|
||||||
alertSound = RingtoneManager
|
alarmSound = RingtoneManager
|
||||||
.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
||||||
if (alertSound == null || !tryToPlayRingtone(alertSound)) {
|
if (alarmSound == null || !tryToPlayRingtone(alarmSound)) {
|
||||||
alertSound = RingtoneManager
|
alarmSound = RingtoneManager
|
||||||
.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
|
.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (application.getAlarmMediaPlayer() == null) {
|
if (application.getAlarmMediaPlayer() == null) {
|
||||||
tryToPlayRingtone(alertSound);
|
tryToPlayRingtone(alarmSound);
|
||||||
}
|
}
|
||||||
mHandler.postDelayed(new Runnable() {
|
mHandler.postDelayed(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@ -296,7 +295,54 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private Observer<Boolean> mAlarmPendingObserver;
|
private boolean mWasLongClick = false;
|
||||||
|
|
||||||
|
private final AdapterView.OnItemClickListener mListItemClickListener = new AdapterView.OnItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> adapterView, View view,
|
||||||
|
int position, long id) {
|
||||||
|
if (mWasLongClick) {
|
||||||
|
mWasLongClick = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mActionMode != null) {
|
||||||
|
/*
|
||||||
|
* If action mode is displayed, cancel out of that
|
||||||
|
*/
|
||||||
|
mActionMode.finish();
|
||||||
|
getListView().clearChoices();
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Otherwise select the clicked departure as the one the user
|
||||||
|
* wants to board
|
||||||
|
*/
|
||||||
|
mSelectedDeparture = (Departure) getListAdapter().getItem(
|
||||||
|
position);
|
||||||
|
setBoardedDeparture(mSelectedDeparture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final AdapterView.OnItemLongClickListener mListItemLongClickListener = new AdapterView.OnItemLongClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onItemLongClick(AdapterView<?> adapterView, View view,
|
||||||
|
int position, long id) {
|
||||||
|
mWasLongClick = true;
|
||||||
|
mSelectedDeparture = (Departure) getListAdapter().getItem(position);
|
||||||
|
view.setSelected(true);
|
||||||
|
startDepartureActionMode();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final View.OnClickListener mYourTrainSectionClickListener = new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
((Checkable) v).setChecked(true);
|
||||||
|
startYourTrainActionMode((BartRunnerApplication) getApplication());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
protected DepartureArrayAdapter getListAdapter() {
|
protected DepartureArrayAdapter getListAdapter() {
|
||||||
return mDeparturesAdapter;
|
return mDeparturesAdapter;
|
||||||
@ -312,10 +358,6 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements
|
|||||||
super.onStop();
|
super.onStop();
|
||||||
if (mEtdService != null)
|
if (mEtdService != null)
|
||||||
mEtdService.unregisterListener(this);
|
mEtdService.unregisterListener(this);
|
||||||
if (mAlarmPendingObserver != null)
|
|
||||||
((BartRunnerApplication) getApplication())
|
|
||||||
.getAlarmPendingObservable().unregisterObserver(
|
|
||||||
mAlarmPendingObserver);
|
|
||||||
if (mBound)
|
if (mBound)
|
||||||
unbindService(mConnection);
|
unbindService(mConnection);
|
||||||
Ticker.getInstance().stopTicking(this);
|
Ticker.getInstance().stopTicking(this);
|
||||||
@ -337,7 +379,10 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements
|
|||||||
}
|
}
|
||||||
outState.putParcelableArray("departures", departures);
|
outState.putParcelableArray("departures", departures);
|
||||||
outState.putParcelable("selectedDeparture", mSelectedDeparture);
|
outState.putParcelable("selectedDeparture", mSelectedDeparture);
|
||||||
outState.putBoolean("hasActionMode", mActionMode != null);
|
outState.putBoolean("hasDepartureActionMode",
|
||||||
|
isDepartureActionModeActive());
|
||||||
|
outState.putBoolean("hasYourTrainActionMode",
|
||||||
|
isYourTrainActionModeActive());
|
||||||
outState.putString("origin", mOrigin.abbreviation);
|
outState.putString("origin", mOrigin.abbreviation);
|
||||||
outState.putString("destination", mDestination.abbreviation);
|
outState.putString("destination", mDestination.abbreviation);
|
||||||
}
|
}
|
||||||
@ -366,25 +411,6 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements
|
|||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
MenuInflater inflater = getSupportMenuInflater();
|
MenuInflater inflater = getSupportMenuInflater();
|
||||||
inflater.inflate(R.menu.route_menu, menu);
|
inflater.inflate(R.menu.route_menu, menu);
|
||||||
final MenuItem cancelAlarmButton = menu
|
|
||||||
.findItem(R.id.cancel_alarm_button);
|
|
||||||
final BartRunnerApplication application = (BartRunnerApplication) getApplication();
|
|
||||||
if (application.isAlarmPending()) {
|
|
||||||
cancelAlarmButton.setVisible(true);
|
|
||||||
}
|
|
||||||
mAlarmPendingObserver = new Observer<Boolean>() {
|
|
||||||
@Override
|
|
||||||
public void onUpdate(final Boolean newValue) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
cancelAlarmButton.setVisible(newValue);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
application.getAlarmPendingObservable().registerObserver(
|
|
||||||
mAlarmPendingObserver);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,11 +423,6 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements
|
|||||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
return true;
|
return true;
|
||||||
} else if (itemId == R.id.cancel_alarm_button) {
|
|
||||||
Intent intent = new Intent(this, NotificationService.class);
|
|
||||||
intent.putExtra("cancelNotifications", true);
|
|
||||||
startService(intent);
|
|
||||||
return true;
|
|
||||||
} else if (itemId == R.id.view_on_bart_site_button) {
|
} else if (itemId == R.id.view_on_bart_site_button) {
|
||||||
startActivity(new Intent(
|
startActivity(new Intent(
|
||||||
Intent.ACTION_VIEW,
|
Intent.ACTION_VIEW,
|
||||||
@ -424,7 +445,7 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements
|
|||||||
private void refreshBoardedDeparture() {
|
private void refreshBoardedDeparture() {
|
||||||
final Departure boardedDeparture = ((BartRunnerApplication) getApplication())
|
final Departure boardedDeparture = ((BartRunnerApplication) getApplication())
|
||||||
.getBoardedDeparture();
|
.getBoardedDeparture();
|
||||||
final View yourTrainSection = findViewById(R.id.yourTrainSection);
|
final YourTrainLayout yourTrainSection = (YourTrainLayout) findViewById(R.id.yourTrainSection);
|
||||||
int currentVisibility = yourTrainSection.getVisibility();
|
int currentVisibility = yourTrainSection.getVisibility();
|
||||||
|
|
||||||
final boolean boardedDepartureDoesNotApply = boardedDeparture == null
|
final boolean boardedDepartureDoesNotApply = boardedDeparture == null
|
||||||
@ -433,65 +454,43 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements
|
|||||||
|
|
||||||
if (boardedDepartureDoesNotApply) {
|
if (boardedDepartureDoesNotApply) {
|
||||||
if (currentVisibility != View.GONE) {
|
if (currentVisibility != View.GONE) {
|
||||||
yourTrainSection.setVisibility(View.GONE);
|
hideYourTrainSection();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
((TextView) findViewById(R.id.yourTrainDestinationText))
|
yourTrainSection.updateFromDeparture(boardedDeparture);
|
||||||
.setText(boardedDeparture.getTrainDestination().toString());
|
|
||||||
|
|
||||||
((TextView) findViewById(R.id.yourTrainTrainLengthText))
|
|
||||||
.setText(boardedDeparture.getTrainLengthText());
|
|
||||||
|
|
||||||
ImageView colorBar = (ImageView) findViewById(R.id.yourTrainDestinationColorBar);
|
|
||||||
((GradientDrawable) colorBar.getDrawable()).setColor(Color
|
|
||||||
.parseColor(boardedDeparture.getTrainDestinationColor()));
|
|
||||||
if (boardedDeparture.isBikeAllowed()) {
|
|
||||||
((ImageView) findViewById(R.id.yourTrainBikeIcon))
|
|
||||||
.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
((ImageView) findViewById(R.id.yourTrainBikeIcon))
|
|
||||||
.setVisibility(View.INVISIBLE);
|
|
||||||
}
|
|
||||||
if (boardedDeparture.getRequiresTransfer()) {
|
|
||||||
((ImageView) findViewById(R.id.yourTrainXferIcon))
|
|
||||||
.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
((ImageView) findViewById(R.id.yourTrainXferIcon))
|
|
||||||
.setVisibility(View.INVISIBLE);
|
|
||||||
}
|
|
||||||
CountdownTextView departureCountdown = (CountdownTextView) findViewById(R.id.yourTrainDepartureCountdown);
|
|
||||||
CountdownTextView arrivalCountdown = (CountdownTextView) findViewById(R.id.yourTrainArrivalCountdown);
|
|
||||||
|
|
||||||
departureCountdown.setText("Leaves in "
|
|
||||||
+ boardedDeparture.getCountdownText() + " "
|
|
||||||
+ boardedDeparture.getUncertaintyText());
|
|
||||||
departureCountdown.setTextProvider(new TextProvider() {
|
|
||||||
@Override
|
|
||||||
public String getText(long tickNumber) {
|
|
||||||
if (boardedDeparture.hasDeparted()) {
|
|
||||||
return "Departed";
|
|
||||||
} else {
|
|
||||||
return "Leaves in " + boardedDeparture.getCountdownText()
|
|
||||||
+ " " + boardedDeparture.getUncertaintyText();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
arrivalCountdown.setText(boardedDeparture
|
|
||||||
.getEstimatedArrivalMinutesLeftText(this));
|
|
||||||
arrivalCountdown.setTextProvider(new TextProvider() {
|
|
||||||
@Override
|
|
||||||
public String getText(long tickNumber) {
|
|
||||||
return boardedDeparture
|
|
||||||
.getEstimatedArrivalMinutesLeftText(ViewDeparturesActivity.this);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (currentVisibility != View.VISIBLE) {
|
if (currentVisibility != View.VISIBLE) {
|
||||||
yourTrainSection.setVisibility(View.VISIBLE);
|
showYourTrainSection(yourTrainSection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mActionMode == null) {
|
||||||
|
for (int i = getListAdapter().getCount() - 1; i >= 0; i--) {
|
||||||
|
if (getListAdapter().getItem(i).equals(boardedDeparture)) {
|
||||||
|
getListView().setSelection(i);
|
||||||
|
final Checkable listItem = (Checkable) getListView()
|
||||||
|
.getChildAt(i);
|
||||||
|
if (listItem != null) {
|
||||||
|
listItem.setChecked(true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getListView().requestLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setBoardedDeparture(Departure selectedDeparture) {
|
||||||
|
final BartRunnerApplication application = (BartRunnerApplication) getApplication();
|
||||||
|
selectedDeparture.setPassengerDestination(mDestination);
|
||||||
|
application.setBoardedDeparture(selectedDeparture);
|
||||||
|
refreshBoardedDeparture();
|
||||||
|
|
||||||
|
// Start the notification service
|
||||||
|
startService(new Intent(ViewDeparturesActivity.this,
|
||||||
|
BoardedDepartureService.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startDepartureActionMode() {
|
private void startDepartureActionMode() {
|
||||||
@ -511,26 +510,14 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||||
|
((Checkable) findViewById(R.id.yourTrainSection)).setChecked(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||||
if (item.getItemId() == R.id.boardTrain) {
|
if (item.getItemId() == R.id.boardTrain) {
|
||||||
final BartRunnerApplication application = (BartRunnerApplication) getApplication();
|
setBoardedDeparture(mSelectedDeparture);
|
||||||
mSelectedDeparture.setPassengerDestination(mDestination);
|
|
||||||
application.setBoardedDeparture(mSelectedDeparture);
|
|
||||||
refreshBoardedDeparture();
|
|
||||||
|
|
||||||
// Start the notification service
|
|
||||||
startService(new Intent(ViewDeparturesActivity.this,
|
|
||||||
NotificationService.class));
|
|
||||||
|
|
||||||
// Don't prompt for alert if train is about to leave
|
|
||||||
if (mSelectedDeparture.getMeanSecondsLeft() / 60 > 1) {
|
|
||||||
new TrainAlertDialogFragment().show(
|
|
||||||
getSupportFragmentManager(), "dialog");
|
|
||||||
}
|
|
||||||
|
|
||||||
mode.finish();
|
mode.finish();
|
||||||
return true;
|
return true;
|
||||||
@ -547,6 +534,158 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void startYourTrainActionMode(BartRunnerApplication application) {
|
||||||
|
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();
|
||||||
|
mActionMode.setSubtitle(getAlarmSubtitle(leadTime));
|
||||||
|
} else {
|
||||||
|
mActionMode.setSubtitle(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getAlarmSubtitle(int leadTime) {
|
||||||
|
if (leadTime == 0)
|
||||||
|
return null;
|
||||||
|
return "Alarm " + leadTime + " minute" + (leadTime != 1 ? "s" : "")
|
||||||
|
+ " before departure";
|
||||||
|
}
|
||||||
|
|
||||||
|
private class YourTrainActionMode implements ActionMode.Callback {
|
||||||
|
private Observer<Boolean> mAlarmPendingObserver;
|
||||||
|
private Observer<Integer> mAlarmLeadTimeObserver;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||||
|
// Suppress new "your train" selections
|
||||||
|
getListView().setChoiceMode(ListView.CHOICE_MODE_NONE);
|
||||||
|
|
||||||
|
mode.getMenuInflater()
|
||||||
|
.inflate(R.menu.your_train_context_menu, menu);
|
||||||
|
final MenuItem cancelAlarmButton = menu
|
||||||
|
.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();
|
||||||
|
if (boardedDeparture.isAlarmPending()) {
|
||||||
|
cancelAlarmButton.setVisible(true);
|
||||||
|
setAlarmButton.setIcon(R.drawable.ic_action_alarm);
|
||||||
|
} else if (boardedDeparture.getMeanSecondsLeft() > 60) {
|
||||||
|
setAlarmButton.setIcon(R.drawable.ic_action_add_alarm);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't allow alarm setting if train is about to leave
|
||||||
|
if (boardedDeparture.getMeanSecondsLeft() / 60 < 1) {
|
||||||
|
menu.findItem(R.id.set_alarm_button).setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
mAlarmPendingObserver = new Observer<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public void onUpdate(final Boolean newValue) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
cancelAlarmButton.setVisible(newValue);
|
||||||
|
if (newValue) {
|
||||||
|
mActionMode
|
||||||
|
.setSubtitle(getAlarmSubtitle(boardedDeparture
|
||||||
|
.getAlarmLeadTimeMinutes()));
|
||||||
|
setAlarmButton
|
||||||
|
.setIcon(R.drawable.ic_action_alarm);
|
||||||
|
} else {
|
||||||
|
mActionMode.setSubtitle(null);
|
||||||
|
setAlarmButton
|
||||||
|
.setIcon(R.drawable.ic_action_add_alarm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mAlarmLeadTimeObserver = new Observer<Integer>() {
|
||||||
|
@Override
|
||||||
|
public void onUpdate(final Integer newValue) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
mActionMode.setSubtitle(getAlarmSubtitle(newValue));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
boardedDeparture.getAlarmPendingObservable().registerObserver(
|
||||||
|
mAlarmPendingObserver);
|
||||||
|
boardedDeparture.getAlarmLeadTimeMinutesObservable()
|
||||||
|
.registerObserver(mAlarmLeadTimeObserver);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||||
|
getListView().clearChoices();
|
||||||
|
getListView().requestLayout();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
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) {
|
||||||
|
new TrainAlarmDialogFragment().show(
|
||||||
|
getSupportFragmentManager(), "dialog");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else if (itemId == R.id.cancel_alarm_button) {
|
||||||
|
Intent intent = new Intent(ViewDeparturesActivity.this,
|
||||||
|
BoardedDepartureService.class);
|
||||||
|
intent.putExtra("cancelNotifications", true);
|
||||||
|
startService(intent);
|
||||||
|
return true;
|
||||||
|
} else if (itemId == R.id.delete) {
|
||||||
|
Intent intent = new Intent(ViewDeparturesActivity.this,
|
||||||
|
BoardedDepartureService.class);
|
||||||
|
intent.putExtra("clearBoardedDeparture", true);
|
||||||
|
startService(intent);
|
||||||
|
hideYourTrainSection();
|
||||||
|
mode.finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyActionMode(ActionMode mode) {
|
||||||
|
((Checkable) findViewById(R.id.yourTrainSection)).setChecked(false);
|
||||||
|
|
||||||
|
final BartRunnerApplication application = (BartRunnerApplication) getApplication();
|
||||||
|
final Departure boardedDeparture = application
|
||||||
|
.getBoardedDeparture();
|
||||||
|
if (boardedDeparture != null) {
|
||||||
|
boardedDeparture.getAlarmPendingObservable()
|
||||||
|
.unregisterObserver(mAlarmPendingObserver);
|
||||||
|
boardedDeparture.getAlarmLeadTimeMinutesObservable()
|
||||||
|
.unregisterObserver(mAlarmLeadTimeObserver);
|
||||||
|
}
|
||||||
|
|
||||||
|
mAlarmPendingObserver = null;
|
||||||
|
mAlarmLeadTimeObserver = null;
|
||||||
|
mActionMode = null;
|
||||||
|
|
||||||
|
// Enable new "your train" selections
|
||||||
|
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onETDChanged(final List<Departure> departures) {
|
public void onETDChanged(final List<Departure> departures) {
|
||||||
runOnUiThread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
@ -558,6 +697,10 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements
|
|||||||
mProgress.setVisibility(View.INVISIBLE);
|
mProgress.setVisibility(View.INVISIBLE);
|
||||||
Linkify.addLinks(textView, Linkify.WEB_URLS);
|
Linkify.addLinks(textView, Linkify.WEB_URLS);
|
||||||
} else {
|
} else {
|
||||||
|
// TODO: Figure out why Ticker occasionally stops
|
||||||
|
Ticker.getInstance().startTicking(
|
||||||
|
ViewDeparturesActivity.this);
|
||||||
|
|
||||||
// Merge lists
|
// Merge lists
|
||||||
if (mDeparturesAdapter.getCount() > 0) {
|
if (mDeparturesAdapter.getCount() > 0) {
|
||||||
int adapterIndex = -1;
|
int adapterIndex = -1;
|
||||||
@ -639,4 +782,24 @@ public class ViewDeparturesActivity extends SherlockFragmentActivity implements
|
|||||||
return null;
|
return null;
|
||||||
return new StationPair(mOrigin, mDestination);
|
return new StationPair(mOrigin, mDestination);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void hideYourTrainSection() {
|
||||||
|
findViewById(R.id.yourTrainSection).setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showYourTrainSection(final YourTrainLayout yourTrainSection) {
|
||||||
|
yourTrainSection.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isYourTrainActionModeActive() {
|
||||||
|
return mActionMode != null
|
||||||
|
&& mActionMode.getTitle()
|
||||||
|
.equals(getString(R.string.your_train));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isDepartureActionModeActive() {
|
||||||
|
return mActionMode != null
|
||||||
|
&& !mActionMode.getTitle().equals(
|
||||||
|
getString(R.string.your_train));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package com.dougkeen.bart.controls;
|
package com.dougkeen.bart.controls;
|
||||||
|
|
||||||
import com.dougkeen.bart.R;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.widget.Checkable;
|
import android.widget.Checkable;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
|
|
||||||
|
import com.dougkeen.bart.R;
|
||||||
|
|
||||||
public class DepartureListItemLayout extends RelativeLayout implements
|
public class DepartureListItemLayout extends RelativeLayout implements
|
||||||
Checkable {
|
Checkable {
|
||||||
|
|
||||||
|
@ -2,12 +2,12 @@ package com.dougkeen.bart.controls;
|
|||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import com.dougkeen.bart.model.TextProvider;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.widget.TextSwitcher;
|
import android.widget.TextSwitcher;
|
||||||
|
|
||||||
|
import com.dougkeen.bart.model.TextProvider;
|
||||||
|
|
||||||
public class TimedTextSwitcher extends TextSwitcher implements
|
public class TimedTextSwitcher extends TextSwitcher implements
|
||||||
Ticker.TickSubscriber {
|
Ticker.TickSubscriber {
|
||||||
|
|
||||||
|
120
src/com/dougkeen/bart/controls/YourTrainLayout.java
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
package com.dougkeen.bart.controls;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.GradientDrawable;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Checkable;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.RelativeLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.dougkeen.bart.R;
|
||||||
|
import com.dougkeen.bart.model.Departure;
|
||||||
|
import com.dougkeen.bart.model.TextProvider;
|
||||||
|
|
||||||
|
public class YourTrainLayout extends RelativeLayout implements Checkable {
|
||||||
|
|
||||||
|
public YourTrainLayout(Context context) {
|
||||||
|
super(context);
|
||||||
|
assignLayout(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public YourTrainLayout(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
assignLayout(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public YourTrainLayout(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
assignLayout(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assignLayout(Context context) {
|
||||||
|
LayoutInflater.from(context).inflate(R.layout.your_train, this, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean mChecked;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isChecked() {
|
||||||
|
return mChecked;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setChecked(boolean checked) {
|
||||||
|
mChecked = checked;
|
||||||
|
setBackground();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setBackground() {
|
||||||
|
if (isChecked()) {
|
||||||
|
setBackgroundDrawable(getContext().getResources().getDrawable(
|
||||||
|
R.color.blue_selection));
|
||||||
|
} else {
|
||||||
|
setBackgroundDrawable(getContext().getResources().getDrawable(
|
||||||
|
R.color.gray));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void toggle() {
|
||||||
|
setChecked(!isChecked());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateFromDeparture(final Departure boardedDeparture) {
|
||||||
|
((TextView) findViewById(R.id.yourTrainDestinationText))
|
||||||
|
.setText(boardedDeparture.getTrainDestination().toString());
|
||||||
|
|
||||||
|
((TextView) findViewById(R.id.yourTrainTrainLengthText))
|
||||||
|
.setText(boardedDeparture.getTrainLengthText());
|
||||||
|
|
||||||
|
ImageView colorBar = (ImageView) findViewById(R.id.yourTrainDestinationColorBar);
|
||||||
|
((GradientDrawable) colorBar.getDrawable()).setColor(Color
|
||||||
|
.parseColor(boardedDeparture.getTrainDestinationColor()));
|
||||||
|
if (boardedDeparture.isBikeAllowed()) {
|
||||||
|
((ImageView) findViewById(R.id.yourTrainBikeIcon))
|
||||||
|
.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
((ImageView) findViewById(R.id.yourTrainBikeIcon))
|
||||||
|
.setVisibility(View.INVISIBLE);
|
||||||
|
}
|
||||||
|
if (boardedDeparture.getRequiresTransfer()) {
|
||||||
|
((ImageView) findViewById(R.id.yourTrainXferIcon))
|
||||||
|
.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
((ImageView) findViewById(R.id.yourTrainXferIcon))
|
||||||
|
.setVisibility(View.INVISIBLE);
|
||||||
|
}
|
||||||
|
CountdownTextView departureCountdown = (CountdownTextView) findViewById(R.id.yourTrainDepartureCountdown);
|
||||||
|
CountdownTextView arrivalCountdown = (CountdownTextView) findViewById(R.id.yourTrainArrivalCountdown);
|
||||||
|
|
||||||
|
final TextProvider textProvider = new TextProvider() {
|
||||||
|
@Override
|
||||||
|
public String getText(long tickNumber) {
|
||||||
|
if (boardedDeparture.hasDeparted()) {
|
||||||
|
return "Departed";
|
||||||
|
} else {
|
||||||
|
return "Leaves in " + boardedDeparture.getCountdownText()
|
||||||
|
+ " " + boardedDeparture.getUncertaintyText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
departureCountdown.setText(textProvider.getText(0));
|
||||||
|
departureCountdown.setTextProvider(textProvider);
|
||||||
|
|
||||||
|
arrivalCountdown.setText(boardedDeparture
|
||||||
|
.getEstimatedArrivalMinutesLeftText(getContext()));
|
||||||
|
arrivalCountdown.setTextProvider(new TextProvider() {
|
||||||
|
@Override
|
||||||
|
public String getText(long tickNumber) {
|
||||||
|
return boardedDeparture
|
||||||
|
.getEstimatedArrivalMinutesLeftText(getContext());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setBackground();
|
||||||
|
}
|
||||||
|
}
|
@ -13,8 +13,6 @@ import android.view.ViewGroup;
|
|||||||
import android.view.animation.AnimationUtils;
|
import android.view.animation.AnimationUtils;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.ListView;
|
|
||||||
import android.widget.RelativeLayout;
|
|
||||||
import android.widget.TextSwitcher;
|
import android.widget.TextSwitcher;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.ViewSwitcher.ViewFactory;
|
import android.widget.ViewSwitcher.ViewFactory;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.dougkeen.bart.data;
|
package com.dougkeen.bart.data;
|
||||||
|
|
||||||
|
|
||||||
public enum RoutesColumns {
|
public enum RoutesColumns {
|
||||||
_ID("_id", "INTEGER", false),
|
_ID("_id", "INTEGER", false),
|
||||||
FROM_STATION("FROM_STATION", "TEXT", false),
|
FROM_STATION("FROM_STATION", "TEXT", false),
|
||||||
|
@ -18,10 +18,12 @@ import android.text.format.DateFormat;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.dougkeen.bart.R;
|
import com.dougkeen.bart.R;
|
||||||
import com.dougkeen.bart.services.NotificationService;
|
import com.dougkeen.bart.services.BoardedDepartureService;
|
||||||
|
import com.dougkeen.util.Observable;
|
||||||
|
|
||||||
public class Departure implements Parcelable, Comparable<Departure> {
|
public class Departure implements Parcelable, Comparable<Departure> {
|
||||||
private static final int MINIMUM_MERGE_OVERLAP_MILLIS = 10000;
|
private static final int MINIMUM_MERGE_OVERLAP_MILLIS = 5000;
|
||||||
|
private static final int EXPIRE_MINUTES_AFTER_ARRIVAL = 1;
|
||||||
|
|
||||||
public Departure() {
|
public Departure() {
|
||||||
super();
|
super();
|
||||||
@ -67,8 +69,9 @@ public class Departure implements Parcelable, Comparable<Departure> {
|
|||||||
|
|
||||||
private long arrivalTimeOverride;
|
private long arrivalTimeOverride;
|
||||||
|
|
||||||
private int alarmLeadTimeMinutes;
|
private Observable<Integer> alarmLeadTimeMinutes = new Observable<Integer>(
|
||||||
private boolean alarmPending;
|
0);
|
||||||
|
private Observable<Boolean> alarmPending = new Observable<Boolean>(false);
|
||||||
|
|
||||||
public Station getOrigin() {
|
public Station getOrigin() {
|
||||||
return origin;
|
return origin;
|
||||||
@ -308,7 +311,7 @@ public class Departure implements Parcelable, Comparable<Departure> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasDeparted() {
|
public boolean hasDeparted() {
|
||||||
return getMeanSecondsLeft() < 0;
|
return getMeanSecondsLeft() <= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void calculateEstimates(long originalEstimateTime) {
|
public void calculateEstimates(long originalEstimateTime) {
|
||||||
@ -333,40 +336,45 @@ public class Departure implements Parcelable, Comparable<Departure> {
|
|||||||
setEstimatedTripTime(departure.getEstimatedTripTime());
|
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
|
if ((getMaxEstimate() - departure.getMinEstimate()) < MINIMUM_MERGE_OVERLAP_MILLIS
|
||||||
|| departure.getMaxEstimate() - getMinEstimate() < MINIMUM_MERGE_OVERLAP_MILLIS) {
|
|| departure.getMaxEstimate() - getMinEstimate() < MINIMUM_MERGE_OVERLAP_MILLIS) {
|
||||||
/*
|
/*
|
||||||
* The estimate must have changed... just use the latest incoming
|
* The estimate must have changed... just use the latest incoming
|
||||||
* values
|
* values
|
||||||
*/
|
*/
|
||||||
setMinEstimate(departure.getMinEstimate());
|
newMin = departure.getMinEstimate();
|
||||||
setMaxEstimate(departure.getMaxEstimate());
|
newMax = departure.getMaxEstimate();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final long newMin = Math.max(getMinEstimate(),
|
|
||||||
departure.getMinEstimate());
|
|
||||||
final long newMax = Math.min(getMaxEstimate(),
|
|
||||||
departure.getMaxEstimate());
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the new departure would mark this as departed, and we have < 1
|
* If the new departure would mark this as departed, and we have < 60
|
||||||
* minute left on a fairly accurate local estimate, ignore the incoming
|
* seconds left on a fairly accurate local estimate, ignore the incoming
|
||||||
* departure
|
* departure
|
||||||
*/
|
*/
|
||||||
if (!wasDeparted && getMeanSecondsLeft(newMin, newMax) < 0
|
if (!wasDeparted && getMeanSecondsLeft(newMin, newMax) <= 0
|
||||||
&& getMeanSecondsLeft() < 60 && getUncertaintySeconds() < 30) {
|
&& getMeanSecondsLeft() < 60 && getUncertaintySeconds() < 30) {
|
||||||
Log.d(Constants.TAG,
|
Log.d(Constants.TAG,
|
||||||
"Skipping estimate merge, since it would make this departure show as 'departed' prematurely");
|
"Skipping estimate merge, since it would make this departure show as 'departed' prematurely");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newMax > newMin) { // We can never have 0 or negative uncertainty
|
if (newMax > newMin) {
|
||||||
|
// We must never have 0 or negative uncertainty
|
||||||
setMinEstimate(newMin);
|
setMinEstimate(newMin);
|
||||||
setMaxEstimate(newMax);
|
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) {
|
public int compareTo(Departure another) {
|
||||||
return (this.getMeanSecondsLeft() > another.getMeanSecondsLeft()) ? 1
|
return (this.getMeanSecondsLeft() > another.getMeanSecondsLeft()) ? 1
|
||||||
: ((this.getMeanSecondsLeft() == another.getMeanSecondsLeft()) ? 0
|
: ((this.getMeanSecondsLeft() == another.getMeanSecondsLeft()) ? 0
|
||||||
@ -475,10 +483,18 @@ public class Departure implements Parcelable, Comparable<Departure> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getAlarmLeadTimeMinutes() {
|
public int getAlarmLeadTimeMinutes() {
|
||||||
|
return alarmLeadTimeMinutes.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Observable<Integer> getAlarmLeadTimeMinutesObservable() {
|
||||||
return alarmLeadTimeMinutes;
|
return alarmLeadTimeMinutes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAlarmPending() {
|
public boolean isAlarmPending() {
|
||||||
|
return alarmPending.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Observable<Boolean> getAlarmPendingObservable() {
|
||||||
return alarmPending;
|
return alarmPending;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -489,7 +505,7 @@ public class Departure implements Parcelable, Comparable<Departure> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private long getAlarmClockTime() {
|
private long getAlarmClockTime() {
|
||||||
return getMeanEstimate() - alarmLeadTimeMinutes * 60 * 1000;
|
return getMeanEstimate() - alarmLeadTimeMinutes.getValue() * 60 * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSecondsUntilAlarm() {
|
public int getSecondsUntilAlarm() {
|
||||||
@ -497,8 +513,8 @@ public class Departure implements Parcelable, Comparable<Departure> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setUpAlarm(int leadTimeMinutes) {
|
public void setUpAlarm(int leadTimeMinutes) {
|
||||||
this.alarmLeadTimeMinutes = leadTimeMinutes;
|
this.alarmLeadTimeMinutes.setValue(leadTimeMinutes);
|
||||||
this.alarmPending = true;
|
this.alarmPending.setValue(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateAlarm(Context context, AlarmManager alarmManager) {
|
public void updateAlarm(Context context, AlarmManager alarmManager) {
|
||||||
@ -509,20 +525,20 @@ public class Departure implements Parcelable, Comparable<Departure> {
|
|||||||
final PendingIntent alarmIntent = getAlarmIntent(context);
|
final PendingIntent alarmIntent = getAlarmIntent(context);
|
||||||
alarmManager.cancel(alarmIntent);
|
alarmManager.cancel(alarmIntent);
|
||||||
|
|
||||||
long alertTime = getAlarmClockTime();
|
long alarmTime = getAlarmClockTime();
|
||||||
|
|
||||||
alarmManager.set(AlarmManager.RTC_WAKEUP, alertTime, alarmIntent);
|
alarmManager.set(AlarmManager.RTC_WAKEUP, alarmTime, alarmIntent);
|
||||||
|
|
||||||
if (Log.isLoggable(Constants.TAG, Log.VERBOSE))
|
if (Log.isLoggable(Constants.TAG, Log.VERBOSE))
|
||||||
Log.v(Constants.TAG,
|
Log.v(Constants.TAG,
|
||||||
"Scheduling alarm for "
|
"Scheduling alarm for "
|
||||||
+ DateFormatUtils.format(alertTime, "h:mm:ss"));
|
+ DateFormatUtils.format(alarmTime, "h:mm:ss"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cancelAlarm(Context context, AlarmManager alarmManager) {
|
public void cancelAlarm(Context context, AlarmManager alarmManager) {
|
||||||
alarmManager.cancel(getAlarmIntent(context));
|
alarmManager.cancel(getAlarmIntent(context));
|
||||||
this.alarmPending = false;
|
this.alarmPending.setValue(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PendingIntent notificationIntent;
|
private PendingIntent notificationIntent;
|
||||||
@ -546,7 +562,7 @@ public class Departure implements Parcelable, Comparable<Departure> {
|
|||||||
: ""));
|
: ""));
|
||||||
|
|
||||||
final Intent cancelAlarmIntent = new Intent(context,
|
final Intent cancelAlarmIntent = new Intent(context,
|
||||||
NotificationService.class);
|
BoardedDepartureService.class);
|
||||||
cancelAlarmIntent.putExtra("cancelNotifications", true);
|
cancelAlarmIntent.putExtra("cancelNotifications", true);
|
||||||
Builder notificationBuilder = new NotificationCompat.Builder(context)
|
Builder notificationBuilder = new NotificationCompat.Builder(context)
|
||||||
.setOngoing(true)
|
.setOngoing(true)
|
||||||
@ -564,7 +580,7 @@ public class Departure implements Parcelable, Comparable<Departure> {
|
|||||||
"Cancel alarm",
|
"Cancel alarm",
|
||||||
PendingIntent.getService(context, 0, cancelAlarmIntent,
|
PendingIntent.getService(context, 0, cancelAlarmIntent,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT)).setSubText(
|
PendingIntent.FLAG_UPDATE_CURRENT)).setSubText(
|
||||||
"Alert " + getAlarmLeadTimeMinutes()
|
"Alarm " + getAlarmLeadTimeMinutes()
|
||||||
+ " minutes before departure");
|
+ " minutes before departure");
|
||||||
}
|
}
|
||||||
} else if (isAlarmPending()) {
|
} else if (isAlarmPending()) {
|
||||||
@ -655,6 +671,6 @@ public class Departure implements Parcelable, Comparable<Departure> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public void notifyAlarmHasBeenHandled() {
|
public void notifyAlarmHasBeenHandled() {
|
||||||
this.alarmPending = false;
|
this.alarmPending.setValue(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,14 +2,14 @@ package com.dougkeen.bart.model;
|
|||||||
|
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
|
|
||||||
import com.dougkeen.bart.data.CursorUtils;
|
|
||||||
import com.dougkeen.bart.data.RoutesColumns;
|
|
||||||
|
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import com.dougkeen.bart.data.CursorUtils;
|
||||||
|
import com.dougkeen.bart.data.RoutesColumns;
|
||||||
|
|
||||||
public class StationPair implements Parcelable {
|
public class StationPair implements Parcelable {
|
||||||
public StationPair(Station origin, Station destination) {
|
public StationPair(Station origin, Station destination) {
|
||||||
super();
|
super();
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.dougkeen.bart.model;
|
package com.dougkeen.bart.model;
|
||||||
|
|
||||||
|
|
||||||
public interface TextProvider {
|
public interface TextProvider {
|
||||||
|
|
||||||
String getText(long tickNumber);
|
String getText(long tickNumber);
|
||||||
|
@ -12,13 +12,13 @@ import org.apache.http.client.methods.HttpGet;
|
|||||||
import org.apache.http.client.methods.HttpUriRequest;
|
import org.apache.http.client.methods.HttpUriRequest;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
import com.dougkeen.bart.model.Constants;
|
|
||||||
import com.dougkeen.bart.model.Station;
|
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Xml;
|
import android.util.Xml;
|
||||||
|
|
||||||
|
import com.dougkeen.bart.model.Constants;
|
||||||
|
import com.dougkeen.bart.model.Station;
|
||||||
|
|
||||||
public abstract class GetRouteFareTask extends
|
public abstract class GetRouteFareTask extends
|
||||||
AsyncTask<GetRouteFareTask.Params, Integer, String> {
|
AsyncTask<GetRouteFareTask.Params, Integer, String> {
|
||||||
|
|
||||||
|
@ -1,38 +1,30 @@
|
|||||||
package com.dougkeen.bart.services;
|
package com.dougkeen.bart.services;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
|
||||||
|
|
||||||
import android.app.AlarmManager;
|
import android.app.AlarmManager;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.ServiceConnection;
|
import android.content.ServiceConnection;
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.Parcelable;
|
|
||||||
import android.os.SystemClock;
|
|
||||||
import android.support.v4.app.NotificationCompat;
|
|
||||||
import android.support.v4.app.NotificationCompat.Builder;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.dougkeen.bart.BartRunnerApplication;
|
import com.dougkeen.bart.BartRunnerApplication;
|
||||||
import com.dougkeen.bart.R;
|
|
||||||
import com.dougkeen.bart.model.Constants;
|
|
||||||
import com.dougkeen.bart.model.Departure;
|
import com.dougkeen.bart.model.Departure;
|
||||||
import com.dougkeen.bart.model.StationPair;
|
import com.dougkeen.bart.model.StationPair;
|
||||||
import com.dougkeen.bart.services.EtdService.EtdServiceBinder;
|
import com.dougkeen.bart.services.EtdService.EtdServiceBinder;
|
||||||
import com.dougkeen.bart.services.EtdService.EtdServiceListener;
|
import com.dougkeen.bart.services.EtdService.EtdServiceListener;
|
||||||
|
import com.dougkeen.util.Observer;
|
||||||
|
|
||||||
public class NotificationService extends Service implements EtdServiceListener {
|
public class BoardedDepartureService extends Service implements
|
||||||
|
EtdServiceListener {
|
||||||
|
|
||||||
private static final int DEPARTURE_NOTIFICATION_ID = 123;
|
private static final int DEPARTURE_NOTIFICATION_ID = 123;
|
||||||
|
|
||||||
@ -44,22 +36,29 @@ public class NotificationService extends Service implements EtdServiceListener {
|
|||||||
private StationPair mStationPair;
|
private StationPair mStationPair;
|
||||||
private NotificationManager mNotificationManager;
|
private NotificationManager mNotificationManager;
|
||||||
private AlarmManager mAlarmManager;
|
private AlarmManager mAlarmManager;
|
||||||
private PendingIntent mNotificationIntent;
|
|
||||||
private Handler mHandler;
|
private Handler mHandler;
|
||||||
private boolean mHasShutDown = false;
|
private boolean mHasShutDown = false;
|
||||||
|
|
||||||
public NotificationService() {
|
public BoardedDepartureService() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class ServiceHandler extends Handler {
|
private static final class ServiceHandler extends Handler {
|
||||||
public ServiceHandler(Looper looper) {
|
private final WeakReference<BoardedDepartureService> mServiceRef;
|
||||||
|
|
||||||
|
public ServiceHandler(Looper looper,
|
||||||
|
BoardedDepartureService boardedDepartureService) {
|
||||||
super(looper);
|
super(looper);
|
||||||
|
mServiceRef = new WeakReference<BoardedDepartureService>(
|
||||||
|
boardedDepartureService);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleMessage(Message msg) {
|
public void handleMessage(Message msg) {
|
||||||
onHandleIntent((Intent) msg.obj);
|
BoardedDepartureService service = mServiceRef.get();
|
||||||
|
if (service != null) {
|
||||||
|
service.onHandleIntent((Intent) msg.obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +73,8 @@ public class NotificationService extends Service implements EtdServiceListener {
|
|||||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
mEtdService = ((EtdServiceBinder) service).getService();
|
mEtdService = ((EtdServiceBinder) service).getService();
|
||||||
if (getStationPair() != null) {
|
if (getStationPair() != null) {
|
||||||
mEtdService.registerListener(NotificationService.this, false);
|
mEtdService.registerListener(BoardedDepartureService.this,
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
mBound = true;
|
mBound = true;
|
||||||
}
|
}
|
||||||
@ -87,7 +87,7 @@ public class NotificationService extends Service implements EtdServiceListener {
|
|||||||
thread.start();
|
thread.start();
|
||||||
|
|
||||||
mServiceLooper = thread.getLooper();
|
mServiceLooper = thread.getLooper();
|
||||||
mServiceHandler = new ServiceHandler(mServiceLooper);
|
mServiceHandler = new ServiceHandler(mServiceLooper, this);
|
||||||
|
|
||||||
bindService(new Intent(this, EtdService.class), mConnection,
|
bindService(new Intent(this, EtdService.class), mConnection,
|
||||||
Context.BIND_AUTO_CREATE);
|
Context.BIND_AUTO_CREATE);
|
||||||
@ -122,17 +122,21 @@ public class NotificationService extends Service implements EtdServiceListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void onHandleIntent(Intent intent) {
|
protected void onHandleIntent(Intent intent) {
|
||||||
final Departure boardedDeparture = ((BartRunnerApplication) getApplication())
|
final BartRunnerApplication application = (BartRunnerApplication) getApplication();
|
||||||
.getBoardedDeparture();
|
final Departure boardedDeparture = application.getBoardedDeparture();
|
||||||
if (boardedDeparture == null) {
|
if (boardedDeparture == null || intent == null) {
|
||||||
// Nothing to notify about
|
// Nothing to notify about
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (intent.getBooleanExtra("cancelNotifications", false)) {
|
if (intent.getBooleanExtra("cancelNotifications", false)
|
||||||
// We want to cancel the alarm/notification
|
|| intent.getBooleanExtra("clearBoardedDeparture", false)) {
|
||||||
|
// We want to cancel the alarm
|
||||||
boardedDeparture
|
boardedDeparture
|
||||||
.cancelAlarm(getApplicationContext(), mAlarmManager);
|
.cancelAlarm(getApplicationContext(), mAlarmManager);
|
||||||
shutDown(false);
|
if (intent.getBooleanExtra("clearBoardedDeparture", false)) {
|
||||||
|
application.setBoardedDeparture(null);
|
||||||
|
shutDown(false);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,11 +152,20 @@ public class NotificationService extends Service implements EtdServiceListener {
|
|||||||
mEtdService.registerListener(this, false);
|
mEtdService.registerListener(this, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Intent targetIntent = new Intent(Intent.ACTION_VIEW,
|
boardedDeparture.getAlarmLeadTimeMinutesObservable().registerObserver(
|
||||||
mStationPair.getUri());
|
new Observer<Integer>() {
|
||||||
targetIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
@Override
|
||||||
mNotificationIntent = PendingIntent.getActivity(this, 0, targetIntent,
|
public void onUpdate(Integer newValue) {
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
updateNotification();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
boardedDeparture.getAlarmPendingObservable().registerObserver(
|
||||||
|
new Observer<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public void onUpdate(Boolean newValue) {
|
||||||
|
updateNotification();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
updateNotification();
|
updateNotification();
|
||||||
|
|
||||||
@ -179,6 +192,9 @@ public class NotificationService extends Service implements EtdServiceListener {
|
|||||||
.getUncertaintySeconds() != departure
|
.getUncertaintySeconds() != departure
|
||||||
.getUncertaintySeconds())) {
|
.getUncertaintySeconds())) {
|
||||||
boardedDeparture.mergeEstimate(departure);
|
boardedDeparture.mergeEstimate(departure);
|
||||||
|
// Also merge back, in case boardedDeparture estimate is better
|
||||||
|
departure.mergeEstimate(boardedDeparture);
|
||||||
|
|
||||||
updateAlarm();
|
updateAlarm();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -211,8 +227,9 @@ public class NotificationService extends Service implements EtdServiceListener {
|
|||||||
final Departure boardedDeparture = ((BartRunnerApplication) getApplication())
|
final Departure boardedDeparture = ((BartRunnerApplication) getApplication())
|
||||||
.getBoardedDeparture();
|
.getBoardedDeparture();
|
||||||
|
|
||||||
if (boardedDeparture.hasDeparted()) {
|
if (boardedDeparture == null || boardedDeparture.hasDeparted()) {
|
||||||
shutDown(false);
|
shutDown(false);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boardedDeparture.updateAlarm(getApplicationContext(), mAlarmManager);
|
boardedDeparture.updateAlarm(getApplicationContext(), mAlarmManager);
|
||||||
@ -280,4 +297,5 @@ public class NotificationService extends Service implements EtdServiceListener {
|
|||||||
// Doesn't support binding
|
// Doesn't support binding
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -36,6 +36,10 @@ public class Observable<T> {
|
|||||||
listeners.remove(observer);
|
listeners.remove(observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void unregisterAllObservers() {
|
||||||
|
listeners.clear();
|
||||||
|
}
|
||||||
|
|
||||||
protected void notifyOfChange(T value) {
|
protected void notifyOfChange(T value) {
|
||||||
for (Observer<T> listener : listeners.keySet()) {
|
for (Observer<T> listener : listeners.keySet()) {
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
|