Changed train length/estimated arrival change timing
Prototyped departure alert dialog
This commit is contained in:
parent
4b94669aa7
commit
64d5eda3b5
@ -52,7 +52,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/topRow"
|
||||
android:layout_toRightOf="@id/destinationColorBar"
|
||||
bart:tickInterval="3" />
|
||||
bart:tickInterval="1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/uncertainty"
|
||||
|
25
res/layout/train_alert_dialog.xml
Normal file
25
res/layout/train_alert_dialog.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<net.simonvt.widget.NumberPicker
|
||||
android:id="@+id/numberPicker"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:layout_weight="1" >
|
||||
</net.simonvt.widget.NumberPicker>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView1"
|
||||
style="@style/LargeText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:maxLines="3"
|
||||
android:singleLine="false"
|
||||
android:text="minutes before departure" />
|
||||
|
||||
</LinearLayout>
|
@ -2,7 +2,13 @@
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- Base application theme is the default theme. -->
|
||||
<style name="AppTheme" parent="@style/Theme.HoloEverywhereDark.Sherlock"></style>
|
||||
|
||||
<style name="AppTheme" parent="@style/Theme.HoloEverywhereDark.Sherlock">
|
||||
<item name="numberPickerUpButtonStyle">@style/NPWidget.Holo.ImageButton.NumberPickerUpButton</item>
|
||||
<item name="numberPickerDownButtonStyle">@style/NPWidget.Holo.ImageButton.NumberPickerDownButton</item>
|
||||
<item name="numberPickerInputTextStyle">@style/NPWidget.Holo.EditText.NumberPickerInputText</item>
|
||||
<item name="numberPickerStyle">@style/NPWidget.Holo.NumberPicker</item>
|
||||
</style>
|
||||
|
||||
<style name="ButtonBar">
|
||||
<item name="android:layout_width">fill_parent</item>
|
||||
|
@ -73,9 +73,12 @@ public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
|
||||
initTextSwitcher(textSwitcher);
|
||||
|
||||
textSwitcher.setCurrentText(departure.getTrainLengthText());
|
||||
textSwitcher.setTextProviders(new TextProvider[] { new TextProvider() {
|
||||
textSwitcher.setTextProvider(new TextProvider() {
|
||||
@Override
|
||||
public String getText() {
|
||||
public String getText(long tickNumber) {
|
||||
if (tickNumber % 4 == 0) {
|
||||
return departure.getTrainLengthText();
|
||||
} else {
|
||||
final String estimatedArrivalTimeText = departure
|
||||
.getEstimatedArrivalTimeText(getContext());
|
||||
if (StringUtils.isBlank(estimatedArrivalTimeText)) {
|
||||
@ -84,12 +87,8 @@ public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
|
||||
return "Est. arrival " + estimatedArrivalTimeText;
|
||||
}
|
||||
}
|
||||
}, new TextProvider() {
|
||||
@Override
|
||||
public String getText() {
|
||||
return departure.getTrainLengthText();
|
||||
}
|
||||
} });
|
||||
});
|
||||
|
||||
ImageView colorBar = (ImageView) view
|
||||
.findViewById(R.id.destinationColorBar);
|
||||
@ -100,7 +99,7 @@ public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
|
||||
countdownTextView.setText(departure.getCountdownText());
|
||||
countdownTextView.setTextProvider(new TextProvider() {
|
||||
@Override
|
||||
public String getText() {
|
||||
public String getText(long tickNumber) {
|
||||
return departure.getCountdownText();
|
||||
}
|
||||
});
|
||||
|
89
src/com/dougkeen/bart/TrainAlertDialogFragment.java
Normal file
89
src/com/dougkeen/bart/TrainAlertDialogFragment.java
Normal file
@ -0,0 +1,89 @@
|
||||
package com.dougkeen.bart;
|
||||
|
||||
import net.simonvt.widget.NumberPicker;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
import com.WazaBe.HoloEverywhere.AlertDialog;
|
||||
import com.dougkeen.bart.model.Departure;
|
||||
|
||||
public class TrainAlertDialogFragment extends DialogFragment {
|
||||
|
||||
private static final String KEY_LAST_ALERT_DELAY = "lastAlertDelay";
|
||||
private Departure mDeparture;
|
||||
|
||||
public TrainAlertDialogFragment(Departure mDeparture) {
|
||||
super();
|
||||
this.mDeparture = mDeparture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
SharedPreferences preferences = getActivity().getPreferences(
|
||||
Context.MODE_PRIVATE);
|
||||
int lastAlertDelay = preferences.getInt(KEY_LAST_ALERT_DELAY, 5);
|
||||
|
||||
NumberPicker numberPicker = (NumberPicker) getDialog().findViewById(
|
||||
R.id.numberPicker);
|
||||
|
||||
final int maxValue = mDeparture.getMeanSecondsLeft() / 60;
|
||||
|
||||
String[] displayedValues = new String[maxValue];
|
||||
for (int i = 1; i <= maxValue; i++) {
|
||||
displayedValues[i - 1] = String.valueOf(i);
|
||||
}
|
||||
numberPicker.setMinValue(1);
|
||||
numberPicker.setMaxValue(maxValue);
|
||||
numberPicker.setDisplayedValues(displayedValues);
|
||||
|
||||
if (maxValue >= lastAlertDelay) {
|
||||
numberPicker.setValue(lastAlertDelay);
|
||||
} else if (maxValue >= 5) {
|
||||
numberPicker.setValue(5);
|
||||
} else if (maxValue >= 3) {
|
||||
numberPicker.setValue(3);
|
||||
} else {
|
||||
numberPicker.setValue(1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final FragmentActivity activity = getActivity();
|
||||
|
||||
return new AlertDialog.Builder(activity)
|
||||
.setTitle(R.string.set_up_departure_alert)
|
||||
.setCancelable(true)
|
||||
.setView(R.layout.train_alert_dialog)
|
||||
.setPositiveButton(R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog,
|
||||
int which) {
|
||||
NumberPicker numberPicker = (NumberPicker) getDialog()
|
||||
.findViewById(R.id.numberPicker);
|
||||
|
||||
Editor editor = getActivity().getPreferences(
|
||||
Context.MODE_PRIVATE).edit();
|
||||
editor.putInt(KEY_LAST_ALERT_DELAY,
|
||||
numberPicker.getValue());
|
||||
editor.commit();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.skip_alert,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog,
|
||||
int whichButton) {
|
||||
dialog.cancel();
|
||||
}
|
||||
}).create();
|
||||
}
|
||||
}
|
@ -19,13 +19,14 @@ import android.text.format.DateFormat;
|
||||
import android.text.util.Linkify;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.actionbarsherlock.app.SherlockListActivity;
|
||||
import com.actionbarsherlock.app.SherlockFragmentActivity;
|
||||
import com.actionbarsherlock.view.ActionMode;
|
||||
import com.actionbarsherlock.view.Menu;
|
||||
import com.actionbarsherlock.view.MenuInflater;
|
||||
@ -44,12 +45,10 @@ import com.dougkeen.bart.model.TextProvider;
|
||||
import com.dougkeen.bart.networktasks.GetRealTimeDeparturesTask;
|
||||
import com.dougkeen.bart.networktasks.GetScheduleInformationTask;
|
||||
|
||||
public class ViewDeparturesActivity extends SherlockListActivity {
|
||||
public class ViewDeparturesActivity extends SherlockFragmentActivity {
|
||||
|
||||
private static final int UNCERTAINTY_THRESHOLD = 17;
|
||||
|
||||
private static final int DIALOG_SET_ALERT = 1;
|
||||
|
||||
private Uri mUri;
|
||||
|
||||
private Station mOrigin;
|
||||
@ -138,6 +137,20 @@ public class ViewDeparturesActivity extends SherlockListActivity {
|
||||
}
|
||||
}
|
||||
setListAdapter(mDeparturesAdapter);
|
||||
getListView().setEmptyView(findViewById(android.R.id.empty));
|
||||
getListView().setOnItemClickListener(
|
||||
new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> adapterView,
|
||||
View view, int position, long id) {
|
||||
mSelectedDeparture = (Departure) getListAdapter()
|
||||
.getItem(position);
|
||||
if (mActionMode != null) {
|
||||
mActionMode.finish();
|
||||
}
|
||||
startDepartureActionMode();
|
||||
}
|
||||
});
|
||||
|
||||
findViewById(R.id.missingDepartureText).setVisibility(View.VISIBLE);
|
||||
|
||||
@ -147,6 +160,22 @@ public class ViewDeparturesActivity extends SherlockListActivity {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private AdapterView<ListAdapter> getListView() {
|
||||
return (AdapterView<ListAdapter>) findViewById(android.R.id.list);
|
||||
}
|
||||
|
||||
private DepartureArrayAdapter mListAdapter;
|
||||
|
||||
protected DepartureArrayAdapter getListAdapter() {
|
||||
return mListAdapter;
|
||||
}
|
||||
|
||||
protected void setListAdapter(DepartureArrayAdapter adapter) {
|
||||
mListAdapter = adapter;
|
||||
getListView().setAdapter(mListAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
cancelDataFetch();
|
||||
@ -310,9 +339,26 @@ public class ViewDeparturesActivity extends SherlockListActivity {
|
||||
}
|
||||
|
||||
boolean needsBetterAccuracy = false;
|
||||
|
||||
// Keep track of first departure, since we'll request another quick
|
||||
// refresh if it has departed.
|
||||
Departure firstDeparture = null;
|
||||
|
||||
final List<Departure> departures = result.getDepartures();
|
||||
if (mDeparturesAdapter.getCount() > 0) {
|
||||
if (mDeparturesAdapter.isEmpty()) {
|
||||
// Just copy everything to the adapter
|
||||
for (Departure departure : departures) {
|
||||
if (firstDeparture == null) {
|
||||
firstDeparture = departure;
|
||||
}
|
||||
mDeparturesAdapter.add(departure);
|
||||
}
|
||||
|
||||
// Since all the departures are new, we'll definitely need better
|
||||
// accuracy
|
||||
needsBetterAccuracy = true;
|
||||
} else {
|
||||
// Let's merge the latest departure list with the adapter
|
||||
int adapterIndex = -1;
|
||||
for (Departure departure : departures) {
|
||||
adapterIndex++;
|
||||
@ -321,37 +367,42 @@ public class ViewDeparturesActivity extends SherlockListActivity {
|
||||
existingDeparture = mDeparturesAdapter
|
||||
.getItem(adapterIndex);
|
||||
}
|
||||
// Looks for departures at the beginning of the adapter that
|
||||
// aren't in the latest list of departures
|
||||
while (existingDeparture != null
|
||||
&& !departure.equals(existingDeparture)) {
|
||||
// Remove old departure
|
||||
mDeparturesAdapter.remove(existingDeparture);
|
||||
if (adapterIndex < mDeparturesAdapter.getCount()) {
|
||||
// Try again with next departure (keep in mind the next
|
||||
// departure is now at the current index, since we
|
||||
// removed a member)
|
||||
existingDeparture = mDeparturesAdapter
|
||||
.getItem(adapterIndex);
|
||||
} else {
|
||||
// Reached the end of the adapter... give up
|
||||
existingDeparture = null;
|
||||
}
|
||||
}
|
||||
// Merge the estimate if we found a matching departure,
|
||||
// otherwise add a new one to the adapter
|
||||
if (existingDeparture != null) {
|
||||
existingDeparture.mergeEstimate(departure);
|
||||
} else {
|
||||
mDeparturesAdapter.add(departure);
|
||||
existingDeparture = departure;
|
||||
}
|
||||
|
||||
// Set first departure
|
||||
if (firstDeparture == null) {
|
||||
firstDeparture = existingDeparture;
|
||||
}
|
||||
|
||||
// Check if estimate is accurate enough
|
||||
if (existingDeparture.getUncertaintySeconds() > UNCERTAINTY_THRESHOLD) {
|
||||
needsBetterAccuracy = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Departure departure : departures) {
|
||||
if (firstDeparture == null) {
|
||||
firstDeparture = departure;
|
||||
}
|
||||
mDeparturesAdapter.add(departure);
|
||||
}
|
||||
needsBetterAccuracy = true;
|
||||
}
|
||||
mDeparturesAdapter.notifyDataSetChanged();
|
||||
requestScheduleIfNecessary();
|
||||
@ -385,15 +436,19 @@ public class ViewDeparturesActivity extends SherlockListActivity {
|
||||
}
|
||||
|
||||
private void requestScheduleIfNecessary() {
|
||||
// Bail if there's nothing to match schedules to
|
||||
if (mDeparturesAdapter.getCount() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch if we don't have anything at all
|
||||
if (mLatestScheduleInfo == null) {
|
||||
fetchLatestSchedule();
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, check if the latest departure doesn't have schedule
|
||||
// info... if not, fetch
|
||||
Departure lastDeparture = mDeparturesAdapter.getItem(mDeparturesAdapter
|
||||
.getCount() - 1);
|
||||
if (mLatestScheduleInfo.getLatestDepartureTime() < lastDeparture
|
||||
@ -424,6 +479,7 @@ public class ViewDeparturesActivity extends SherlockListActivity {
|
||||
}
|
||||
}
|
||||
|
||||
// Match scheduled departures with real time departures in adapter
|
||||
int lastSearchIndex = 0;
|
||||
int tripCount = mLatestScheduleInfo.getTrips().size();
|
||||
boolean departureUpdated = false;
|
||||
@ -433,6 +489,7 @@ public class ViewDeparturesActivity extends SherlockListActivity {
|
||||
Departure departure = mDeparturesAdapter.getItem(departureIndex);
|
||||
for (int i = lastSearchIndex; i < tripCount; i++) {
|
||||
ScheduleItem trip = mLatestScheduleInfo.getTrips().get(i);
|
||||
// Definitely not a match if they have different destinations
|
||||
if (!departure.getDestination().abbreviation.equals(trip
|
||||
.getTrainHeadStation())) {
|
||||
continue;
|
||||
@ -478,17 +535,21 @@ public class ViewDeparturesActivity extends SherlockListActivity {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't estimate for non-scheduled transfers
|
||||
if (!departure.getRequiresTransfer()) {
|
||||
if (!departure.hasEstimatedTripTime() && localAverageLength > 0) {
|
||||
// Use the average we just calculated if available
|
||||
departure.setEstimatedTripTime(localAverageLength);
|
||||
} else if (!departure.hasEstimatedTripTime()) {
|
||||
// Otherwise just assume the global average
|
||||
departure.setEstimatedTripTime(mAverageTripLength);
|
||||
}
|
||||
} else if (departure.getRequiresTransfer()
|
||||
&& !departure.hasAnyArrivalEstimate()) {
|
||||
lastUnestimatedTransfer = departure;
|
||||
}
|
||||
|
||||
if (!departure.hasAnyArrivalEstimate()) {
|
||||
departuresWithoutEstimates++;
|
||||
}
|
||||
@ -515,6 +576,7 @@ public class ViewDeparturesActivity extends SherlockListActivity {
|
||||
getContentResolver().update(mUri, contentValues, null, null);
|
||||
}
|
||||
|
||||
// If we still have some departures without estimates, try again later
|
||||
if (departuresWithoutEstimates > 0) {
|
||||
scheduleScheduleInfoFetch(20000);
|
||||
}
|
||||
@ -618,7 +680,7 @@ public class ViewDeparturesActivity extends SherlockListActivity {
|
||||
+ " " + departure.getUncertaintyText());
|
||||
departureCountdown.setTextProvider(new TextProvider() {
|
||||
@Override
|
||||
public String getText() {
|
||||
public String getText(long tickNumber) {
|
||||
if (departure.hasDeparted()) {
|
||||
return "Departed";
|
||||
} else {
|
||||
@ -632,22 +694,13 @@ public class ViewDeparturesActivity extends SherlockListActivity {
|
||||
.getEstimatedArrivalMinutesLeftText(this));
|
||||
arrivalCountdown.setTextProvider(new TextProvider() {
|
||||
@Override
|
||||
public String getText() {
|
||||
public String getText(long tickNumber) {
|
||||
return departure
|
||||
.getEstimatedArrivalMinutesLeftText(ViewDeparturesActivity.this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onListItemClick(ListView l, View v, int position, long id) {
|
||||
mSelectedDeparture = (Departure) getListAdapter().getItem(position);
|
||||
if (mActionMode != null) {
|
||||
mActionMode.finish();
|
||||
}
|
||||
startDepartureActionMode();
|
||||
}
|
||||
|
||||
private void startDepartureActionMode() {
|
||||
mActionMode = startActionMode(new DepartureActionMode());
|
||||
mActionMode.setTitle(mSelectedDeparture.getDestinationName());
|
||||
@ -672,6 +725,13 @@ public class ViewDeparturesActivity extends SherlockListActivity {
|
||||
if (item.getItemId() == R.id.boardTrain) {
|
||||
mBoardedDeparture = mSelectedDeparture;
|
||||
refreshBoardedDeparture();
|
||||
|
||||
// Don't prompt for alert if train is about to leave
|
||||
if (mBoardedDeparture.getMeanSecondsLeft() / 60 > 1) {
|
||||
new TrainAlertDialogFragment(mBoardedDeparture).show(
|
||||
getSupportFragmentManager(), "dialog");
|
||||
}
|
||||
|
||||
mode.finish();
|
||||
return true;
|
||||
}
|
||||
|
@ -50,8 +50,8 @@ public class CountdownTextView extends TextView implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTick() {
|
||||
setText(mTextProvider.getText());
|
||||
public void onTick(long tickNumber) {
|
||||
setText(mTextProvider.getText(tickNumber));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,13 +4,12 @@ import java.util.Iterator;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
|
||||
public class Ticker {
|
||||
public static interface TickSubscriber {
|
||||
int getTickInterval();
|
||||
|
||||
void onTick();
|
||||
void onTick(long mTickCount);
|
||||
}
|
||||
|
||||
private static Ticker sInstance;
|
||||
@ -54,7 +53,7 @@ public class Ticker {
|
||||
stillHasListeners = true;
|
||||
if (subscriber.getTickInterval() > 0
|
||||
&& mTickCount % subscriber.getTickInterval() == 0)
|
||||
subscriber.onTick();
|
||||
subscriber.onTick(mTickCount);
|
||||
}
|
||||
long endTimeNanos = System.nanoTime();
|
||||
|
||||
|
@ -30,8 +30,7 @@ public class TimedTextSwitcher extends TextSwitcher implements
|
||||
}
|
||||
|
||||
private int mTickInterval;
|
||||
private TextProvider[] mTextProviderArray;
|
||||
private int mCurrentIndex = 0;
|
||||
private TextProvider mTextProvider;
|
||||
|
||||
@Override
|
||||
public int getTickInterval() {
|
||||
@ -42,22 +41,21 @@ public class TimedTextSwitcher extends TextSwitcher implements
|
||||
this.mTickInterval = tickInterval;
|
||||
}
|
||||
|
||||
public void setTextProviders(TextProvider[] textProviders) {
|
||||
mTextProviderArray = textProviders;
|
||||
public void setTextProvider(TextProvider textProvider) {
|
||||
mTextProvider = textProvider;
|
||||
Ticker.getInstance().addSubscriber(this);
|
||||
}
|
||||
|
||||
private String mLastText;
|
||||
|
||||
@Override
|
||||
public void onTick() {
|
||||
String text = mTextProviderArray[mCurrentIndex].getText();
|
||||
public void onTick(long tickNumber) {
|
||||
String text = mTextProvider.getText(tickNumber);
|
||||
if (StringUtils.isNotBlank(text)
|
||||
&& !StringUtils.equalsIgnoreCase(text, mLastText)) {
|
||||
mLastText = text;
|
||||
setText(text);
|
||||
}
|
||||
mCurrentIndex = (mCurrentIndex + 1) % mTextProviderArray.length;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -405,7 +405,7 @@ public class Departure implements Parcelable, Comparable<Departure> {
|
||||
if (hasDeparted()) {
|
||||
return "";
|
||||
} else {
|
||||
return "(±" + getUncertaintySeconds() + "s)";
|
||||
return "(±" + getUncertaintySeconds() + "s)";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,6 @@ package com.dougkeen.bart.model;
|
||||
|
||||
public interface TextProvider {
|
||||
|
||||
String getText();
|
||||
String getText(long tickNumber);
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user