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