Now shows fares
This commit is contained in:
parent
43988a2ea4
commit
c72d07ce3a
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.dougkeen.bart"
|
package="com.dougkeen.bart"
|
||||||
android:versionCode="8"
|
android:versionCode="9"
|
||||||
android:versionName="1.0.3" >
|
android:versionName="1.1" >
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
@ -1,13 +1,39 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="fill_parent" android:layout_height="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:paddingLeft="5dp" android:paddingRight="5dp">
|
android:layout_height="fill_parent"
|
||||||
<TextView android:id="@+id/originText" style="@style/FavoriteListingTextView"
|
android:paddingLeft="5dp"
|
||||||
android:layout_alignParentTop="true" android:text="Origin" />
|
android:paddingRight="5dp" >
|
||||||
<TextView style="@style/FavoriteListingTextView" android:text="to"
|
|
||||||
android:layout_below="@id/originText" android:paddingLeft="15dp"
|
<TextView
|
||||||
android:paddingRight="8dp" android:id="@+id/to" />
|
android:id="@+id/fareText"
|
||||||
<TextView android:id="@+id/destinationText" style="@style/FavoriteListingTextView"
|
style="@style/FareTextView"
|
||||||
android:layout_below="@id/originText" android:layout_toRightOf="@id/to"
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:maxLines="3" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/originText"
|
||||||
|
style="@style/FavoriteListingTextView"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_toLeftOf="@id/fareText"
|
||||||
|
android:text="Origin" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/to"
|
||||||
|
style="@style/FavoriteListingTextView"
|
||||||
|
android:layout_below="@id/originText"
|
||||||
|
android:paddingLeft="15dp"
|
||||||
|
android:paddingRight="8dp"
|
||||||
|
android:text="to" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/destinationText"
|
||||||
|
style="@style/FavoriteListingTextView"
|
||||||
|
android:layout_below="@id/originText"
|
||||||
|
android:layout_toLeftOf="@id/fareText"
|
||||||
|
android:layout_toRightOf="@id/to"
|
||||||
android:text="Destination" />
|
android:text="Destination" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
@ -18,6 +18,14 @@
|
|||||||
<item name="android:singleLine">true</item>
|
<item name="android:singleLine">true</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="FareTextView">
|
||||||
|
<item name="android:layout_width">wrap_content</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:textSize">16dp</item>
|
||||||
|
<item name="android:ellipsize">end</item>
|
||||||
|
<item name="android:singleLine">true</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="DepartureDestinationText">
|
<style name="DepartureDestinationText">
|
||||||
<item name="android:layout_width">wrap_content</item>
|
<item name="android:layout_width">wrap_content</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
@ -11,4 +11,5 @@ public class Constants {
|
|||||||
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 = "BartCatcher";
|
public static final String TAG = "BartCatcher";
|
||||||
|
public static final String API_KEY = "5LD9-IAYI-TRAT-MHHW";
|
||||||
}
|
}
|
||||||
|
46
src/com/dougkeen/bart/FareContentHandler.java
Normal file
46
src/com/dougkeen/bart/FareContentHandler.java
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package com.dougkeen.bart;
|
||||||
|
|
||||||
|
import org.xml.sax.Attributes;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
import org.xml.sax.helpers.DefaultHandler;
|
||||||
|
|
||||||
|
public class FareContentHandler extends DefaultHandler {
|
||||||
|
public FareContentHandler() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String currentValue;
|
||||||
|
private boolean isParsingTag;
|
||||||
|
private String fare;
|
||||||
|
|
||||||
|
public String getFare() {
|
||||||
|
return fare;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void characters(char[] ch, int start, int length)
|
||||||
|
throws SAXException {
|
||||||
|
if (isParsingTag) {
|
||||||
|
currentValue = new String(ch, start, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startElement(String uri, String localName, String qName,
|
||||||
|
Attributes attributes) throws SAXException {
|
||||||
|
if (localName.equals("fare")) {
|
||||||
|
isParsingTag = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endElement(String uri, String localName, String qName)
|
||||||
|
throws SAXException {
|
||||||
|
if (localName.equals("fare")) {
|
||||||
|
fare = "$" + currentValue;
|
||||||
|
}
|
||||||
|
isParsingTag = false;
|
||||||
|
currentValue = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -9,14 +9,8 @@ import java.util.List;
|
|||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
import org.apache.http.client.ClientProtocolException;
|
|
||||||
import org.apache.http.client.HttpClient;
|
|
||||||
import org.apache.http.client.methods.HttpGet;
|
import org.apache.http.client.methods.HttpGet;
|
||||||
import org.apache.http.client.methods.HttpUriRequest;
|
import org.apache.http.client.methods.HttpUriRequest;
|
||||||
import org.apache.http.conn.params.ConnManagerParams;
|
|
||||||
import org.apache.http.impl.client.DefaultHttpClient;
|
|
||||||
import org.apache.http.params.HttpConnectionParams;
|
|
||||||
import org.apache.http.params.HttpParams;
|
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
@ -29,10 +23,8 @@ public abstract class GetRealTimeDeparturesTask
|
|||||||
extends
|
extends
|
||||||
AsyncTask<GetRealTimeDeparturesTask.Params, Integer, RealTimeDepartures> {
|
AsyncTask<GetRealTimeDeparturesTask.Params, Integer, RealTimeDepartures> {
|
||||||
|
|
||||||
private static final int CONNECTION_TIMEOUT_MILLIS = 10000;
|
private final static String ETD_URL = "http://api.bart.gov/api/etd.aspx?cmd=etd&key="
|
||||||
private final static String API_KEY = "5LD9-IAYI-TRAT-MHHW";
|
+ Constants.API_KEY + "&orig=%1$s&dir=%2$s";
|
||||||
private final static String API_URL = "http://api.bart.gov/api/etd.aspx?cmd=etd&key="
|
|
||||||
+ API_KEY + "&orig=%1$s&dir=%2$s";
|
|
||||||
private final static int MAX_ATTEMPTS = 5;
|
private final static int MAX_ATTEMPTS = 5;
|
||||||
|
|
||||||
private Exception mException;
|
private Exception mException;
|
||||||
@ -57,7 +49,7 @@ public abstract class GetRealTimeDeparturesTask
|
|||||||
int attemptNumber) {
|
int attemptNumber) {
|
||||||
String xml = null;
|
String xml = null;
|
||||||
try {
|
try {
|
||||||
HttpUriRequest request = new HttpGet(String.format(API_URL,
|
HttpUriRequest request = new HttpGet(String.format(ETD_URL,
|
||||||
params.origin.abbreviation, mRoutes.get(0).getDirection()));
|
params.origin.abbreviation, mRoutes.get(0).getDirection()));
|
||||||
|
|
||||||
EtdContentHandler handler = new EtdContentHandler(params.origin,
|
EtdContentHandler handler = new EtdContentHandler(params.origin,
|
||||||
@ -66,7 +58,7 @@ public abstract class GetRealTimeDeparturesTask
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse response = executeWithRecovery(request);
|
HttpResponse response = NetworkUtils.executeWithRecovery(request);
|
||||||
|
|
||||||
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
|
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
|
||||||
throw new IOException("Server returned "
|
throw new IOException("Server returned "
|
||||||
@ -111,26 +103,6 @@ public abstract class GetRealTimeDeparturesTask
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HttpResponse executeWithRecovery(final HttpUriRequest request)
|
|
||||||
throws IOException, ClientProtocolException {
|
|
||||||
try {
|
|
||||||
return getHttpClient().execute(request);
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
// try again... this is a rare error
|
|
||||||
return getHttpClient().execute(request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static HttpClient getHttpClient() {
|
|
||||||
HttpClient client = new DefaultHttpClient();
|
|
||||||
final HttpParams params = client.getParams();
|
|
||||||
HttpConnectionParams.setConnectionTimeout(params,
|
|
||||||
CONNECTION_TIMEOUT_MILLIS);
|
|
||||||
HttpConnectionParams.setSoTimeout(params, CONNECTION_TIMEOUT_MILLIS);
|
|
||||||
ConnManagerParams.setTimeout(params, CONNECTION_TIMEOUT_MILLIS);
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Params {
|
public static class Params {
|
||||||
public Params(Station origin, Station destination) {
|
public Params(Station origin, Station destination) {
|
||||||
super();
|
super();
|
||||||
|
122
src/com/dougkeen/bart/GetRouteFareTask.java
Normal file
122
src/com/dougkeen/bart/GetRouteFareTask.java
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
package com.dougkeen.bart;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.HttpStatus;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.client.methods.HttpUriRequest;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.Xml;
|
||||||
|
|
||||||
|
public abstract class GetRouteFareTask extends
|
||||||
|
AsyncTask<GetRouteFareTask.Params, Integer, String> {
|
||||||
|
|
||||||
|
private final static int MAX_ATTEMPTS = 5;
|
||||||
|
private final static String FARE_URL = "http://api.bart.gov/api/sched.aspx?cmd=fare&date=today&key="
|
||||||
|
+ Constants.API_KEY + "&orig=%1$s&dest=%2$s";
|
||||||
|
|
||||||
|
private Exception mException;
|
||||||
|
|
||||||
|
private String fare;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String doInBackground(Params... paramsArray) {
|
||||||
|
Params params = paramsArray[0];
|
||||||
|
|
||||||
|
if (!isCancelled()) {
|
||||||
|
return getFareFromNetwork(params, 0);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFareFromNetwork(Params params, int attemptNumber) {
|
||||||
|
String xml = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
HttpUriRequest request = new HttpGet(
|
||||||
|
String.format(FARE_URL, params.origin.abbreviation,
|
||||||
|
params.destination.abbreviation));
|
||||||
|
|
||||||
|
FareContentHandler handler = new FareContentHandler();
|
||||||
|
if (isCancelled()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpResponse response = NetworkUtils.executeWithRecovery(request);
|
||||||
|
|
||||||
|
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
|
||||||
|
throw new IOException("Server returned "
|
||||||
|
+ response.getStatusLine().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
IOUtils.copy(response.getEntity().getContent(), writer, "UTF-8");
|
||||||
|
|
||||||
|
xml = writer.toString();
|
||||||
|
if (xml.length() == 0) {
|
||||||
|
throw new IOException("Server returned blank xml document");
|
||||||
|
}
|
||||||
|
|
||||||
|
Xml.parse(xml, handler);
|
||||||
|
fare = handler.getFare();
|
||||||
|
return fare;
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (attemptNumber < MAX_ATTEMPTS - 1) {
|
||||||
|
try {
|
||||||
|
Log.w(Constants.TAG,
|
||||||
|
"Attempt to contact server failed... retrying in 3s",
|
||||||
|
e);
|
||||||
|
Thread.sleep(3000);
|
||||||
|
} catch (InterruptedException interrupt) {
|
||||||
|
// Ignore... just go on to next attempt
|
||||||
|
}
|
||||||
|
return getFareFromNetwork(params, attemptNumber + 1);
|
||||||
|
} else {
|
||||||
|
mException = new Exception("Could not contact BART system", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (SAXException e) {
|
||||||
|
mException = new Exception(
|
||||||
|
"Could not understand response from BART system: " + xml, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Params {
|
||||||
|
public Params(Station origin, Station destination) {
|
||||||
|
super();
|
||||||
|
this.origin = origin;
|
||||||
|
this.destination = destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Station origin;
|
||||||
|
public final Station destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(String result) {
|
||||||
|
if (result != null) {
|
||||||
|
onResult(fare);
|
||||||
|
} else {
|
||||||
|
onError(mException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void onResult(String fare);
|
||||||
|
|
||||||
|
public abstract void onError(Exception exception);
|
||||||
|
|
||||||
|
}
|
38
src/com/dougkeen/bart/NetworkUtils.java
Normal file
38
src/com/dougkeen/bart/NetworkUtils.java
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package com.dougkeen.bart;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.client.ClientProtocolException;
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
import org.apache.http.client.methods.HttpUriRequest;
|
||||||
|
import org.apache.http.conn.params.ConnManagerParams;
|
||||||
|
import org.apache.http.impl.client.DefaultHttpClient;
|
||||||
|
import org.apache.http.params.HttpConnectionParams;
|
||||||
|
import org.apache.http.params.HttpParams;
|
||||||
|
|
||||||
|
public class NetworkUtils {
|
||||||
|
|
||||||
|
public static HttpClient getHttpClient() {
|
||||||
|
HttpClient client = new DefaultHttpClient();
|
||||||
|
final HttpParams params = client.getParams();
|
||||||
|
HttpConnectionParams.setConnectionTimeout(params,
|
||||||
|
NetworkUtils.CONNECTION_TIMEOUT_MILLIS);
|
||||||
|
HttpConnectionParams.setSoTimeout(params, NetworkUtils.CONNECTION_TIMEOUT_MILLIS);
|
||||||
|
ConnManagerParams.setTimeout(params, NetworkUtils.CONNECTION_TIMEOUT_MILLIS);
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HttpResponse executeWithRecovery(final HttpUriRequest request)
|
||||||
|
throws IOException, ClientProtocolException {
|
||||||
|
try {
|
||||||
|
return getHttpClient().execute(request);
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
// try again... this is a rare error
|
||||||
|
return getHttpClient().execute(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static final int CONNECTION_TIMEOUT_MILLIS = 10000;
|
||||||
|
|
||||||
|
}
|
@ -7,6 +7,8 @@ public class Route {
|
|||||||
private boolean requiresTransfer;
|
private boolean requiresTransfer;
|
||||||
private Station transferStation;
|
private Station transferStation;
|
||||||
private String direction;
|
private String direction;
|
||||||
|
private String fare;
|
||||||
|
private Long fareLastUpdated;
|
||||||
|
|
||||||
public Station getOrigin() {
|
public Station getOrigin() {
|
||||||
return origin;
|
return origin;
|
||||||
@ -56,6 +58,22 @@ public class Route {
|
|||||||
this.direction = direction;
|
this.direction = direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getFare() {
|
||||||
|
return fare;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFare(String fare) {
|
||||||
|
this.fare = fare;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getFareLastUpdated() {
|
||||||
|
return fareLastUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFareLastUpdated(Long fareLastUpdated) {
|
||||||
|
this.fareLastUpdated = fareLastUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
@ -71,6 +89,10 @@ public class Route {
|
|||||||
builder.append(transferStation);
|
builder.append(transferStation);
|
||||||
builder.append(", direction=");
|
builder.append(", direction=");
|
||||||
builder.append(direction);
|
builder.append(direction);
|
||||||
|
builder.append(", fare=");
|
||||||
|
builder.append(fare);
|
||||||
|
builder.append(", fareLastUpdated=");
|
||||||
|
builder.append(fareLastUpdated);
|
||||||
builder.append("]");
|
builder.append("]");
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
@ -90,8 +112,7 @@ public class Route {
|
|||||||
int lineDestinationIndex = viaLine.stations
|
int lineDestinationIndex = viaLine.stations
|
||||||
.indexOf(lineDestination);
|
.indexOf(lineDestination);
|
||||||
return routeDestinationIndex >= 0
|
return routeDestinationIndex >= 0
|
||||||
&& ((originIndex <= routeDestinationIndex && routeDestinationIndex <= lineDestinationIndex)
|
&& ((originIndex <= routeDestinationIndex && routeDestinationIndex <= lineDestinationIndex) || (originIndex >= routeDestinationIndex && routeDestinationIndex >= lineDestinationIndex));
|
||||||
|| (originIndex >= routeDestinationIndex && routeDestinationIndex >= lineDestinationIndex));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
package com.dougkeen.bart;
|
package com.dougkeen.bart;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
|
import android.app.AlertDialog.Builder;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.app.ListActivity;
|
import android.app.ListActivity;
|
||||||
import android.app.AlertDialog.Builder;
|
|
||||||
import android.content.ContentUris;
|
import android.content.ContentUris;
|
||||||
|
import android.content.ContentValues;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
@ -27,6 +31,9 @@ import com.dougkeen.bart.data.CursorUtils;
|
|||||||
import com.dougkeen.bart.data.RoutesColumns;
|
import com.dougkeen.bart.data.RoutesColumns;
|
||||||
|
|
||||||
public class RoutesListActivity extends ListActivity {
|
public class RoutesListActivity extends ListActivity {
|
||||||
|
private static final TimeZone PACIFIC_TIME = TimeZone
|
||||||
|
.getTimeZone("America/Los_Angeles");
|
||||||
|
|
||||||
private static final int DIALOG_DELETE_EVENT = 0;
|
private static final int DIALOG_DELETE_EVENT = 0;
|
||||||
|
|
||||||
protected Cursor mQuery;
|
protected Cursor mQuery;
|
||||||
@ -43,19 +50,31 @@ public class RoutesListActivity extends ListActivity {
|
|||||||
|
|
||||||
mQuery = managedQuery(Constants.FAVORITE_CONTENT_URI, new String[] {
|
mQuery = managedQuery(Constants.FAVORITE_CONTENT_URI, new String[] {
|
||||||
RoutesColumns._ID.string, RoutesColumns.FROM_STATION.string,
|
RoutesColumns._ID.string, RoutesColumns.FROM_STATION.string,
|
||||||
RoutesColumns.TO_STATION.string }, null, null,
|
RoutesColumns.TO_STATION.string, RoutesColumns.FARE.string,
|
||||||
|
RoutesColumns.FARE_LAST_UPDATED.string }, null, null,
|
||||||
RoutesColumns._ID.string);
|
RoutesColumns._ID.string);
|
||||||
|
|
||||||
|
refreshFares();
|
||||||
|
|
||||||
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
|
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
|
||||||
R.layout.favorite_listing, mQuery, new String[] {
|
R.layout.favorite_listing, mQuery, new String[] {
|
||||||
RoutesColumns.FROM_STATION.string,
|
RoutesColumns.FROM_STATION.string,
|
||||||
RoutesColumns.TO_STATION.string }, new int[] {
|
RoutesColumns.TO_STATION.string,
|
||||||
R.id.originText, R.id.destinationText });
|
RoutesColumns.FARE.string }, new int[] {
|
||||||
|
R.id.originText, R.id.destinationText, R.id.fareText });
|
||||||
adapter.setViewBinder(new ViewBinder() {
|
adapter.setViewBinder(new ViewBinder() {
|
||||||
public boolean setViewValue(View view, Cursor cursor,
|
public boolean setViewValue(View view, Cursor cursor,
|
||||||
int columnIndex) {
|
int columnIndex) {
|
||||||
|
if (view.getId() == R.id.fareText) {
|
||||||
|
String fare = cursor.getString(columnIndex);
|
||||||
|
if (fare != null) {
|
||||||
|
((TextView) view).setSingleLine(false);
|
||||||
|
((TextView) view).setText("Fare:\n" + fare);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
((TextView) view).setText(Station.getByAbbreviation(cursor
|
((TextView) view).setText(Station.getByAbbreviation(cursor
|
||||||
.getString(columnIndex)).name);
|
.getString(columnIndex)).name);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -65,6 +84,52 @@ public class RoutesListActivity extends ListActivity {
|
|||||||
registerForContextMenu(getListView());
|
registerForContextMenu(getListView());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void refreshFares() {
|
||||||
|
if (mQuery.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
final Station orig = Station.getByAbbreviation(CursorUtils
|
||||||
|
.getString(mQuery, RoutesColumns.FROM_STATION));
|
||||||
|
final Station dest = Station.getByAbbreviation(CursorUtils
|
||||||
|
.getString(mQuery, RoutesColumns.TO_STATION));
|
||||||
|
final Long id = CursorUtils.getLong(mQuery, RoutesColumns._ID);
|
||||||
|
final Long lastUpdateMillis = CursorUtils.getLong(mQuery,
|
||||||
|
RoutesColumns.FARE_LAST_UPDATED);
|
||||||
|
|
||||||
|
Calendar now = Calendar.getInstance();
|
||||||
|
Calendar lastUpdate = Calendar.getInstance();
|
||||||
|
lastUpdate.setTimeInMillis(lastUpdateMillis);
|
||||||
|
|
||||||
|
now.setTimeZone(PACIFIC_TIME);
|
||||||
|
lastUpdate.setTimeZone(PACIFIC_TIME);
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
|
||||||
|
getContentResolver()
|
||||||
|
.update(ContentUris.withAppendedId(
|
||||||
|
Constants.FAVORITE_CONTENT_URI, id),
|
||||||
|
values, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Exception exception) {
|
||||||
|
// Ignore... we can do this later
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fareTask.execute(new GetRouteFareTask.Params(orig, dest));
|
||||||
|
}
|
||||||
|
} while (mQuery.moveToNext());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
@ -72,6 +137,7 @@ public class RoutesListActivity extends ListActivity {
|
|||||||
.setText(R.string.favorite_routes);
|
.setText(R.string.favorite_routes);
|
||||||
((TextView) findViewById(android.R.id.empty))
|
((TextView) findViewById(android.R.id.empty))
|
||||||
.setText(R.string.empty_favorites_list_message);
|
.setText(R.string.empty_favorites_list_message);
|
||||||
|
refreshFares();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -41,6 +41,10 @@ public class BartContentProvider extends ContentProvider {
|
|||||||
RoutesColumns.FROM_STATION.string);
|
RoutesColumns.FROM_STATION.string);
|
||||||
sFavoritesProjectionMap.put(RoutesColumns.TO_STATION.string,
|
sFavoritesProjectionMap.put(RoutesColumns.TO_STATION.string,
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DatabaseHelper mDatabaseHelper;
|
private DatabaseHelper mDatabaseHelper;
|
||||||
@ -65,8 +69,7 @@ public class BartContentProvider extends ContentProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Cursor query(Uri uri, String[] projection, String selection,
|
public Cursor query(Uri uri, String[] projection, String selection,
|
||||||
String[] selectionArgs,
|
String[] selectionArgs, String sortOrder) {
|
||||||
String sortOrder) {
|
|
||||||
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
|
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
|
||||||
|
|
||||||
SQLiteDatabase db = mDatabaseHelper.getReadableDatabase();
|
SQLiteDatabase db = mDatabaseHelper.getReadableDatabase();
|
||||||
@ -104,8 +107,6 @@ public class BartContentProvider extends ContentProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Uri insert(Uri uri, ContentValues initialValues) {
|
public Uri insert(Uri uri, ContentValues initialValues) {
|
||||||
// TODO: Hook this up to the REST service?
|
|
||||||
|
|
||||||
ContentValues values;
|
ContentValues values;
|
||||||
if (initialValues != null) {
|
if (initialValues != null) {
|
||||||
values = new ContentValues(initialValues);
|
values = new ContentValues(initialValues);
|
||||||
@ -127,9 +128,7 @@ public class BartContentProvider extends ContentProvider {
|
|||||||
new String[] {
|
new String[] {
|
||||||
values.getAsString(RoutesColumns.FROM_STATION.string),
|
values.getAsString(RoutesColumns.FROM_STATION.string),
|
||||||
values.getAsString(RoutesColumns.TO_STATION.string) },
|
values.getAsString(RoutesColumns.TO_STATION.string) },
|
||||||
null,
|
null, null, null);
|
||||||
null,
|
|
||||||
null);
|
|
||||||
try {
|
try {
|
||||||
if (cursor.moveToFirst()) {
|
if (cursor.moveToFirst()) {
|
||||||
rowId = cursor.getLong(0);
|
rowId = cursor.getLong(0);
|
||||||
@ -158,8 +157,24 @@ public class BartContentProvider extends ContentProvider {
|
|||||||
@Override
|
@Override
|
||||||
public int update(Uri uri, ContentValues values, String where,
|
public int update(Uri uri, ContentValues values, String where,
|
||||||
String[] whereArgs) {
|
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;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
// No updating supported yet
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -173,8 +188,10 @@ public class BartContentProvider extends ContentProvider {
|
|||||||
whereArgs);
|
whereArgs);
|
||||||
} else if (match == FAVORITE_ID) {
|
} else if (match == FAVORITE_ID) {
|
||||||
String favoriteId = uri.getPathSegments().get(1);
|
String favoriteId = uri.getPathSegments().get(1);
|
||||||
count = db.delete(DatabaseHelper.FAVORITES_TABLE_NAME,
|
count = db.delete(
|
||||||
RoutesColumns._ID + " = "
|
DatabaseHelper.FAVORITES_TABLE_NAME,
|
||||||
|
RoutesColumns._ID
|
||||||
|
+ " = "
|
||||||
+ favoriteId
|
+ favoriteId
|
||||||
+ (!TextUtils.isEmpty(where) ? " AND (" + where
|
+ (!TextUtils.isEmpty(where) ? " AND (" + where
|
||||||
+ ')' : ""), whereArgs);
|
+ ')' : ""), whereArgs);
|
||||||
|
@ -17,4 +17,7 @@ public final class CursorUtils {
|
|||||||
return cursor.getString(cursor.getColumnIndex(column.string));
|
return cursor.getString(cursor.getColumnIndex(column.string));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final Long getLong(Cursor cursor, RoutesColumns column) {
|
||||||
|
return cursor.getLong(cursor.getColumnIndex(column.string));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,21 @@
|
|||||||
package com.dougkeen.bart.data;
|
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.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
public class DatabaseHelper extends SQLiteOpenHelper {
|
public class DatabaseHelper extends SQLiteOpenHelper {
|
||||||
|
|
||||||
private static final String DATABASE_NAME = "bart.dougkeen.db";
|
private static final String DATABASE_NAME = "bart.dougkeen.db";
|
||||||
private static final int DATABASE_VERSION = 1;
|
private static final int DATABASE_VERSION = 2;
|
||||||
|
|
||||||
public static final String FAVORITES_TABLE_NAME = "Favorites";
|
public static final String FAVORITES_TABLE_NAME = "Favorites";
|
||||||
|
|
||||||
@ -17,13 +25,72 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(SQLiteDatabase db) {
|
public void onCreate(SQLiteDatabase db) {
|
||||||
db.execSQL("CREATE TABLE " + FAVORITES_TABLE_NAME + " (" +
|
createFavoritesTable(db);
|
||||||
RoutesColumns._ID.getColumnDef() + " PRIMARY KEY, " +
|
}
|
||||||
RoutesColumns.FROM_STATION.getColumnDef() + ", " +
|
|
||||||
RoutesColumns.TO_STATION.getColumnDef() + ");");
|
private void createFavoritesTable(SQLiteDatabase db) {
|
||||||
|
db.execSQL("CREATE TABLE IF NOT EXISTS " + FAVORITES_TABLE_NAME + " ("
|
||||||
|
+ RoutesColumns._ID.getColumnDef() + " PRIMARY KEY, "
|
||||||
|
+ RoutesColumns.FROM_STATION.getColumnDef() + ", "
|
||||||
|
+ RoutesColumns.TO_STATION.getColumnDef() + ", "
|
||||||
|
+ RoutesColumns.FARE.getColumnDef() + ", "
|
||||||
|
+ RoutesColumns.FARE_LAST_UPDATED.getColumnDef() + ");");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
|
db.beginTransaction();
|
||||||
|
try {
|
||||||
|
createFavoritesTable(db);
|
||||||
|
|
||||||
|
List<String> columns = getColumns(db, FAVORITES_TABLE_NAME);
|
||||||
|
|
||||||
|
db.execSQL("ALTER TABLE " + FAVORITES_TABLE_NAME
|
||||||
|
+ " RENAME TO temp_" + FAVORITES_TABLE_NAME);
|
||||||
|
|
||||||
|
createFavoritesTable(db);
|
||||||
|
|
||||||
|
columns.retainAll(getColumns(db, FAVORITES_TABLE_NAME));
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
db.execSQL("DROP TABLE temp_" + 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,25 @@
|
|||||||
package com.dougkeen.bart.data;
|
package com.dougkeen.bart.data;
|
||||||
|
|
||||||
public enum RoutesColumns {
|
public enum RoutesColumns {
|
||||||
_ID("_id", "INTEGER"),
|
_ID("_id", "INTEGER", false),
|
||||||
FROM_STATION("FROM_STATION", "TEXT"),
|
FROM_STATION("FROM_STATION", "TEXT", false),
|
||||||
TO_STATION("TO_STATION", "TEXT");
|
TO_STATION("TO_STATION", "TEXT", false),
|
||||||
|
FARE("FARE", "TEXT", true),
|
||||||
|
FARE_LAST_UPDATED("FARE_LAST_UPDATED", "INTEGER", true);
|
||||||
|
|
||||||
|
|
||||||
// This class cannot be instantiated
|
// This class cannot be instantiated
|
||||||
private RoutesColumns(String string, String type) {
|
private RoutesColumns(String string, String type, Boolean nullable) {
|
||||||
this.string = string;
|
this.string = string;
|
||||||
this.sqliteType = type;
|
this.sqliteType = type;
|
||||||
|
this.nullable = nullable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final String string;
|
public final String string;
|
||||||
public final String sqliteType;
|
public final String sqliteType;
|
||||||
|
public final Boolean nullable;
|
||||||
|
|
||||||
protected String getColumnDef() {
|
protected String getColumnDef() {
|
||||||
return string + " " + sqliteType;
|
return string + " " + sqliteType + (nullable?"":" NOT NULL");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user