Implemented service alerts & elevator info

If bikes aren't allowed, now shows specific icon
Introduced Android Annotations
This commit is contained in:
Doug Keen 2013-01-29 08:31:18 -08:00
parent 3f617d48f0
commit f6efac2af6
32 changed files with 603 additions and 75 deletions

View File

@ -7,5 +7,10 @@
<classpathentry kind="lib" path="libs/commons-lang3-3.1.jar"/> <classpathentry kind="lib" path="libs/commons-lang3-3.1.jar"/>
<classpathentry kind="lib" path="libs/android-support-v4.jar"/> <classpathentry kind="lib" path="libs/android-support-v4.jar"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/> <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry kind="src" path=".apt_generated">
<attributes>
<attribute name="optional" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="bin/classes"/> <classpathentry kind="output" path="bin/classes"/>
</classpath> </classpath>

3
.factorypath Normal file
View File

@ -0,0 +1,3 @@
<factorypath>
<factorypathentry kind="WKSPJAR" id="/BARTRunner/compile-libs/androidannotations-2.7.jar" enabled="true" runInBatchMode="false"/>
</factorypath>

View File

@ -0,0 +1,4 @@
eclipse.preferences.version=1
org.eclipse.jdt.apt.aptEnabled=true
org.eclipse.jdt.apt.genSrcDir=.apt_generated
org.eclipse.jdt.apt.reconcileEnabled=true

View File

@ -1,4 +1,12 @@
eclipse.preferences.version=1 eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.6 org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.processAnnotations=enabled
org.eclipse.jdt.core.compiler.source=1.6 org.eclipse.jdt.core.compiler.source=1.6

View File

@ -19,7 +19,7 @@
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/AppTheme" > android:theme="@style/AppTheme" >
<activity <activity
android:name=".activities.RoutesListActivity" android:name=".activities.RoutesListActivity_"
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name" > android:label="@string/app_name" >
<intent-filter> <intent-filter>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
res/drawable-hdpi/ic_allgood.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 B

BIN
res/drawable-hdpi/ic_warn.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 849 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 682 B

BIN
res/drawable-mdpi/ic_allgood.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

BIN
res/drawable-mdpi/ic_warn.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
res/drawable-xhdpi/ic_allgood.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 B

BIN
res/drawable-xhdpi/ic_warn.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
res/drawable/nobike.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -28,8 +28,7 @@
<ImageView <ImageView
android:id="@+id/bikeIcon" android:id="@+id/bikeIcon"
style="@style/BikeIcon" style="@style/BikeIcon" />
android:src="@drawable/bike" />
<com.dougkeen.bart.controls.CountdownTextView <com.dougkeen.bart.controls.CountdownTextView
android:id="@+id/countdown" android:id="@+id/countdown"

View File

@ -31,6 +31,13 @@
android:layout_gravity="center" /> android:layout_gravity="center" />
</FrameLayout> </FrameLayout>
<TextView
android:id="@+id/alertMessages"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_warn"
android:visibility="gone" />
<FrameLayout <FrameLayout
style="ButtonBar" style="ButtonBar"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="56dp"
android:layout_height="45dp" >
<ProgressBar
style="?android:attr/progressBarStyleSmallInverse"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center" />
</FrameLayout>

View File

@ -45,8 +45,7 @@
android:id="@+id/yourTrainBikeIcon" android:id="@+id/yourTrainBikeIcon"
style="@style/BikeIcon" style="@style/BikeIcon"
android:layout_below="@id/yourTrainHeader" android:layout_below="@id/yourTrainHeader"
android:layout_toRightOf="@id/yourTrainDestinationText" android:layout_toRightOf="@id/yourTrainDestinationText" />
android:src="@drawable/bike" />
<ImageView <ImageView
android:id="@+id/yourTrainXferIcon" android:id="@+id/yourTrainXferIcon"

View File

