Strong references in ETD listener, replaced SQLite junk with simple JSON file, annotation conversion

This commit is contained in:
Doug Keen 2013-03-24 16:11:43 -07:00
parent aaefc58702
commit 601d8516e6
21 changed files with 350 additions and 728 deletions

View File

@ -14,7 +14,7 @@
android:targetSdkVersion="14" />
<application
android:name=".BartRunnerApplication"
android:name=".BartRunnerApplication_"
android:icon="@drawable/icon"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
@ -61,17 +61,11 @@
android:label="@string/system_map" >
</activity>
<provider
android:name=".data.BartContentProvider"
android:authorities="com.dougkeen.bart.dataprovider"
android:exported="false"
android:label="BartRunner data provider" />
<service
android:name=".services.BoardedDepartureService"
android:exported="false" />
<service
android:name=".services.EtdService"
android:name=".services.EtdService_"
android:exported="false" />
<receiver

Binary file not shown.

BIN
libs/jackson-core-2.1.2.jar Normal file

Binary file not shown.

Binary file not shown.

View File

@ -16,21 +16,9 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/empty_favorites_list_message"
android:visibility="gone" />
<FrameLayout
android:id="@+id/progress"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1000" >
<ProgressBar
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</FrameLayout>
<TextView
android:id="@+id/alertMessages"
android:layout_width="wrap_content"

View File