@ -13,5 +13,11 @@
android:showAsAction="ifRoom|withText" android:showAsAction="ifRoom|withText"
android:title="@string/view_system_map"> android:title="@string/view_system_map">
</item> </item>
<item
android:id="@+id/elevator_button"
android:icon="@drawable/ic_action_elevator"
android:showAsAction="ifRoom|withText"
android:title="Check elevator status">
</item>
</menu> </menu>

View File

@ -13,14 +13,17 @@ import android.os.Bundle;
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;
import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.Button; import android.widget.Button;
import android.widget.ListAdapter; import android.widget.ListAdapter;
import com.WazaBe.HoloEverywhere.app.AlertDialog; import com.WazaBe.HoloEverywhere.app.AlertDialog;
import com.WazaBe.HoloEverywhere.app.AlertDialog.Builder;
import com.WazaBe.HoloEverywhere.app.DialogFragment; import com.WazaBe.HoloEverywhere.app.DialogFragment;
import com.WazaBe.HoloEverywhere.sherlock.SActivity; import com.WazaBe.HoloEverywhere.sherlock.SActivity;
import com.WazaBe.HoloEverywhere.widget.ListView;
import com.WazaBe.HoloEverywhere.widget.TextView; import com.WazaBe.HoloEverywhere.widget.TextView;
import com.actionbarsherlock.view.ActionMode; import com.actionbarsherlock.view.ActionMode;
import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.Menu;
@ -28,16 +31,33 @@ import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem; import com.actionbarsherlock.view.MenuItem;
import com.dougkeen.bart.R; import com.dougkeen.bart.R;
import com.dougkeen.bart.controls.Ticker; import com.dougkeen.bart.controls.Ticker;
import com.dougkeen.bart.controls.Ticker.TickSubscriber;
import com.dougkeen.bart.data.CursorUtils; import com.dougkeen.bart.data.CursorUtils;
import com.dougkeen.bart.data.FavoritesArrayAdapter; import com.dougkeen.bart.data.FavoritesArrayAdapter;
import com.dougkeen.bart.data.RoutesColumns; 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.Constants;
import com.dougkeen.bart.model.Station; import com.dougkeen.bart.model.Station;
import com.dougkeen.bart.model.StationPair; import com.dougkeen.bart.model.StationPair;
import com.dougkeen.bart.networktasks.AlertsClient;
import com.dougkeen.bart.networktasks.ElevatorClient;
import com.dougkeen.bart.networktasks.GetRouteFareTask; import com.dougkeen.bart.networktasks.GetRouteFareTask;
import com.googlecode.androidannotations.annotations.AfterViews;
import com.googlecode.androidannotations.annotations.Background;
import com.googlecode.androidannotations.annotations.Click;
import com.googlecode.androidannotations.annotations.EActivity;
import com.googlecode.androidannotations.annotations.ItemClick;
import com.googlecode.androidannotations.annotations.ItemLongClick;
import com.googlecode.androidannotations.annotations.UiThread;
import com.googlecode.androidannotations.annotations.ViewById;
import com.googlecode.androidannotations.annotations.rest.RestService;
@EActivity(R.layout.main)
public class RoutesListActivity extends SActivity implements public class RoutesListActivity extends SActivity implements
LoaderCallbacks<Cursor> { LoaderCallbacks<Cursor>, TickSubscriber {
private static final String NO_DELAYS_REPORTED = "No delays reported";
private static final int FAVORITES_LOADER_ID = 0; private static final int FAVORITES_LOADER_ID = 0;
private static final TimeZone PACIFIC_TIME = TimeZone private static final TimeZone PACIFIC_TIME = TimeZone
@ -52,11 +72,52 @@ public class RoutesListActivity extends SActivity implements
private FavoritesArrayAdapter mRoutesAdapter; private FavoritesArrayAdapter mRoutesAdapter;
/** Called when the activity is first created. */ @RestService
@Override AlertsClient alertsClient;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); @RestService
setContentView(R.layout.main); ElevatorClient elevatorClient;
@ViewById(android.R.id.list)
ListView listView;
@ViewById(R.id.quickLookupButton)
Button quickLookupButton;
@ViewById(R.id.alertMessages)
TextView alertMessages;
@Click(R.id.quickLookupButton)
void quickLookupButtonClick() {
DialogFragment dialog = new QuickRouteDialogFragment(
getString(R.string.quick_departure_lookup));
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())));
}
@ItemLongClick(android.R.id.list)
void listItemLongClick(StationPair item) {
if (mActionMode != null) {
mActionMode.finish();
}
mCurrentlySelectedUri = ContentUris.withAppendedId(
Constants.FAVORITE_CONTENT_URI, item.getId());
mCurrentlySelectedOrigin = item.getOrigin();
mCurrentlySelectedDestination = item.getDestination();
startContextualActionMode();
}
@AfterViews
void afterViews() {
setTitle(R.string.favorite_routes); setTitle(R.string.favorite_routes);
mRoutesAdapter = new FavoritesArrayAdapter(this, mRoutesAdapter = new FavoritesArrayAdapter(this,
@ -65,52 +126,18 @@ public class RoutesListActivity extends SActivity implements
getSupportLoaderManager().initLoader(FAVORITES_LOADER_ID, null, this); getSupportLoaderManager().initLoader(FAVORITES_LOADER_ID, null, this);
setListAdapter(mRoutesAdapter); setListAdapter(mRoutesAdapter);
getListView().setOnItemClickListener(
new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> l, View v,
int position, long id) {
;
startActivity(new Intent(Intent.ACTION_VIEW,
ContentUris.withAppendedId(
Constants.FAVORITE_CONTENT_URI,
getListAdapter().getItem(position)
.getId())));
}
});
getListView().setEmptyView(findViewById(android.R.id.empty));
getListView().setOnItemLongClickListener(
new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent,
View view, int position, long id) {
if (mActionMode != null) {
mActionMode.finish();
}
StationPair item = getListAdapter().getItem(position); listView.setEmptyView(findViewById(android.R.id.empty));
mCurrentlySelectedUri = ContentUris.withAppendedId( if (mCurrentAlerts != null) {
Constants.FAVORITE_CONTENT_URI, item.getId()); showAlertMessage(mCurrentAlerts);
}
}
mCurrentlySelectedOrigin = item.getOrigin(); /** Called when the activity is first created. */
mCurrentlySelectedDestination = item.getDestination(); @Override
public void onCreate(Bundle savedInstanceState) {
startContextualActionMode(); super.onCreate(savedInstanceState);
return true;
}
});
((Button) findViewById(R.id.quickLookupButton))
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
DialogFragment dialog = new QuickRouteDialogFragment(
getString(R.string.quick_departure_lookup));
dialog.show(getSupportFragmentManager()
.beginTransaction());
}
});
if (savedInstanceState != null) { if (savedInstanceState != null) {
if (savedInstanceState.getString("currentlySelectedOrigin") != null) { if (savedInstanceState.getString("currentlySelectedOrigin") != null) {
@ -130,7 +157,10 @@ public class RoutesListActivity extends SActivity implements
if (savedInstanceState.getBoolean("hasActionMode")) { if (savedInstanceState.getBoolean("hasActionMode")) {
startContextualActionMode(); startContextualActionMode();
} }
mCurrentAlerts = savedInstanceState.getString("currentAlerts");
} }
Ticker.getInstance().addSubscriber(this, getApplicationContext());
} }
@Override @Override
@ -162,9 +192,8 @@ public class RoutesListActivity extends SActivity implements
// Nothing to do // Nothing to do
} }
@SuppressWarnings("unchecked")
private AdapterView<ListAdapter> getListView() { private AdapterView<ListAdapter> getListView() {
return (AdapterView<ListAdapter>) findViewById(android.R.id.list); return listView;
} }
protected FavoritesArrayAdapter getListAdapter() { protected FavoritesArrayAdapter getListAdapter() {
@ -233,6 +262,7 @@ public class RoutesListActivity extends SActivity implements
mCurrentlySelectedDestination.abbreviation); mCurrentlySelectedDestination.abbreviation);
outState.putParcelable("currentlySelectedUri", mCurrentlySelectedUri); outState.putParcelable("currentlySelectedUri", mCurrentlySelectedUri);
outState.putBoolean("hasActionMode", mActionMode != null); outState.putBoolean("hasActionMode", mActionMode != null);
outState.putString("currentAlerts", mCurrentAlerts);
} }
@Override @Override
@ -281,6 +311,11 @@ public class RoutesListActivity extends SActivity implements
return super.onCreateOptionsMenu(menu); return super.onCreateOptionsMenu(menu);
} }
private MenuItem elevatorMenuItem;
private View origElevatorActionView;
private String mCurrentAlerts;
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId(); int itemId = item.getItemId();
if (itemId == R.id.add_favorite_menu_button) { if (itemId == R.id.add_favorite_menu_button) {
@ -290,11 +325,86 @@ public class RoutesListActivity extends SActivity implements
} else if (itemId == R.id.view_system_map_button) { } else if (itemId == R.id.view_system_map_button) {
startActivity(new Intent(this, ViewMapActivity.class)); startActivity(new Intent(this, ViewMapActivity.class));
return true; return true;
} else if (itemId == R.id.elevator_button) {
elevatorMenuItem = item;
fetchElevatorInfo();
origElevatorActionView = elevatorMenuItem.getActionView();
elevatorMenuItem.setActionView(R.layout.progress_spinner);
return true;
} else { } else {
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
} }
@Background
void fetchAlerts() {
Log.d(TAG, "Fetching alerts");
AlertList alertList = alertsClient.getAlerts();
if (alertList.hasAlerts()) {
StringBuilder alertText = new StringBuilder();
boolean firstAlert = true;
for (Alert alert : alertList.getAlerts()) {
if (!firstAlert) {
alertText.append("\n\n");
}
alertText.append(alert.getPostedTime()).append("\n");
alertText.append(alert.getDescription());
firstAlert = false;
}
showAlertMessage(alertText.toString());
} else if (alertList.areNoDelaysReported()) {
showAlertMessage(NO_DELAYS_REPORTED);
} else {
hideAlertMessage();
}
}
@UiThread
void hideAlertMessage() {
mCurrentAlerts = null;
alertMessages.setVisibility(View.GONE);
}
@UiThread
void showAlertMessage(String messageText) {
if (messageText == null) {
hideAlertMessage();
return;
} else if (messageText == NO_DELAYS_REPORTED) {
alertMessages.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.ic_allgood, 0, 0, 0);
} else {
alertMessages.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.ic_warn, 0, 0, 0);
}
mCurrentAlerts = messageText;
alertMessages.setText(messageText);
alertMessages.setVisibility(View.VISIBLE);
}
@Background
void fetchElevatorInfo() {
String elevatorMessage = elevatorClient.getElevatorMessage();
if (elevatorMessage != null) {
showElevatorMessage(elevatorMessage);
}
resetElevatorMenuGraphic();
}
@UiThread
void resetElevatorMenuGraphic() {
invalidateOptionsMenu();
elevatorMenuItem.setActionView(origElevatorActionView);
}
@UiThread
void showElevatorMessage(String message) {
Builder builder = new AlertDialog.Builder(this);
builder.setMessage(message);
builder.setTitle("Elevator status");
builder.show();
}
private void startContextualActionMode() { private void startContextualActionMode() {
mActionMode = startActionMode(new RouteActionMode()); mActionMode = startActionMode(new RouteActionMode());
mActionMode.setTitle(mCurrentlySelectedOrigin.name); mActionMode.setTitle(mCurrentlySelectedOrigin.name);
@ -358,4 +468,14 @@ public class RoutesListActivity extends SActivity implements
} }
} }
@Override
public int getTickInterval() {
return 90;
}
@Override
public void onTick(long mTickCount) {
fetchAlerts();
}
} }

View File

@ -119,12 +119,13 @@ public class YourTrainLayout extends RelativeLayout implements Checkable {
ImageView colorBar = (ImageView) findViewById(R.id.yourTrainDestinationColorBar); ImageView colorBar = (ImageView) findViewById(R.id.yourTrainDestinationColorBar);
((GradientDrawable) colorBar.getDrawable()).setColor(Color ((GradientDrawable) colorBar.getDrawable()).setColor(Color
.parseColor(boardedDeparture.getTrainDestinationColor())); .parseColor(boardedDeparture.getTrainDestinationColor()));
ImageView bikeIcon = (ImageView) findViewById(R.id.yourTrainBikeIcon);
if (boardedDeparture.isBikeAllowed()) { if (boardedDeparture.isBikeAllowed()) {
((ImageView) findViewById(R.id.yourTrainBikeIcon)) bikeIcon.setImageDrawable(getResources().getDrawable(
.setVisibility(View.VISIBLE); R.drawable.bike));
} else { } else {
((ImageView) findViewById(R.id.yourTrainBikeIcon)) bikeIcon.setImageDrawable(getResources().getDrawable(
.setVisibility(View.INVISIBLE); R.drawable.nobike));
} }
if (boardedDeparture.getRequiresTransfer()) { if (boardedDeparture.getRequiresTransfer()) {
((ImageView) findViewById(R.id.yourTrainXferIcon)) ((ImageView) findViewById(R.id.yourTrainXferIcon))

View File

@ -6,6 +6,7 @@ import org.apache.commons.lang3.StringUtils;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.GradientDrawable;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -27,33 +28,48 @@ import com.dougkeen.bart.model.TextProvider;
public class DepartureArrayAdapter extends ArrayAdapter<Departure> { public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
private Drawable noBikeDrawable;
private Drawable bikeDrawable;
public DepartureArrayAdapter(Context context, int textViewResourceId, public DepartureArrayAdapter(Context context, int textViewResourceId,
Departure[] objects) { Departure[] objects) {
super(context, textViewResourceId, objects); super(context, textViewResourceId, objects);
assignBikeDrawables();
}
private void assignBikeDrawables() {
noBikeDrawable = getContext().getResources().getDrawable(
R.drawable.nobike);
bikeDrawable = getContext().getResources().getDrawable(R.drawable.bike);
} }
public DepartureArrayAdapter(Context context, int resource, public DepartureArrayAdapter(Context context, int resource,
int textViewResourceId, Departure[] objects) { int textViewResourceId, Departure[] objects) {
super(context, resource, textViewResourceId, objects); super(context, resource, textViewResourceId, objects);
assignBikeDrawables();
} }
public DepartureArrayAdapter(Context context, int resource, public DepartureArrayAdapter(Context context, int resource,
int textViewResourceId, List<Departure> objects) { int textViewResourceId, List<Departure> objects) {
super(context, resource, textViewResourceId, objects); super(context, resource, textViewResourceId, objects);
assignBikeDrawables();
} }
public DepartureArrayAdapter(Context context, int resource, public DepartureArrayAdapter(Context context, int resource,
int textViewResourceId) { int textViewResourceId) {
super(context, resource, textViewResourceId); super(context, resource, textViewResourceId);
assignBikeDrawables();
} }
public DepartureArrayAdapter(Context context, int textViewResourceId, public DepartureArrayAdapter(Context context, int textViewResourceId,
List<Departure> objects) { List<Departure> objects) {
super(context, textViewResourceId, objects); super(context, textViewResourceId, objects);
assignBikeDrawables();
} }
public DepartureArrayAdapter(Context context, int textViewResourceId) { public DepartureArrayAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId); super(context, textViewResourceId);
assignBikeDrawables();
} }
@Override @Override
@ -135,12 +151,11 @@ public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
} }
}); });
ImageView bikeIcon = (ImageView) view.findViewById(R.id.bikeIcon);
if (departure.isBikeAllowed()) { if (departure.isBikeAllowed()) {
((ImageView) view.findViewById(R.id.bikeIcon)) bikeIcon.setImageDrawable(bikeDrawable);
.setVisibility(View.VISIBLE);
} else { } else {
((ImageView) view.findViewById(R.id.bikeIcon)) bikeIcon.setImageDrawable(noBikeDrawable);
.setVisibility(View.INVISIBLE);
} }
if (departure.getRequiresTransfer()) { if (departure.getRequiresTransfer()) {
((ImageView) view.findViewById(R.id.xferIcon)) ((ImageView) view.findViewById(R.id.xferIcon))

View File

@ -0,0 +1,89 @@
package com.dougkeen.bart.model;
import java.util.ArrayList;
import java.util.List;
public class Alert {
private String id;
private String type;
private String description;
private String postedTime;
private String expiresTime;
public Alert(String id) {
super();
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getPostedTime() {
return postedTime;
}
public void setPostedTime(String postedTime) {
this.postedTime = postedTime;
}
public String getExpiresTime() {
return expiresTime;
}
public void setExpiresTime(String expiresTime) {
this.expiresTime = expiresTime;
}
public static class AlertList {
private List<Alert> alerts;
private boolean noDelaysReported;
public List<Alert> getAlerts() {
if (alerts == null) {
alerts = new ArrayList<Alert>();
}
return alerts;
}
public void addAlert(Alert alert) {
getAlerts().add(alert);
}
public void clear() {
getAlerts().clear();
}
public boolean hasAlerts() {
return !getAlerts().isEmpty();
}
public boolean areNoDelaysReported() {
return noDelaysReported;
}
public void setNoDelaysReported(boolean noDelaysReported) {
this.noDelaysReported = noDelaysReported;
}
}
}

View File

@ -0,0 +1,139 @@
package com.dougkeen.bart.networktasks;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import android.util.Log;
import android.util.Xml;
import com.dougkeen.bart.model.Alert;
public class AlertListConverter extends
AbstractHttpMessageConverter<Alert.AlertList> {
@Override
protected Alert.AlertList readInternal(
Class<? extends Alert.AlertList> clazz,
HttpInputMessage inputMessage) throws IOException,
HttpMessageNotReadableException {
StringWriter writer = new StringWriter();
IOUtils.copy(inputMessage.getBody(), writer, "UTF-8");
String xml = writer.toString();
if (xml.length() == 0) {
throw new IOException("Server returned blank xml document");
}
AlertListHandler handler = new AlertListHandler();
try {
Xml.parse(xml, handler);
} catch (SAXException e) {
Log.e("AlertListConverter", "XML parsing error", e);
return null;
}
return handler.getAlertList();
}
@Override
protected boolean supports(Class<?> clazz) {
return Alert.AlertList.class.equals(clazz);
}
@Override
public List<MediaType> getSupportedMediaTypes() {
final List<MediaType> supportedMediaTypes = new ArrayList<MediaType>();
supportedMediaTypes.add(MediaType.TEXT_HTML);
supportedMediaTypes.addAll(super.getSupportedMediaTypes());
return supportedMediaTypes;
}
@Override
protected void writeInternal(Alert.AlertList arg0, HttpOutputMessage arg1)
throws IOException, HttpMessageNotWritableException {
throw new UnsupportedOperationException();
}
private static class AlertListHandler extends DefaultHandler {
private final static List<String> TAGS = Arrays.asList("bsa", "type",
"description", "posted", "expires");
private String currentValue;
private boolean isParsingTag;
private Alert currentAlert;
private Alert.AlertList returnList = new Alert.AlertList();
public Alert.AlertList getAlertList() {
return returnList;
}
@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 (TAGS.contains(localName)) {
isParsingTag = true;
}
final int numberOfAttributes = attributes.getLength();
if (localName.equals("bsa")) {
for (int i = 0; i < numberOfAttributes; i++) {
if (attributes.getLocalName(i).equalsIgnoreCase("id")) {
currentAlert = new Alert(attributes.getValue(i));
break;
}
}
}
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
if (currentAlert != null) {
if (localName.equals("type")) {
currentAlert.setType(currentValue);
} else if (localName.equals("description")) {
currentAlert.setDescription(currentValue);
} else if (localName.equals("posted")) {
currentAlert.setPostedTime(currentValue);
} else if (localName.equals("expires")) {
currentAlert.setExpiresTime(currentValue);
} else if (localName.equals("bsa")) {
returnList.addAlert(currentAlert);
currentAlert = null;
}
}
isParsingTag = false;
currentValue = null;
}
@Override
public void endDocument() throws SAXException {
super.endDocument();
if (!returnList.hasAlerts()) {
returnList.setNoDelaysReported(true);
}
}
}
}

View File

@ -0,0 +1,12 @@
package com.dougkeen.bart.networktasks;
import com.dougkeen.bart.model.Alert;
import com.dougkeen.bart.model.Constants;
import com.googlecode.androidannotations.annotations.rest.Get;
import com.googlecode.androidannotations.annotations.rest.Rest;
@Rest(rootUrl = "http://api.bart.gov", converters = { AlertListConverter.class })
public interface AlertsClient {
@Get("/api/bsa.aspx?cmd=bsa&key=" + Constants.API_KEY)
Alert.AlertList getAlerts();
}

View File

@ -0,0 +1,11 @@
package com.dougkeen.bart.networktasks;
import com.dougkeen.bart.model.Constants;
import com.googlecode.androidannotations.annotations.rest.Get;
import com.googlecode.androidannotations.annotations.rest.Rest;
@Rest(rootUrl = "http://api.bart.gov", converters = { ElevatorMessageConverter.class })
public interface ElevatorClient {
@Get("/api/bsa.aspx?cmd=elev&key=" + Constants.API_KEY)
String getElevatorMessage();
}

View File

@ -0,0 +1,98 @@
package com.dougkeen.bart.networktasks;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import android.util.Log;
import android.util.Xml;
public class ElevatorMessageConverter extends
AbstractHttpMessageConverter<String> {
private static final String TAG = "ElevatorMessageConverter";
@Override
protected String readInternal(Class<? extends String> clazz,
HttpInputMessage inputMessage) throws IOException,
HttpMessageNotReadableException {
final ElevatorMessageHandler handler = new ElevatorMessageHandler();
try {
Xml.parse(new InputStreamReader(inputMessage.getBody()), handler);
} catch (SAXException e) {
Log.e(TAG, "Unable to parse elevator message", e);
return null;
}
return handler.getMessage();
}
@Override
protected boolean supports(Class<?> arg0) {
return String.class.equals(arg0);
}
@Override
public List<MediaType> getSupportedMediaTypes() {
final List<MediaType> supportedMediaTypes = new ArrayList<MediaType>();
supportedMediaTypes.add(MediaType.TEXT_HTML);
supportedMediaTypes.addAll(super.getSupportedMediaTypes());
return supportedMediaTypes;
}
@Override
protected void writeInternal(String arg0, HttpOutputMessage arg1)
throws IOException, HttpMessageNotWritableException {
throw new UnsupportedOperationException();
}
private static class ElevatorMessageHandler extends DefaultHandler {
private String currentValue;
private boolean isParsingTag;
private String message;
public String getMessage() {
return message;
}
@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 ("description".equals(localName)) {
isParsingTag = true;
}
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
if ("description".equals(localName)) {
message = currentValue;
}
isParsingTag = false;
currentValue = null;
}
}
}