@ -4,6 +4,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ObjectUtils;
@ -15,9 +16,16 @@ import android.media.MediaPlayer;
import android.os.Parcel;
import android.util.Log;
import com.dougkeen.bart.data.DatabaseHelper;
import com.dougkeen.bart.data.FavoritesPersistence;
import com.dougkeen.bart.model.Constants;
import com.dougkeen.bart.model.Departure;
import com.dougkeen.bart.model.Station;
import com.dougkeen.bart.model.StationPair;
import com.googlecode.androidannotations.annotations.Bean;
import com.googlecode.androidannotations.annotations.EApplication;
@EApplication
public class BartRunnerApplication extends Application {
private static final int FIVE_MINUTES = 5 * 60 * 1000;
@ -33,6 +41,53 @@ public class BartRunnerApplication extends Application {
private static Context context;
@Bean
FavoritesPersistence favoritesPersistenceContext;
private List<StationPair> favorites;
public void saveFavorites() {
if (favorites != null) {
favoritesPersistenceContext.persist(favorites);
}
}
public List<StationPair> getFavorites() {
if (favorites == null) {
favorites = favoritesPersistenceContext.restore();
if (favorites.isEmpty()) {
// Upgrade database, in case favorites are still in there
new DatabaseHelper(this).getReadableDatabase().close();
favorites = favoritesPersistenceContext.restore();
}
}
return favorites;
}
public void setFavorites(List<StationPair> favorites) {
this.favorites = favorites;
}
public StationPair getFavorite(Station origin, Station destination) {
for (StationPair favorite : getFavorites()) {
if (origin.equals(favorite.getOrigin())
&& destination.equals(favorite.getDestination())) {
return favorite;
}
}
return null;
}
public void addFavorite(StationPair favorite) {
getFavorites().add(favorite);
saveFavorites();
}
public void removeFavorite(StationPair favorite) {
getFavorites().remove(favorite);
saveFavorites();
}
@Override
public void onCreate() {
super.onCreate();
@ -59,12 +114,13 @@ public class BartRunnerApplication extends Application {
InputStream inputStream = null;
try {
inputStream = new FileInputStream(cachedDepartureFile);
byte[] byteArray = IOUtils.toByteArray(inputStream);
Parcel parcel = Parcel.obtain();
final byte[] byteArray = IOUtils.toByteArray(inputStream);
final Parcel parcel = Parcel.obtain();
parcel.unmarshall(byteArray, 0, byteArray.length);
parcel.setDataPosition(0);
Departure lastBoardedDeparture = Departure.CREATOR
.createFromParcel(parcel);
parcel.recycle();
/*
* Check if the cached one is relatively recent. If so,

View File

@ -29,6 +29,13 @@ public abstract class AbstractRouteSelectionFragment extends DialogFragment {
mTitle = title;
}
@Override
public void setArguments(Bundle args) {
super.setArguments(args);
if (args.containsKey("title"))
mTitle = args.getString("title");
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

View File

@ -1,14 +1,12 @@
package com.dougkeen.bart.activities;
import android.content.ContentValues;
import android.view.View;
import android.widget.CheckBox;
import com.dougkeen.bart.BartRunnerApplication;
import com.dougkeen.bart.R;
import com.dougkeen.bart.data.RoutesColumns;
import com.dougkeen.bart.model.Constants;
import com.dougkeen.bart.model.Station;
import com.dougkeen.bart.model.StationPair;
public class AddRouteDialogFragment extends AbstractRouteSelectionFragment {
public AddRouteDialogFragment() {
@ -16,10 +14,6 @@ public class AddRouteDialogFragment extends AbstractRouteSelectionFragment {
R.string.add_route));
}
public AddRouteDialogFragment(String title) {
super(title);
}
@Override
public void onStart() {
super.onStart();
@ -29,22 +23,12 @@ public class AddRouteDialogFragment extends AbstractRouteSelectionFragment {
@Override
protected void onOkButtonClick(Station origin, Station destination) {
ContentValues values = new ContentValues();
values.put(RoutesColumns.FROM_STATION.string, origin.abbreviation);
values.put(RoutesColumns.TO_STATION.string, destination.abbreviation);
getActivity().getContentResolver().insert(
Constants.FAVORITE_CONTENT_URI, values);
RoutesListActivity activity = (RoutesListActivity) getActivity();
activity.addFavorite(new StationPair(origin, destination));
if (((CheckBox) getDialog().findViewById(R.id.return_checkbox))
.isChecked()) {
values = new ContentValues();
values.put(RoutesColumns.FROM_STATION.string,
destination.abbreviation);
values.put(RoutesColumns.TO_STATION.string, origin.abbreviation);
getActivity().getContentResolver().insert(
Constants.FAVORITE_CONTENT_URI, values);
activity.addFavorite(new StationPair(destination, origin));
}
dismiss();

View File

@ -14,10 +14,6 @@ public class QuickRouteDialogFragment extends AbstractRouteSelectionFragment {
R.string.quick_departure_lookup));
}
public QuickRouteDialogFragment(String title) {
super(title);
}
@Override
protected void onOkButtonClick(Station origin, Station destination) {
startActivity(new Intent(Intent.ACTION_VIEW,

View File

@ -3,16 +3,10 @@ package com.dougkeen.bart.activities;
import java.util.Calendar;
import java.util.TimeZone;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
@ -29,24 +23,24 @@ import com.actionbarsherlock.view.ActionMode;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import com.dougkeen.bart.BartRunnerApplication;
import com.dougkeen.bart.R;
import com.dougkeen.bart.controls.Ticker;
import com.dougkeen.bart.controls.Ticker.TickSubscriber;
import com.dougkeen.bart.data.CursorUtils;
import com.dougkeen.bart.data.FavoritesArrayAdapter;
import com.dougkeen.bart.data.RoutesColumns;
import com.dougkeen.bart.model.Alert;
import com.dougkeen.bart.model.Alert.AlertList;
import com.dougkeen.bart.model.Constants;
import com.dougkeen.bart.model.Station;
import com.dougkeen.bart.model.StationPair;
import com.dougkeen.bart.networktasks.AlertsClient;
import com.dougkeen.bart.networktasks.ElevatorClient;
import com.dougkeen.bart.networktasks.GetRouteFareTask;
import com.googlecode.androidannotations.annotations.AfterViews;
import com.googlecode.androidannotations.annotations.App;
import com.googlecode.androidannotations.annotations.Background;
import com.googlecode.androidannotations.annotations.Click;
import com.googlecode.androidannotations.annotations.EActivity;
import com.googlecode.androidannotations.annotations.InstanceState;
import com.googlecode.androidannotations.annotations.ItemClick;
import com.googlecode.androidannotations.annotations.ItemLongClick;
import com.googlecode.androidannotations.annotations.UiThread;
@ -54,24 +48,25 @@ import com.googlecode.androidannotations.annotations.ViewById;
import com.googlecode.androidannotations.annotations.rest.RestService;
@EActivity(R.layout.main)
public class RoutesListActivity extends SActivity implements
LoaderCallbacks<Cursor>, TickSubscriber {
public class RoutesListActivity extends SActivity implements TickSubscriber {
private static final String NO_DELAYS_REPORTED = "No delays reported";
private static final int FAVORITES_LOADER_ID = 0;
private static final TimeZone PACIFIC_TIME = TimeZone
.getTimeZone("America/Los_Angeles");
private Uri mCurrentlySelectedUri;
@InstanceState
StationPair mCurrentlySelectedStationPair;
private Station mCurrentlySelectedOrigin;
private Station mCurrentlySelectedDestination;
@InstanceState
String mCurrentAlerts;
private ActionMode mActionMode;
private FavoritesArrayAdapter mRoutesAdapter;
@App
BartRunnerApplication app;
@RestService
AlertsClient alertsClient;
@ -89,16 +84,16 @@ public class RoutesListActivity extends SActivity implements
@Click(R.id.quickLookupButton)
void quickLookupButtonClick() {
DialogFragment dialog = new QuickRouteDialogFragment(
getString(R.string.quick_departure_lookup));
DialogFragment dialog = new QuickRouteDialogFragment();
dialog.show(getSupportFragmentManager().beginTransaction());
}
@ItemClick(android.R.id.list)
void listItemClicked(StationPair item) {
startActivity(new Intent(Intent.ACTION_VIEW,
ContentUris.withAppendedId(Constants.FAVORITE_CONTENT_URI,
item.getId())));
Intent intent = new Intent(RoutesListActivity.this,
ViewDeparturesActivity.class);
intent.putExtra(Constants.STATION_PAIR_EXTRA, item);
startActivity(intent);
}
@ItemLongClick(android.R.id.list)
@ -107,11 +102,7 @@ public class RoutesListActivity extends SActivity implements
mActionMode.finish();
}
mCurrentlySelectedUri = ContentUris.withAppendedId(
Constants.FAVORITE_CONTENT_URI, item.getId());
mCurrentlySelectedOrigin = item.getOrigin();
mCurrentlySelectedDestination = item.getDestination();
mCurrentlySelectedStationPair = item;
startContextualActionMode();
}
@ -121,9 +112,7 @@ public class RoutesListActivity extends SActivity implements
setTitle(R.string.favorite_routes);
mRoutesAdapter = new FavoritesArrayAdapter(this,
R.layout.favorite_listing);
getSupportLoaderManager().initLoader(FAVORITES_LOADER_ID, null, this);
R.layout.favorite_listing, app.getFavorites());
setListAdapter(mRoutesAdapter);
@ -132,6 +121,9 @@ public class RoutesListActivity extends SActivity implements
if (mCurrentAlerts != null) {
showAlertMessage(mCurrentAlerts);
}
startEtdListeners();
refreshFares();
}
/** Called when the activity is first created. */
@ -140,58 +132,14 @@ public class RoutesListActivity extends SActivity implements
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
if (savedInstanceState.getString("currentlySelectedOrigin") != null) {
mCurrentlySelectedOrigin = Station
.getByAbbreviation(savedInstanceState
.getString("currentlySelectedOrigin"));
}
if (savedInstanceState.getString("currentlySelectedDestination") != null) {
mCurrentlySelectedDestination = Station
.getByAbbreviation(savedInstanceState
.getString("currentlySelectedDestination"));
}
if (savedInstanceState.getParcelable("currentlySelectedUri") != null) {
mCurrentlySelectedUri = (Uri) savedInstanceState
.getParcelable("currentlySelectedUri");
}
if (savedInstanceState.getBoolean("hasActionMode")) {
startContextualActionMode();
}
mCurrentAlerts = savedInstanceState.getString("currentAlerts");
}
Ticker.getInstance().addSubscriber(this, getApplicationContext());
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(this, Constants.FAVORITE_CONTENT_URI,
new String[] { RoutesColumns._ID.string,
RoutesColumns.FROM_STATION.string,
RoutesColumns.TO_STATION.string,
RoutesColumns.FARE.string,
RoutesColumns.FARE_LAST_UPDATED.string,
RoutesColumns.AVERAGE_TRIP_SAMPLE_COUNT.string,
RoutesColumns.AVERAGE_TRIP_LENGTH.string }, null, null,
RoutesColumns._ID.string);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
if (cursor.getCount() == 0) {
((TextView) findViewById(android.R.id.empty))
.setText(R.string.empty_favorites_list_message);
}
mRoutesAdapter.updateFromCursor(cursor);
refreshFares(cursor);
findViewById(R.id.progress).setVisibility(View.GONE);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
// Nothing to do
}
private AdapterView<ListAdapter> getListView() {
return listView;
}
@ -205,70 +153,59 @@ public class RoutesListActivity extends SActivity implements
getListView().setAdapter(mRoutesAdapter);
}
private void refreshFares(Cursor cursor) {
if (cursor.moveToFirst()) {
do {
final Station orig = Station.getByAbbreviation(CursorUtils
.getString(cursor, RoutesColumns.FROM_STATION));
final Station dest = Station.getByAbbreviation(CursorUtils
.getString(cursor, RoutesColumns.TO_STATION));
final Long id = CursorUtils.getLong(cursor, RoutesColumns._ID);
final Long lastUpdateMillis = CursorUtils.getLong(cursor,
RoutesColumns.FARE_LAST_UPDATED);
void addFavorite(StationPair pair) {
mRoutesAdapter.add(pair);
}
Calendar now = Calendar.getInstance();
Calendar lastUpdate = Calendar.getInstance();
lastUpdate.setTimeInMillis(lastUpdateMillis);
private void refreshFares() {
for (int i = getListAdapter().getCount() - 1; i >= 0; i--) {
final StationPair stationPair = getListAdapter().getItem(i);
now.setTimeZone(PACIFIC_TIME);
lastUpdate.setTimeZone(PACIFIC_TIME);
Calendar now = Calendar.getInstance();
Calendar lastUpdate = Calendar.getInstance();
lastUpdate.setTimeInMillis(stationPair.getFareLastUpdated());
// Update every day
if (now.get(Calendar.DAY_OF_YEAR) != lastUpdate
.get(Calendar.DAY_OF_YEAR)) {
GetRouteFareTask fareTask = new GetRouteFareTask() {
@Override
public void onResult(String fare) {
ContentValues values = new ContentValues();
values.put(RoutesColumns.FARE.string, fare);
values.put(RoutesColumns.FARE_LAST_UPDATED.string,
System.currentTimeMillis());
now.setTimeZone(PACIFIC_TIME);
lastUpdate.setTimeZone(PACIFIC_TIME);
getContentResolver()
.update(ContentUris.withAppendedId(
Constants.FAVORITE_CONTENT_URI, id),
values, null, null);
}
// Update every day
if (now.get(Calendar.DAY_OF_YEAR) != lastUpdate
.get(Calendar.DAY_OF_YEAR)
|| now.get(Calendar.YEAR) != lastUpdate.get(Calendar.YEAR)) {
GetRouteFareTask fareTask = new GetRouteFareTask() {
@Override
public void onResult(String fare) {
stationPair.setFare(fare);
stationPair.setFareLastUpdated(System
.currentTimeMillis());
getListAdapter().notifyDataSetChanged();
}
@Override
public void onError(Exception exception) {
// Ignore... we can do this later
}
};
fareTask.execute(new GetRouteFareTask.Params(orig, dest));
}
} while (cursor.moveToNext());
@Override
public void onError(Exception exception) {
// Ignore... we can do this later
}
};
fareTask.execute(new GetRouteFareTask.Params(stationPair
.getOrigin(), stationPair.getDestination()));
}
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mCurrentlySelectedOrigin != null)
outState.putString("currentlySelectedOrigin",
mCurrentlySelectedOrigin.abbreviation);
if (mCurrentlySelectedDestination != null)
outState.putString("currentlySelectedDestination",
mCurrentlySelectedDestination.abbreviation);
outState.putParcelable("currentlySelectedUri", mCurrentlySelectedUri);
outState.putBoolean("hasActionMode", mActionMode != null);
outState.putString("currentAlerts", mCurrentAlerts);
}
@Override
protected void onResume() {
super.onResume();
Ticker.getInstance().startTicking(this);
startEtdListeners();
}
private void startEtdListeners() {
if (mRoutesAdapter != null && !mRoutesAdapter.isEmpty()
&& !mRoutesAdapter.areEtdListenersActive()) {
mRoutesAdapter.setUpEtdListeners();
@ -287,6 +224,8 @@ public class RoutesListActivity extends SActivity implements
protected void onStop() {
super.onStop();
Ticker.getInstance().stopTicking(this);
app.saveFavorites();
}
@Override
@ -314,13 +253,11 @@ public class RoutesListActivity extends SActivity implements
private MenuItem elevatorMenuItem;
private View origElevatorActionView;
private String mCurrentAlerts;
public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId();
if (itemId == R.id.add_favorite_menu_button) {
new AddRouteDialogFragment(getString(R.string.add_route))
.show(getSupportFragmentManager().beginTransaction());
new AddRouteDialogFragment().show(getSupportFragmentManager()
.beginTransaction());
return true;
} else if (itemId == R.id.view_system_map_button) {
startActivity(new Intent(this, ViewMapActivity.class));
@ -393,7 +330,7 @@ public class RoutesListActivity extends SActivity implements
@UiThread
void resetElevatorMenuGraphic() {
invalidateOptionsMenu();
ActivityCompat.invalidateOptionsMenu(this);
elevatorMenuItem.setActionView(origElevatorActionView);
}
@ -407,8 +344,9 @@ public class RoutesListActivity extends SActivity implements
private void startContextualActionMode() {
mActionMode = startActionMode(new RouteActionMode());
mActionMode.setTitle(mCurrentlySelectedOrigin.name);
mActionMode.setSubtitle("to " + mCurrentlySelectedDestination.name);
mActionMode.setTitle(mCurrentlySelectedStationPair.getOrigin().name);
mActionMode.setSubtitle("to "
+ mCurrentlySelectedStationPair.getDestination().name);
}
private final class RouteActionMode implements ActionMode.Callback {
@ -426,8 +364,11 @@ public class RoutesListActivity extends SActivity implements
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
if (item.getItemId() == R.id.view) {
startActivity(new Intent(Intent.ACTION_VIEW,
mCurrentlySelectedUri));
Intent intent = new Intent(RoutesListActivity.this,
ViewDeparturesActivity.class);
intent.putExtra(Constants.STATION_PAIR_EXTRA,
mCurrentlySelectedStationPair);
startActivity(intent);
mode.finish();
return true;
} else if (item.getItemId() == R.id.delete) {
@ -439,11 +380,9 @@ public class RoutesListActivity extends SActivity implements
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
getContentResolver().delete(
mCurrentlySelectedUri, null, null);
mCurrentlySelectedUri = null;
mCurrentlySelectedOrigin = null;
mCurrentlySelectedDestination = null;
getListAdapter().remove(
mCurrentlySelectedStationPair);
mCurrentlySelectedStationPair = null;
mActionMode.finish();
dialog.dismiss();
}

View File

@ -9,7 +9,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.database.Cursor;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.net.Uri;
@ -18,9 +17,6 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Parcelable;
import android.os.Vibrator;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.text.format.DateFormat;
import android.text.util.Linkify;
import android.util.Log;
@ -44,27 +40,21 @@ import com.dougkeen.bart.controls.SwipeHelper;
import com.dougkeen.bart.controls.Ticker;
import com.dougkeen.bart.controls.YourTrainLayout;
import com.dougkeen.bart.data.DepartureArrayAdapter;
import com.dougkeen.bart.data.RoutesColumns;
import com.dougkeen.bart.model.Constants;
import com.dougkeen.bart.model.Departure;
import com.dougkeen.bart.model.Station;
import com.dougkeen.bart.model.StationPair;
import com.dougkeen.bart.services.BoardedDepartureService;
import com.dougkeen.bart.services.EtdService;
import com.dougkeen.bart.services.EtdService.EtdServiceBinder;
import com.dougkeen.bart.services.EtdService.EtdServiceListener;
import com.dougkeen.bart.services.EtdService_;
import com.dougkeen.util.Observer;
import com.dougkeen.util.WakeLocker;
public class ViewDeparturesActivity extends SActivity implements
EtdServiceListener {
private static final int LOADER_ID = 123;
private Uri mUri;
private Station mOrigin;
private Station mDestination;
private StationPair mStationPair;
private Departure mSelectedDeparture;
@ -89,64 +79,8 @@ public class ViewDeparturesActivity extends SActivity implements
final Intent intent = getIntent();
String action = intent.getAction();
if (Intent.ACTION_VIEW.equals(action)) {
mUri = intent.getData();
}
final Uri uri = mUri;
final BartRunnerApplication bartRunnerApplication = (BartRunnerApplication) getApplication();
if (savedInstanceState != null
&& savedInstanceState.containsKey("origin")
&& savedInstanceState.containsKey("destination")) {
mOrigin = Station.getByAbbreviation(savedInstanceState
.getString("origin"));
mDestination = Station.getByAbbreviation(savedInstanceState
.getString("destination"));
setListTitle();
} else {
getSupportLoaderManager().initLoader(LOADER_ID, null,
new LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(
ViewDeparturesActivity.this, uri,
new String[] {
RoutesColumns.FROM_STATION.string,
RoutesColumns.TO_STATION.string },
null, null, null);
}
@Override
public void onLoadFinished(Loader<Cursor> loader,
Cursor cursor) {
if (!cursor.moveToFirst()) {
Log.wtf(Constants.TAG,
"Couldn't find Route record for the current Activity");
}
mOrigin = Station.getByAbbreviation(cursor
.getString(0));
mDestination = Station.getByAbbreviation(cursor
.getString(1));
setListTitle();
if (mBound && mEtdService != null)
mEtdService.registerListener(
ViewDeparturesActivity.this, false);
refreshBoardedDeparture(false);
getSupportLoaderManager().destroyLoader(LOADER_ID);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
// ignore
}
});
}
mEmptyView = (TextView) findViewById(android.R.id.empty);
mEmptyView.setText(R.string.departure_wait_message);
@ -155,6 +89,43 @@ public class ViewDeparturesActivity extends SActivity implements
mDeparturesAdapter = new DepartureArrayAdapter(this,
R.layout.departure_listing);
setListAdapter(mDeparturesAdapter);
final ListView listView = getListView();
listView.setEmptyView(findViewById(android.R.id.empty));
listView.setOnItemClickListener(mListItemClickListener);
listView.setOnItemLongClickListener(mListItemLongClickListener);
mMissingDepartureText = findViewById(R.id.missingDepartureText);
mMissingDepartureText.setVisibility(View.VISIBLE);
mYourTrainSection = (YourTrainLayout) findViewById(R.id.yourTrainSection);
mYourTrainSection.setOnClickListener(mYourTrainSectionClickListener);
mSwipeHelper = new SwipeHelper(mYourTrainSection, null,
new SwipeHelper.OnDismissCallback() {
@Override
public void onDismiss(View view, Object token) {
dismissYourTrainSelection();
if (mActionMode != null) {
mActionMode.finish();
}
}
});
mYourTrainSection.setOnTouchListener(mSwipeHelper);
if (savedInstanceState != null
&& savedInstanceState.containsKey("stationPair")) {
mStationPair = savedInstanceState.getParcelable("stationPair");
setListTitle();
} else {
mStationPair = intent.getExtras().getParcelable(
Constants.STATION_PAIR_EXTRA);
setListTitle();
if (mBound && mEtdService != null)
mEtdService
.registerListener(ViewDeparturesActivity.this, false);
refreshBoardedDeparture(false);
}
if (savedInstanceState != null) {
if (savedInstanceState.containsKey("departures")) {
for (Parcelable departure : savedInstanceState
@ -178,29 +149,6 @@ public class ViewDeparturesActivity extends SActivity implements
startYourTrainActionMode();
}
}
setListAdapter(mDeparturesAdapter);
final ListView listView = getListView();
listView.setEmptyView(findViewById(android.R.id.empty));
listView.setOnItemClickListener(mListItemClickListener);
listView.setOnItemLongClickListener(mListItemLongClickListener);
mMissingDepartureText = findViewById(R.id.missingDepartureText);
mMissingDepartureText.setVisibility(View.VISIBLE);
mYourTrainSection = (YourTrainLayout) findViewById(R.id.yourTrainSection);
mYourTrainSection.setOnClickListener(mYourTrainSectionClickListener);
mSwipeHelper = new SwipeHelper(mYourTrainSection, null,
new SwipeHelper.OnDismissCallback() {
@Override
public void onDismiss(View view, Object token) {
dismissYourTrainSelection();
if (mActionMode != null) {
mActionMode.finish();
}
}
});
mYourTrainSection.setOnTouchListener(mSwipeHelper);
refreshBoardedDeparture(false);
getSupportActionBar().setHomeButtonEnabled(true);
@ -297,8 +245,9 @@ public class ViewDeparturesActivity extends SActivity implements
}
private void setListTitle() {
((TextView) findViewById(R.id.listTitle)).setText(mOrigin.name + " to "
+ mDestination.name);
((TextView) findViewById(R.id.listTitle))
.setText(mStationPair.getOrigin().name + " to "
+ mStationPair.getDestination().name);
}
private ListView getListView() {
@ -399,7 +348,7 @@ public class ViewDeparturesActivity extends SActivity implements
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mOrigin != null || mDestination != null) {
if (mStationPair != null) {
/*
* If origin or destination are null, this thing was never
* initialized in the first place, so there's really nothing to save
@ -415,15 +364,14 @@ public class ViewDeparturesActivity extends SActivity implements
isDepartureActionModeActive());
outState.putBoolean("hasYourTrainActionMode",
isYourTrainActionModeActive());
outState.putString("origin", mOrigin.abbreviation);
outState.putString("destination", mDestination.abbreviation);
outState.putParcelable("stationPair", mStationPair);
}
}
@Override
protected void onStart() {
super.onStart();
bindService(new Intent(this, EtdService.class), mConnection,
bindService(EtdService_.intent(this).get(), mConnection,
Context.BIND_AUTO_CREATE);
Ticker.getInstance().startTicking(this);
}
@ -459,10 +407,8 @@ public class ViewDeparturesActivity extends SActivity implements
public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId();
if (itemId == android.R.id.home) {
Intent intent = new Intent(Intent.ACTION_VIEW,
Constants.FAVORITE_CONTENT_URI);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
RoutesListActivity_.intent(this)
.flags(Intent.FLAG_ACTIVITY_CLEAR_TOP).start();
return true;
} else if (itemId == R.id.view_on_bart_site_button) {
startActivity(new Intent(
@ -471,9 +417,9 @@ public class ViewDeparturesActivity extends SActivity implements
+ DateFormat.format("h:mmaa",
System.currentTimeMillis())
+ "&orig="
+ mOrigin.abbreviation
+ mStationPair.getOrigin().abbreviation
+ "&dest="
+ mDestination.abbreviation)));
+ mStationPair.getDestination().abbreviation)));
return true;
} else if (itemId == R.id.view_system_map_button) {
startActivity(new Intent(this, ViewMapActivity.class));
@ -508,13 +454,16 @@ public class ViewDeparturesActivity extends SActivity implements
private void setBoardedDeparture(Departure selectedDeparture) {
final BartRunnerApplication application = (BartRunnerApplication) getApplication();
selectedDeparture.setPassengerDestination(mDestination);
selectedDeparture
.setPassengerDestination(mStationPair.getDestination());
application.setBoardedDeparture(selectedDeparture);
refreshBoardedDeparture(true);
// Start the notification service
startService(new Intent(ViewDeparturesActivity.this,
BoardedDepartureService.class));
final Intent intent = new Intent(ViewDeparturesActivity.this,
BoardedDepartureService.class);
intent.putExtra("departure", selectedDeparture);
startService(intent);
}
private void startDepartureActionMode() {
@ -815,9 +764,7 @@ public class ViewDeparturesActivity extends SActivity implements
@Override
public StationPair getStationPair() {
if (mOrigin == null || mDestination == null)
return null;
return new StationPair(mOrigin, mDestination);
return mStationPair;
}
private void hideYourTrainSection() {
@ -846,4 +793,4 @@ public class ViewDeparturesActivity extends SActivity implements
private Departure getBoardedDeparture() {
return ((BartRunnerApplication) getApplication()).getBoardedDeparture();
}
}
}

View File

@ -1,271 +0,0 @@
package com.dougkeen.bart.data;
import java.util.HashMap;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.MatrixCursor.RowBuilder;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import com.dougkeen.bart.model.Constants;
public class BartContentProvider extends ContentProvider {
private static final UriMatcher sUriMatcher;
private static HashMap<String, String> sFavoritesProjectionMap;
private static final int FAVORITES = 1;
private static final int FAVORITE_ID = 2;
private static final int ARBITRARY_ROUTE = 3;
private static final int ARBITRARY_ROUTE_UNDEFINED = 4;
/**
* The default sort order for events
*/
private static final String DEFAULT_SORT_ORDER = RoutesColumns.FROM_STATION.string
+ " DESC";
static {
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(Constants.AUTHORITY, "favorites", FAVORITES);
sUriMatcher.addURI(Constants.AUTHORITY, "favorites/#", FAVORITE_ID);
sUriMatcher.addURI(Constants.AUTHORITY, "route/*/*", ARBITRARY_ROUTE);
sUriMatcher.addURI(Constants.AUTHORITY, "route",
ARBITRARY_ROUTE_UNDEFINED);
sFavoritesProjectionMap = new HashMap<String, String>();
sFavoritesProjectionMap.put(RoutesColumns._ID.string,
RoutesColumns._ID.string);
sFavoritesProjectionMap.put(RoutesColumns.FROM_STATION.string,
RoutesColumns.FROM_STATION.string);
sFavoritesProjectionMap.put(RoutesColumns.TO_STATION.string,
RoutesColumns.TO_STATION.string);
sFavoritesProjectionMap.put(RoutesColumns.FARE.string,
RoutesColumns.FARE.string);
sFavoritesProjectionMap.put(RoutesColumns.FARE_LAST_UPDATED.string,
RoutesColumns.FARE_LAST_UPDATED.string);
sFavoritesProjectionMap.put(
RoutesColumns.AVERAGE_TRIP_SAMPLE_COUNT.string,
RoutesColumns.AVERAGE_TRIP_SAMPLE_COUNT.string);
sFavoritesProjectionMap.put(RoutesColumns.AVERAGE_TRIP_LENGTH.string,
RoutesColumns.AVERAGE_TRIP_LENGTH.string);
}
private DatabaseHelper mDatabaseHelper;
@Override
public boolean onCreate() {
mDatabaseHelper = new DatabaseHelper(getContext());
return true;
}
@Override
public String getType(Uri uri) {
int match = sUriMatcher.match(uri);
if (match == FAVORITES) {
return Constants.FAVORITE_CONTENT_TYPE;
} else if (match == FAVORITE_ID) {
return Constants.FAVORITE_CONTENT_ITEM_TYPE;
} else if (match == ARBITRARY_ROUTE) {
return Constants.ARBITRARY_ROUTE_TYPE;
} else if (match == ARBITRARY_ROUTE_UNDEFINED) {
return Constants.ARBITRARY_ROUTE_UNDEFINED_TYPE;
} else {
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
SQLiteDatabase db = mDatabaseHelper.getReadableDatabase();
String orderBy = sortOrder;
int match = sUriMatcher.match(uri);
if (match == ARBITRARY_ROUTE) {
final String origin = uri.getPathSegments().get(1);
final String destination = uri.getPathSegments().get(2);
qb.setTables(DatabaseHelper.FAVORITES_TABLE_NAME);
qb.setProjectionMap(sFavoritesProjectionMap);
qb.appendWhere(String.format("%s = '%s' AND %s = '%s'",
RoutesColumns.FROM_STATION, origin,
RoutesColumns.TO_STATION, destination));
Cursor query = qb.query(db, projection, selection, selectionArgs,
null, null, sortOrder);
if (query.getCount() > 0)
return query;
MatrixCursor returnCursor = new MatrixCursor(projection);
RowBuilder newRow = returnCursor.newRow();
for (String column : projection) {
if (column.equals(RoutesColumns.FROM_STATION.string)) {
newRow.add(origin);
} else if (column.equals(RoutesColumns.TO_STATION.string)) {
newRow.add(destination);
} else {
newRow.add(null);
}
}
return returnCursor;
} else if (match == FAVORITE_ID) {
qb.setTables(DatabaseHelper.FAVORITES_TABLE_NAME);
qb.setProjectionMap(sFavoritesProjectionMap);
qb.appendWhere(RoutesColumns._ID + " = "
+ uri.getPathSegments().get(1));
} else if (match == FAVORITES) {
qb.setTables(DatabaseHelper.FAVORITES_TABLE_NAME);
qb.setProjectionMap(sFavoritesProjectionMap);
} else {
throw new IllegalArgumentException("Unknown URI " + uri);
}
// If no sort order is specified use the default
if (TextUtils.isEmpty(orderBy)) {
orderBy = DEFAULT_SORT_ORDER;
}
// Get the database and run the query
Cursor cursor = qb.query(db, projection, selection, selectionArgs,
null, null, orderBy);
// Tell the cursor what uri to watch, so it knows when its source data
// changes
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
@Override
public Uri insert(Uri uri, ContentValues initialValues) {
ContentValues values;
if (initialValues != null) {
values = new ContentValues(initialValues);
} else {
values = new ContentValues();
}
SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
// Validate the requested uri
int match = sUriMatcher.match(uri);
if (match == FAVORITES) {
long rowId = -1;
Cursor cursor = db
.query(DatabaseHelper.FAVORITES_TABLE_NAME,
new String[] { RoutesColumns._ID.string },
RoutesColumns.FROM_STATION + "=? AND "
+ RoutesColumns.TO_STATION + "=?",
new String[] {
values.getAsString(RoutesColumns.FROM_STATION.string),
values.getAsString(RoutesColumns.TO_STATION.string) },
null, null, null);
try {
if (cursor.moveToFirst()) {
rowId = cursor.getLong(0);
}
} finally {
CursorUtils.closeCursorQuietly(cursor);
}
if (rowId < 0) {
rowId = db.insert(DatabaseHelper.FAVORITES_TABLE_NAME,
RoutesColumns.FROM_STATION.string, values);
}
if (rowId > 0) {
Uri eventUri = ContentUris.withAppendedId(
Constants.FAVORITE_CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(eventUri, null,
false);
return eventUri;
}
} else {
throw new IllegalArgumentException("Unknown URI " + uri);
}
throw new SQLException("Failed to insert row into " + uri);
}
@Override
public int update(Uri uri, ContentValues values, String where,
String[] whereArgs) {
SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
// Validate the requested uri
int match = sUriMatcher.match(uri);
if (match == FAVORITE_ID) {
String favoriteId = uri.getPathSegments().get(1);
int count = db.update(
DatabaseHelper.FAVORITES_TABLE_NAME,
values,
RoutesColumns._ID
+ " = "
+ favoriteId
+ (!TextUtils.isEmpty(where) ? " AND (" + where
+ ')' : ""), whereArgs);
getContext().getContentResolver().notifyChange(uri, null);
return count;
} else if (match == ARBITRARY_ROUTE) {
// Get the route with the origin and destination provided, and
// simply delegate to the previous log branch. If the given route
// doesn't exist, do nothing.
String origin = uri.getPathSegments().get(1);
String destination = uri.getPathSegments().get(2);
Cursor query = db.query(DatabaseHelper.FAVORITES_TABLE_NAME,
new String[] { RoutesColumns._ID.string },
RoutesColumns.FROM_STATION.string + "=? AND "
+ RoutesColumns.TO_STATION.string + "=?",
new String[] { origin, destination }, null, null, null);
try {
if (query.moveToFirst()) {
return update(ContentUris.withAppendedId(
Constants.FAVORITE_CONTENT_URI, query.getLong(0)),
values, where, whereArgs);
}
} finally {
CursorUtils.closeCursorQuietly(query);
}
}
return 0;
}
@Override
public int delete(Uri uri, String where, String[] whereArgs) {
// TODO: Sync with REST service?
SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
int count;
int match = sUriMatcher.match(uri);
if (match == FAVORITES) {
count = db.delete(DatabaseHelper.FAVORITES_TABLE_NAME, where,
whereArgs);
} else if (match == FAVORITE_ID) {
String favoriteId = uri.getPathSegments().get(1);
count = db.delete(
DatabaseHelper.FAVORITES_TABLE_NAME,
RoutesColumns._ID
+ " = "
+ favoriteId
+ (!TextUtils.isEmpty(where) ? " AND (" + where
+ ')' : ""), whereArgs);
} else {
throw new IllegalArgumentException("Unknown URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
}

View File

@ -20,4 +20,8 @@ public final class CursorUtils {
public static final Long getLong(Cursor cursor, RoutesColumns column) {
return cursor.getLong(cursor.getColumnIndex(column.string));
}
public static final Integer getInteger(Cursor cursor, RoutesColumns column) {
return cursor.getInt(cursor.getColumnIndex(column.string));
}
}

View File

@ -1,31 +1,32 @@
package com.dougkeen.bart.data;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import com.dougkeen.bart.BartRunnerApplication;
import com.dougkeen.bart.model.StationPair;
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "bart.dougkeen.db";
private static final int DATABASE_VERSION = 4;
private static final int DATABASE_VERSION = 6;
public static final String FAVORITES_TABLE_NAME = "Favorites";
private BartRunnerApplication app;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
app = (BartRunnerApplication) context.getApplicationContext();
}
@Override
public void onCreate(SQLiteDatabase db) {
createFavoritesTable(db);
}
private void createFavoritesTable(SQLiteDatabase db) {
@ -45,54 +46,24 @@ public class DatabaseHelper extends SQLiteOpenHelper {
try {
createFavoritesTable(db);
List<String> columns = getColumns(db, FAVORITES_TABLE_NAME);
Cursor query = db.query(FAVORITES_TABLE_NAME, RoutesColumns.all(),
null, null, null, null, null);
db.execSQL("ALTER TABLE " + FAVORITES_TABLE_NAME
+ " RENAME TO temp_" + FAVORITES_TABLE_NAME);
List<StationPair> favorites = new ArrayList<StationPair>();
createFavoritesTable(db);
while (query.moveToNext()) {
favorites.add(StationPair.createFromCursor(query));
}
columns.retainAll(getColumns(db, FAVORITES_TABLE_NAME));
query.close();
String cols = StringUtils.join(columns, ",");
db.execSQL(String.format(
"INSERT INTO %s (%s) SELECT %s from temp_%s",
FAVORITES_TABLE_NAME, cols, cols, FAVORITES_TABLE_NAME));
new FavoritesPersistence(app).persist(favorites);
db.execSQL("DROP TABLE temp_" + FAVORITES_TABLE_NAME);
db.execSQL("DROP TABLE " + FAVORITES_TABLE_NAME);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
public static List<String> getColumns(SQLiteDatabase db, String tableName) {
List<String> ar = null;
Cursor c = null;
try {
c = db.rawQuery("select * from " + tableName + " limit 1", null);
if (c != null) {
ar = new ArrayList<String>(Arrays.asList(c.getColumnNames()));
}
} catch (Exception e) {
Log.v(tableName, e.getMessage(), e);
e.printStackTrace();
} finally {
if (c != null)
c.close();
}
return ar;
}
public static String join(List<String> list, String delim) {
StringBuilder buf = new StringBuilder();
int num = list.size();
for (int i = 0; i < num; i++) {
if (i != 0)
buf.append(delim);
buf.append((String) list.get(i));
}
return buf.toString();
}
}

View File

@ -9,9 +9,7 @@ import org.apache.commons.lang3.StringUtils;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.database.Cursor;
import android.os.IBinder;
import android.view.LayoutInflater;
import android.view.View;
@ -32,6 +30,7 @@ import com.dougkeen.bart.model.TextProvider;
import com.dougkeen.bart.services.EtdService;
import com.dougkeen.bart.services.EtdService.EtdServiceBinder;
import com.dougkeen.bart.services.EtdService.EtdServiceListener;
import com.dougkeen.bart.services.EtdService_;
public class FavoritesArrayAdapter extends ArrayAdapter<StationPair> {
@ -82,10 +81,11 @@ public class FavoritesArrayAdapter extends ArrayAdapter<StationPair> {
return !mEtdListeners.isEmpty();
}
public FavoritesArrayAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
public FavoritesArrayAdapter(Context context, int textViewResourceId,
List<StationPair> objects) {
super(context, textViewResourceId, objects);
mHostActivity = (Activity) context;
mHostActivity.bindService(new Intent(mHostActivity, EtdService.class),
mHostActivity.bindService(EtdService_.intent(mHostActivity).get(),
mConnection, Context.BIND_AUTO_CREATE);
}
@ -118,42 +118,6 @@ public class FavoritesArrayAdapter extends ArrayAdapter<StationPair> {
clearEtdListeners();
}
public void updateFromCursor(Cursor cursor) {
if (!cursor.moveToFirst()) {
clear();
}
for (int i = 0; i < getCount(); i++) {
StationPair adapterItem = getItem(i);
if (cursor.isAfterLast()) {
while (i < getCount()) {
remove(getItem(i));
}
} else {
StationPair cursorItem = StationPair.createFromCursor(cursor);
while (!cursorItem.equals(adapterItem)) {
remove(adapterItem);
if (i < getCount()) {
adapterItem = getItem(i);
} else {
break;
}
}
if (cursorItem.equals(adapterItem)
&& !cursorItem.fareEquals(adapterItem)) {
adapterItem.setFare(cursorItem.getFare());
adapterItem.setFareLastUpdated(cursorItem
.getFareLastUpdated());
notifyDataSetChanged();
}
cursor.moveToNext();
}
}
while (!cursor.isAfterLast()) {
add(StationPair.createFromCursor(cursor));
cursor.moveToNext();
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;

View File

@ -0,0 +1,63 @@
package com.dougkeen.bart.data;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.IOUtils;
import android.content.Context;
import android.util.Log;
import com.dougkeen.bart.BartRunnerApplication;
import com.dougkeen.bart.model.StationPair;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.googlecode.androidannotations.annotations.EBean;
@EBean
public class FavoritesPersistence {
private static final String TAG = "FavoritesPersistence";
private final ObjectMapper objectMapper = new ObjectMapper();
private BartRunnerApplication app;
public FavoritesPersistence(Context context) {
app = (BartRunnerApplication) context.getApplicationContext();
}
public void persist(List<StationPair> favorites) {
FileOutputStream outputStream = null;
try {
outputStream = app
.openFileOutput("favorites", Context.MODE_PRIVATE);
objectMapper.writeValue(outputStream, favorites);
} catch (Exception e) {
Log.e(TAG, "Could not write favorites file", e);
} finally {
IOUtils.closeQuietly(outputStream);
}
}
public List<StationPair> restore() {
for (String file : app.fileList()) {
if ("favorites".equals(file)) {
FileInputStream inputStream = null;
try {
inputStream = app.openFileInput("favorites");
return objectMapper.readValue(inputStream,
new TypeReference<ArrayList<StationPair>>() {
});
} catch (Exception e) {
Log.e(TAG, "Could not read favorites file", e);
} finally {
IOUtils.closeQuietly(inputStream);
}
}
}
return new ArrayList<StationPair>();
}
}

View File

@ -1,14 +1,11 @@
package com.dougkeen.bart.data;
public enum RoutesColumns {
_ID("_id", "INTEGER", false),
FROM_STATION("FROM_STATION", "TEXT", false),
TO_STATION("TO_STATION", "TEXT", false),
FARE("FARE", "TEXT", true),
FARE_LAST_UPDATED("FARE_LAST_UPDATED", "INTEGER", true),
AVERAGE_TRIP_SAMPLE_COUNT("AVE_TRIP_SAMPLE_COUNT", "INTEGER", true),
AVERAGE_TRIP_LENGTH("AVE_TRIP_LENGTH", "INTEGER", true);
_ID("_id", "INTEGER", false), FROM_STATION("FROM_STATION", "TEXT", false), TO_STATION(
"TO_STATION", "TEXT", false), FARE("FARE", "TEXT", true), FARE_LAST_UPDATED(
"FARE_LAST_UPDATED", "INTEGER", true), AVERAGE_TRIP_SAMPLE_COUNT(
"AVE_TRIP_SAMPLE_COUNT", "INTEGER", true), AVERAGE_TRIP_LENGTH(
"AVE_TRIP_LENGTH", "INTEGER", true);
// This class cannot be instantiated
private RoutesColumns(String string, String type, Boolean nullable) {
@ -24,4 +21,13 @@ public enum RoutesColumns {
protected String getColumnDef() {
return string + " " + sqliteType + (nullable ? "" : " NOT NULL");
}
public static String[] all() {
final RoutesColumns[] values = RoutesColumns.values();
String[] returnArray = new String[values.length];
for (int i = values.length - 1; i >= 0; i--) {
returnArray[i] = values[i].string;
}
return returnArray;
}
}

View File

@ -17,4 +17,5 @@ public class Constants {
public static final String TAG = "com.dougkeen.BartRunner";
public static final String API_KEY = "5LD9-IAYI-TRAT-MHHW";
public static final String ACTION_ALARM = "com.dougkeen.action.ALARM";
public static final String STATION_PAIR_EXTRA = "StationPair";
}

View File

@ -3,27 +3,23 @@ package com.dougkeen.bart.model;
import org.apache.commons.lang3.ObjectUtils;
import android.database.Cursor;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import com.dougkeen.bart.data.CursorUtils;
import com.dougkeen.bart.data.RoutesColumns;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
public class StationPair implements Parcelable {
public StationPair(Station origin, Station destination) {
@JsonCreator
public StationPair(@JsonProperty("origin") Station origin,
@JsonProperty("destination") Station destination) {
super();
this.origin = origin;
this.destination = destination;
}
public StationPair(Long id, Station origin, Station destination) {
super();
this.origin = origin;
this.destination = destination;
this.id = id;
}
public StationPair(Parcel in) {
readFromParcel(in);
}
@ -34,22 +30,22 @@ public class StationPair implements Parcelable {
RoutesColumns.FROM_STATION)),
Station.getByAbbreviation(CursorUtils.getString(cursor,
RoutesColumns.TO_STATION)));
pair.id = CursorUtils.getLong(cursor, RoutesColumns._ID);
pair.fare = CursorUtils.getString(cursor, RoutesColumns.FARE);
pair.fareLastUpdated = CursorUtils.getLong(cursor,
RoutesColumns.FARE_LAST_UPDATED);
pair.averageTripLength = CursorUtils.getInteger(cursor,
RoutesColumns.AVERAGE_TRIP_LENGTH);
pair.averageTripSampleCount = CursorUtils.getInteger(cursor,
RoutesColumns.AVERAGE_TRIP_SAMPLE_COUNT);
return pair;
}
private Long id;
private Station origin;
private Station destination;
private String fare;
private Long fareLastUpdated;
public Long getId() {
return id;
}
private long fareLastUpdated;
private int averageTripLength;
private int averageTripSampleCount;
public Station getOrigin() {
return origin;
@ -67,29 +63,35 @@ public class StationPair implements Parcelable {
this.fare = fare;
}
public Long getFareLastUpdated() {
public long getFareLastUpdated() {
return fareLastUpdated;
}
public void setFareLastUpdated(Long fareLastUpdated) {
public void setFareLastUpdated(long fareLastUpdated) {
this.fareLastUpdated = fareLastUpdated;
}
public int getAverageTripLength() {
return averageTripLength;
}
public void setAverageTripLength(int averageTripLength) {
this.averageTripLength = averageTripLength;
}
public int getAverageTripSampleCount() {
return averageTripSampleCount;
}
public void setAverageTripSampleCount(int averageTripSampleCount) {
this.averageTripSampleCount = averageTripSampleCount;
}
public boolean isBetweenStations(Station station1, Station station2) {
return (origin.equals(station1) && destination.equals(station2))
|| (origin.equals(station2) && destination.equals(station1));
}
public Uri getUri() {
if (getOrigin() != null && getDestination() != null) {
return Constants.ARBITRARY_ROUTE_CONTENT_URI_ROOT.buildUpon()
.appendPath(getOrigin().abbreviation)
.appendPath(getDestination().abbreviation).build();
} else {
return null;
}
}
@Override
public int hashCode() {
final int prime = 31;

View File

@ -89,7 +89,7 @@ public class BoardedDepartureService extends Service implements
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper, this);
bindService(new Intent(this, EtdService.class), mConnection,
bindService(EtdService_.intent(this).get(), mConnection,
Context.BIND_AUTO_CREATE);
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

View File

@ -10,20 +10,15 @@ import java.util.WeakHashMap;
import org.apache.commons.lang3.math.NumberUtils;
import android.app.Service;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.support.v4.content.CursorLoader;
import android.util.Log;
import com.dougkeen.bart.BartRunnerApplication;
import com.dougkeen.bart.R;
import com.dougkeen.bart.data.RoutesColumns;
import com.dougkeen.bart.model.Constants;
import com.dougkeen.bart.model.Departure;
import com.dougkeen.bart.model.RealTimeDepartures;
@ -33,7 +28,9 @@ import com.dougkeen.bart.model.Station;
import com.dougkeen.bart.model.StationPair;
import com.dougkeen.bart.networktasks.GetRealTimeDeparturesTask;
import com.dougkeen.bart.networktasks.GetScheduleInformationTask;
import com.googlecode.androidannotations.annotations.EService;
@EService
public class EtdService extends Service {
private IBinder mBinder;
@ -106,19 +103,13 @@ public class EtdService extends Service {
private class EtdServiceEngine {
private static final int UNCERTAINTY_THRESHOLD = 17;
private Uri mUri;
private final StationPair mStationPair;
private boolean mIgnoreDepartureDirection = false;
private boolean mPendingEtdRequest = false;
private int mAverageTripLength;
private int mAverageTripSampleCount;
// We'll only use the keys
private WeakHashMap<EtdServiceListener, Boolean> mListeners;
private Map<EtdServiceListener, Boolean> mListeners;
private boolean mLimitToFirstNonDeparted = true;
@ -134,24 +125,9 @@ public class EtdService extends Service {
public EtdServiceEngine(final StationPair route) {
mStationPair = route;
mListeners = new WeakHashMap<EtdService.EtdServiceListener, Boolean>();
mListeners = new HashMap<EtdService.EtdServiceListener, Boolean>();
mRunnableQueue = new Handler();
mLatestDepartures = new ArrayList<Departure>();
mUri = Constants.ARBITRARY_ROUTE_CONTENT_URI_ROOT.buildUpon()
.appendPath(mStationPair.getOrigin().abbreviation)
.appendPath(mStationPair.getDestination().abbreviation)
.build();
Cursor cursor = new CursorLoader(EtdService.this, mUri,
new String[] { RoutesColumns.AVERAGE_TRIP_LENGTH.string,
RoutesColumns.AVERAGE_TRIP_SAMPLE_COUNT.string },
null, null, null).loadInBackground();
if (cursor.moveToFirst()) {
mAverageTripLength = cursor.getInt(0);
mAverageTripSampleCount = cursor.getInt(1);
}
cursor.close();
}
protected void registerListener(EtdServiceListener listener,
@ -238,8 +214,7 @@ public class EtdService extends Service {
};
mGetDeparturesTask = task;
Log.v(Constants.TAG, "Fetching data from server");
task.execute(new StationPair(mStationPair.getOrigin(), mStationPair
.getDestination()));
task.execute(mStationPair);
notifyListenersOfRequestStart();
}
@ -271,8 +246,7 @@ public class EtdService extends Service {
};
Log.i(Constants.TAG, "Fetching data from server");
mGetScheduleInformationTask = task;
task.execute(new StationPair(mStationPair.getOrigin(), mStationPair
.getDestination()));
task.execute(mStationPair);
}
protected void applyScheduleInformation(ScheduleInformation result) {
@ -363,7 +337,8 @@ public class EtdService extends Service {
departure.setEstimatedTripTime(localAverageLength);
} else if (!departure.hasEstimatedTripTime()) {
// Otherwise just assume the global average
departure.setEstimatedTripTime(mAverageTripLength);
departure.setEstimatedTripTime(mStationPair
.getAverageTripLength());
}
} else if (departure.getRequiresTransfer()
&& !departure.hasAnyArrivalEstimate()) {
@ -381,20 +356,16 @@ public class EtdService extends Service {
// Update global average
if (mLatestScheduleInfo.getTripCountForAverage() > 0) {
int newAverageSampleCount = mAverageTripSampleCount
int newAverageSampleCount = mStationPair
.getAverageTripSampleCount()
+ mLatestScheduleInfo.getTripCountForAverage();
int newAverage = (mAverageTripLength * mAverageTripSampleCount + localAverageLength
int newAverage = (mStationPair.getAverageTripLength()
* mStationPair.getAverageTripSampleCount() + localAverageLength
* mLatestScheduleInfo.getTripCountForAverage())
/ newAverageSampleCount;
ContentValues contentValues = new ContentValues();
contentValues.put(RoutesColumns.AVERAGE_TRIP_LENGTH.string,
newAverage);
contentValues.put(
RoutesColumns.AVERAGE_TRIP_SAMPLE_COUNT.string,
newAverageSampleCount);
getContentResolver().update(mUri, contentValues, null, null);
mStationPair.setAverageTripLength(newAverage);
mStationPair.setAverageTripSampleCount(newAverageSampleCount);
}
/*