Added bike and xfer icons.

Now more well-behaved when switching orientations.
Added option to view routes on m.bart.gov

--HG--
rename : res/menu/favorite_context_menu.xml => res/menu/route_context_menu.xml
rename : res/menu/favorites_menu.xml => res/menu/routes_list_menu.xml
rename : src/com/dougkeen/bart/AddFavoriteActivity.java => src/com/dougkeen/bart/AddRouteActivity.java
rename : src/com/dougkeen/bart/FavoritesDashboardActivity.java => src/com/dougkeen/bart/RoutesListActivity.java
rename : src/com/dougkeen/bart/data/FavoritesColumns.java => src/com/dougkeen/bart/data/RoutesColumns.java
This commit is contained in:
dkeen 2011-05-27 14:06:58 -07:00
parent 1f4f5938b5
commit 2bb1b5d035
29 changed files with 534 additions and 102 deletions

View File

@ -1,3 +1,9 @@
syntax: glob syntax: glob
bin/* bin/*
syntax: regexp
^app_icon\.psd$
syntax: regexp
^bart-train\.jpg$
syntax: regexp
^colorscheme\.jpg$

View File

@ -1,6 +1,6 @@
<?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" android:versionCode="2" package="com.dougkeen.bart" android:versionCode="3"
android:versionName="1.0"> android:versionName="1.0">
<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" />
@ -9,7 +9,7 @@
<application android:icon="@drawable/icon" android:label="@string/app_name" <application android:icon="@drawable/icon" android:label="@string/app_name"
android:debuggable="true"> android:debuggable="true">
<activity android:name=".FavoritesDashboardActivity" <activity android:name="RoutesListActivity"
android:label="@string/app_name"> android:label="@string/app_name">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -28,7 +28,7 @@
<data android:mimeType="vnd.android.cursor.item/com.dougkeen.bart.favorite" /> <data android:mimeType="vnd.android.cursor.item/com.dougkeen.bart.favorite" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".AddFavoriteActivity" android:label="@string/app_name"> <activity android:name="AddRouteActivity" android:label="@string/app_name">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.INSERT" /> <action android:name="android.intent.action.INSERT" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" <shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:shape="rectangle">
<size android:height="15dp" android:width="60dp" /> <size android:height="45dp" android:width="15dp" />
</shape> </shape>

BIN
res/drawable/bike.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1008 B

BIN
res/drawable/xfer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

View File

@ -5,7 +5,7 @@
<TextView android:id="@+id/form_header" android:layout_width="wrap_content" <TextView android:id="@+id/form_header" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:textSize="24dp" android:layout_height="wrap_content" android:textSize="24dp"
android:textStyle="bold" android:layout_alignParentTop="true" android:textStyle="bold" android:layout_alignParentTop="true"
android:text="@string/add_favorite_route" android:paddingBottom="10dp" /> android:text="@string/add_route" android:paddingBottom="10dp" />
<TextView android:id="@+id/origin_label" android:text="@string/origin" <TextView android:id="@+id/origin_label" android:text="@string/origin"
android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:textSize="15dp" android:layout_below="@+id/form_header" android:textSize="15dp" android:layout_below="@+id/form_header"

View File

@ -1,12 +1,22 @@
<?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:layout_height="fill_parent">
<TextView android:id="@+id/destinationText" style="@style/ArrivalDestinationText"
android:layout_alignParentTop="true" android:layout_alignParentLeft="true" />
<ImageView android:id="@+id/destinationColorBar" android:src="@drawable/basic_rectangle" <ImageView android:id="@+id/destinationColorBar" android:src="@drawable/basic_rectangle"
android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentLeft="true" android:layout_centerVertical="true" />
<TextView android:id="@+id/destinationText" style="@style/ArrivalDestinationText"
android:layout_alignParentTop="true" android:layout_toRightOf="@id/destinationColorBar" />
<TextView android:id="@+id/trainLengthText" style="@style/ArrivalTrainLengthText"
android:layout_toRightOf="@id/destinationColorBar"
android:layout_below="@id/destinationText" /> android:layout_below="@id/destinationText" />
<TextView android:layout_centerVertical="true" <TextView android:layout_alignParentRight="true" android:id="@+id/countdown"
android:layout_alignParentRight="true" android:id="@+id/countdown"
style="@style/ArrivalCountdownText" /> style="@style/ArrivalCountdownText" />
</RelativeLayout> <TextView android:layout_alignParentRight="true" android:id="@+id/uncertainty"
android:layout_below="@id/countdown" style="@style/ArrivalUncertaintyText" />
<ImageView android:id="@+id/bikeIcon" android:src="@drawable/bike"
android:layout_alignParentTop="true" android:layout_alignParentRight="true"
style="@style/BikeIcon" />
<ImageView android:id="@+id/xferIcon" android:src="@drawable/xfer"
android:layout_below="@id/bikeIcon" android:layout_alignParentRight="true"
style="@style/XferIcon" />
</RelativeLayout>

View File

@ -3,6 +3,6 @@
android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_width="fill_parent" android:layout_height="wrap_content"
android:paddingTop="15dip" android:paddingBottom="15dip" android:paddingTop="15dip" android:paddingBottom="15dip"
android:paddingLeft="5dip" android:paddingRight="5dip" android:id="@android:id/text1" android:paddingLeft="5dip" android:paddingRight="5dip" android:id="@android:id/text1"
android:textColor="#cccccc" android:ellipsize="marquee" android:ellipsize="marquee" android:textColor="@color/black"
android:singleLine="true"> android:singleLine="true">
</TextView> </TextView>

5
res/menu/route_menu.xml Normal file
View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="@string/view_on_bart_site" android:icon="@android:drawable/ic_menu_view"
android:id="@+id/view_on_bart_site_button"></item>
</menu>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="@string/add_favorite" android:icon="@android:drawable/ic_menu_add" <item android:title="@string/add_route" android:icon="@android:drawable/ic_menu_add"
android:id="@+id/add_favorite_menu_button"></item> android:id="@+id/add_favorite_menu_button"></item>
</menu> </menu>

View File

@ -2,23 +2,24 @@
<resources> <resources>
<string name="app_name">Bart Catcher</string> <string name="app_name">Bart Catcher</string>
<string name="favorite_routes">Favorite Routes</string> <string name="favorite_routes">Favorite Routes</string>
<string name="empty_favorites_list_message">Press the menu button and select "Add <string name="empty_favorites_list_message">Press the menu button and select \"Add route\" to add
favorite" to a route</string>
add a route</string> <string name="add_route">Add a route</string>
<string name="add_favorite">Add favorite</string>
<string name="add_favorite_route">Add a favorite route</string>
<string name="origin">Origin</string> <string name="origin">Origin</string>
<string name="destination">Destination</string> <string name="destination">Destination</string>
<string name="save">Save</string> <string name="save">Save</string>
<string name="error_matching_origin_and_destination">The origin and destination stations must be different</string> <string name="error_matching_origin_and_destination">The origin and destination stations must be
different</string>
<string name="error_null_destination">You must select a destination station</string> <string name="error_null_destination">You must select a destination station</string>
<string name="error_null_origin">You must select an origin station</string> <string name="error_null_origin">You must select an origin station</string>
<string name="arrival_wait_message">Please wait while real time arrival data is loaded...</string> <string name="arrival_wait_message">Please wait while real time arrival data is
<string name="no_data_message">No arrival data is currently available for this route</string> loaded...</string>
<string name="no_data_message">No arrival data is currently available for this
route</string>
<string name="view">View</string> <string name="view">View</string>
<string name="view_arrivals">View arrivals</string> <string name="view_arrivals">View arrivals</string>
<string name="delete">Delete</string> <string name="delete">Delete</string>
<string name="yes">Yes</string> <string name="yes">Yes</string>
<string name="cancel">Cancel</string> <string name="cancel">Cancel</string>
<string name="view_on_bart_site">View details on BART site</string>
</resources> </resources>

View File

@ -21,8 +21,18 @@
<style name="ArrivalDestinationText"> <style name="ArrivalDestinationText">
<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>
<item name="android:textSize">20dp</item> <item name="android:textSize">22dp</item>
<item name="android:textStyle">bold</item>
<item name="android:singleLine">true</item> <item name="android:singleLine">true</item>
<item name="android:layout_marginLeft">3dp</item>
</style>
<style name="ArrivalTrainLengthText">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textSize">18dp</item>
<item name="android:singleLine">true</item>
<item name="android:layout_marginLeft">3dp</item>
</style> </style>
<style name="ArrivalCountdownText"> <style name="ArrivalCountdownText">
@ -31,4 +41,22 @@
<item name="android:textSize">20dp</item> <item name="android:textSize">20dp</item>
<item name="android:singleLine">true</item> <item name="android:singleLine">true</item>
</style> </style>
<style name="ArrivalUncertaintyText">
<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:singleLine">true</item>
</style>
<style name="BikeIcon">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginRight">100dp</item>
<item name="android:layout_marginTop">5dp</item>
</style>
<style name="XferIcon">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginRight">97dp</item>
<item name="android:layout_marginTop">5dp</item>
</style>
</resources> </resources>

View File

@ -1,6 +1,6 @@
package com.dougkeen.bart; package com.dougkeen.bart;
import com.dougkeen.bart.data.FavoritesColumns; import com.dougkeen.bart.data.RoutesColumns;
import android.app.Activity; import android.app.Activity;
import android.content.ContentValues; import android.content.ContentValues;
@ -14,7 +14,7 @@ import android.widget.Spinner;
import android.widget.SpinnerAdapter; import android.widget.SpinnerAdapter;
import android.widget.Toast; import android.widget.Toast;
public class AddFavoriteActivity extends Activity { public class AddRouteActivity extends Activity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -80,8 +80,8 @@ public class AddFavoriteActivity extends Activity {
} }
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(FavoritesColumns.FROM_STATION.string, origin.abbreviation); values.put(RoutesColumns.FROM_STATION.string, origin.abbreviation);
values.put(FavoritesColumns.TO_STATION.string, destination.abbreviation); values.put(RoutesColumns.TO_STATION.string, destination.abbreviation);
Uri newUri = getContentResolver().insert( Uri newUri = getContentResolver().insert(
Constants.FAVORITE_CONTENT_URI, values); Constants.FAVORITE_CONTENT_URI, values);

View File

@ -0,0 +1,90 @@
package com.dougkeen.bart;
import java.util.List;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.dougkeen.bart.data.Arrival;
public class ArrivalArrayAdapter extends ArrayAdapter<Arrival> {
public ArrivalArrayAdapter(Context context, int textViewResourceId,
Arrival[] objects) {
super(context, textViewResourceId, objects);
}
public ArrivalArrayAdapter(Context context, int resource,
int textViewResourceId, Arrival[] objects) {
super(context, resource, textViewResourceId, objects);
}
public ArrivalArrayAdapter(Context context, int resource,
int textViewResourceId, List<Arrival> objects) {
super(context, resource, textViewResourceId, objects);
}
public ArrivalArrayAdapter(Context context, int resource,
int textViewResourceId) {
super(context, resource, textViewResourceId);
}
public ArrivalArrayAdapter(Context context, int textViewResourceId,
List<Arrival> objects) {
super(context, textViewResourceId, objects);
}
public ArrivalArrayAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
if (convertView != null && convertView instanceof RelativeLayout) {
view = convertView;
} else {
LayoutInflater inflater = LayoutInflater.from(getContext());
view = inflater.inflate(R.layout.arrival_listing, parent, false);
}
Arrival arrival = getItem(position);
((TextView) view.findViewById(R.id.destinationText)).setText(arrival
.getDestination().toString());
((TextView) view.findViewById(R.id.trainLengthText)).setText(arrival
.getTrainLengthText());
ImageView colorBar = (ImageView) view
.findViewById(R.id.destinationColorBar);
((GradientDrawable) colorBar.getDrawable()).setColor(Color
.parseColor(arrival.getDestinationColor()));
((TextView) view.findViewById(R.id.countdown)).setText(arrival
.getCountdownText());
((TextView) view.findViewById(R.id.uncertainty)).setText(arrival
.getUncertaintyText());
if (arrival.isBikeAllowed()) {
((ImageView) view.findViewById(R.id.bikeIcon))
.setVisibility(View.VISIBLE);
} else {
((ImageView) view.findViewById(R.id.bikeIcon))
.setVisibility(View.INVISIBLE);
}
if (arrival.getRequiresTransfer()) {
((ImageView) view.findViewById(R.id.xferIcon))
.setVisibility(View.VISIBLE);
} else {
((ImageView) view.findViewById(R.id.xferIcon))
.setVisibility(View.INVISIBLE);
}
return view;
}
}

View File

@ -40,7 +40,11 @@ public abstract class GetRealTimeArrivalsTask extends
throw new RuntimeException(e); throw new RuntimeException(e);
} }
return getArrivalsFromNetwork(params, sourceUrl, 0); if (!isCancelled()) {
return getArrivalsFromNetwork(params, sourceUrl, 0);
} else {
return null;
}
} }
private RealTimeArrivals getArrivalsFromNetwork(Params params, private RealTimeArrivals getArrivalsFromNetwork(Params params,
@ -48,6 +52,9 @@ public abstract class GetRealTimeArrivalsTask extends
try { try {
EtdContentHandler handler = new EtdContentHandler(params.origin, EtdContentHandler handler = new EtdContentHandler(params.origin,
params.destination, mRoutes); params.destination, mRoutes);
if (isCancelled()) {
return null;
}
Xml.parse(sourceUrl.openStream(), Xml.findEncodingByName("UTF-8"), Xml.parse(sourceUrl.openStream(), Xml.findEncodingByName("UTF-8"),
handler); handler);
final RealTimeArrivals realTimeArrivals = handler final RealTimeArrivals realTimeArrivals = handler

View File

@ -5,6 +5,7 @@ public class Route {
private Station destination; private Station destination;
private Line line; private Line line;
private boolean requiresTransfer; private boolean requiresTransfer;
private Station transferStation;
private String direction; private String direction;
public Station getOrigin() { public Station getOrigin() {
@ -39,6 +40,14 @@ public class Route {
this.requiresTransfer = requiresTransfer; this.requiresTransfer = requiresTransfer;
} }
public Station getTransferStation() {
return transferStation;
}
public void setTransferStation(Station transferStation) {
this.transferStation = transferStation;
}
public String getDirection() { public String getDirection() {
return direction; return direction;
} }
@ -58,6 +67,8 @@ public class Route {
builder.append(line); builder.append(line);
builder.append(", requiresTransfer="); builder.append(", requiresTransfer=");
builder.append(requiresTransfer); builder.append(requiresTransfer);
builder.append(", transferStation=");
builder.append(transferStation);
builder.append(", direction="); builder.append(", direction=");
builder.append(direction); builder.append(direction);
builder.append("]"); builder.append("]");

View File

@ -24,9 +24,9 @@ import android.widget.SimpleCursorAdapter.ViewBinder;
import android.widget.TextView; import android.widget.TextView;
import com.dougkeen.bart.data.CursorUtils; import com.dougkeen.bart.data.CursorUtils;
import com.dougkeen.bart.data.FavoritesColumns; import com.dougkeen.bart.data.RoutesColumns;
public class FavoritesDashboardActivity extends ListActivity { public class RoutesListActivity extends ListActivity {
private static final int DIALOG_DELETE_EVENT = 0; private static final int DIALOG_DELETE_EVENT = 0;
protected Cursor mQuery; protected Cursor mQuery;
@ -41,22 +41,17 @@ public class FavoritesDashboardActivity extends ListActivity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.main); setContentView(R.layout.main);
((TextView) findViewById(R.id.listTitle))
.setText(R.string.favorite_routes);
((TextView) findViewById(android.R.id.empty))
.setText(R.string.empty_favorites_list_message);
mQuery = managedQuery(Constants.FAVORITE_CONTENT_URI, new String[] { mQuery = managedQuery(Constants.FAVORITE_CONTENT_URI, new String[] {
FavoritesColumns._ID.string, RoutesColumns._ID.string,
FavoritesColumns.FROM_STATION.string, RoutesColumns.FROM_STATION.string,
FavoritesColumns.TO_STATION.string }, null, null, RoutesColumns.TO_STATION.string }, null, null,
FavoritesColumns._ID.string); RoutesColumns._ID.string);
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
R.layout.favorite_listing, R.layout.favorite_listing,
mQuery, mQuery,
new String[] { FavoritesColumns.FROM_STATION.string, new String[] { RoutesColumns.FROM_STATION.string,
FavoritesColumns.TO_STATION.string }, RoutesColumns.TO_STATION.string },
new int[] { R.id.originText, new int[] { R.id.originText,
R.id.destinationText }); R.id.destinationText });
adapter.setViewBinder(new ViewBinder() { adapter.setViewBinder(new ViewBinder() {
@ -74,10 +69,19 @@ public class FavoritesDashboardActivity extends ListActivity {
registerForContextMenu(getListView()); registerForContextMenu(getListView());
} }
@Override
protected void onResume() {
super.onResume();
((TextView) findViewById(R.id.listTitle))
.setText(R.string.favorite_routes);
((TextView) findViewById(android.R.id.empty))
.setText(R.string.empty_favorites_list_message);
}
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater(); MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.favorites_menu, menu); inflater.inflate(R.menu.routes_list_menu, menu);
return true; return true;
} }
@ -105,15 +109,15 @@ public class FavoritesDashboardActivity extends ListActivity {
super.onCreateContextMenu(menu, v, menuInfo); super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getMenuInflater(); MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.favorite_context_menu, menu); inflater.inflate(R.menu.route_context_menu, menu);
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
CursorWrapper item = (CursorWrapper) getListAdapter().getItem( CursorWrapper item = (CursorWrapper) getListAdapter().getItem(
info.position); info.position);
Station orig = Station.getByAbbreviation(CursorUtils.getString(item, Station orig = Station.getByAbbreviation(CursorUtils.getString(item,
FavoritesColumns.FROM_STATION)); RoutesColumns.FROM_STATION));
Station dest = Station.getByAbbreviation(CursorUtils.getString(item, Station dest = Station.getByAbbreviation(CursorUtils.getString(item,
FavoritesColumns.TO_STATION)); RoutesColumns.TO_STATION));
mCurrentlySelectedRouteName = orig.name + " - " + dest.name; mCurrentlySelectedRouteName = orig.name + " - " + dest.name;
menu.setHeaderTitle(mCurrentlySelectedRouteName); menu.setHeaderTitle(mCurrentlySelectedRouteName);
} }

View File

@ -115,10 +115,11 @@ public enum Station {
} }
public List<Route> getRoutesForDestination(Station dest) { public List<Route> getRoutesForDestination(Station dest) {
return getRoutesForDestination(dest, false); return getRoutesForDestination(dest, null);
} }
public List<Route> getRoutesForDestination(Station dest, boolean isTransfer) { public List<Route> getRoutesForDestination(Station dest,
Station transferStation) {
if (dest == null) if (dest == null)
return null; return null;
Boolean isNorth = null; Boolean isNorth = null;
@ -140,7 +141,7 @@ public enum Station {
route.setDestination(dest); route.setDestination(dest);
route.setDirection(isNorth ? "n" : "s"); route.setDirection(isNorth ? "n" : "s");
route.setLine(line); route.setLine(line);
if (isTransfer || line.requiresTransfer) { if (transferStation != null || line.requiresTransfer) {
route.setTransfer(true); route.setTransfer(true);
} else { } else {
route.setTransfer(false); route.setTransfer(false);
@ -149,11 +150,14 @@ public enum Station {
} }
if (isNorth == null) { if (isNorth == null) {
if (outboundTransferStation != null) { if (outboundTransferStation != null) {
returnList.addAll(getOutboundTransferStation() returnList
.getRoutesForDestination(dest, true)); .addAll(getOutboundTransferStation()
.getRoutesForDestination(dest,
getOutboundTransferStation()));
} else { } else {
returnList.addAll(getRoutesForDestination(dest returnList.addAll(getRoutesForDestination(dest
.getInboundTransferStation(), true)); .getInboundTransferStation(), dest
.getInboundTransferStation()));
} }
} }
return returnList; return returnList;

View File

@ -10,18 +10,29 @@ import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcelable;
import android.os.PowerManager; import android.os.PowerManager;
import android.text.format.DateFormat;
import android.util.Log;
import android.util.TimeFormatException;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.dougkeen.bart.GetRealTimeArrivalsTask.Params; import com.dougkeen.bart.GetRealTimeArrivalsTask.Params;
import com.dougkeen.bart.data.Arrival; import com.dougkeen.bart.data.Arrival;
import com.dougkeen.bart.data.FavoritesColumns; import com.dougkeen.bart.data.RoutesColumns;
import com.dougkeen.bart.data.RealTimeArrivals; import com.dougkeen.bart.data.RealTimeArrivals;
public class ViewArrivalsActivity extends ListActivity { public class ViewArrivalsActivity extends ListActivity {
private static final String TAG = "BartCatcher";
private static final int UNCERTAINTY_THRESHOLD = 17;
private Uri mUri; private Uri mUri;
private Station mOrigin; private Station mOrigin;
@ -44,9 +55,10 @@ public class ViewArrivalsActivity extends ListActivity {
private PowerManager.WakeLock mWakeLock; private PowerManager.WakeLock mWakeLock;
private boolean mFetchArrivalsOnNextFocus;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.main); setContentView(R.layout.main);
@ -59,8 +71,8 @@ public class ViewArrivalsActivity extends ListActivity {
} }
Cursor cursor = managedQuery(mUri, new String[] { Cursor cursor = managedQuery(mUri, new String[] {
FavoritesColumns.FROM_STATION.string, RoutesColumns.FROM_STATION.string,
FavoritesColumns.TO_STATION.string }, null, null, null); RoutesColumns.TO_STATION.string }, null, null, null);
if (!cursor.moveToFirst()) { if (!cursor.moveToFirst()) {
throw new IllegalStateException("URI not found: " + mUri.toString()); throw new IllegalStateException("URI not found: " + mUri.toString());
@ -75,31 +87,69 @@ public class ViewArrivalsActivity extends ListActivity {
((TextView) findViewById(android.R.id.empty)) ((TextView) findViewById(android.R.id.empty))
.setText(R.string.arrival_wait_message); .setText(R.string.arrival_wait_message);
mArrivalsAdapter = new ArrayAdapter<Arrival>( mArrivalsAdapter = new ArrivalArrayAdapter(this,
this, R.layout.simple_spinner_item); R.layout.arrival_listing);
if (savedInstanceState != null
&& savedInstanceState.containsKey("arrivals")) {
for (Parcelable arrival : savedInstanceState
.getParcelableArray("arrivals")) {
mArrivalsAdapter.add((Arrival) arrival);
}
}
setListAdapter(mArrivalsAdapter); setListAdapter(mArrivalsAdapter);
fetchLatestArrivals(); mFetchArrivalsOnNextFocus = true;
}
@Override
protected void onDestroy() {
if (mGetArrivalsTask != null) {
mGetArrivalsTask.cancel(true);
}
super.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Arrival[] arrivals = new Arrival[mArrivalsAdapter.getCount()];
for (int i = mArrivalsAdapter.getCount() - 1; i >= 0; i--) {
arrivals[i] = mArrivalsAdapter.getItem(i);
}
outState.putParcelableArray("arrivals", arrivals);
} }
@Override @Override
public void onWindowFocusChanged(boolean hasFocus) { public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus); super.onWindowFocusChanged(hasFocus);
if (hasFocus) { if (hasFocus) {
if (mFetchArrivalsOnNextFocus) {
fetchLatestArrivals();
mFetchArrivalsOnNextFocus = false;
}
PowerManager powerManaer = (PowerManager) getSystemService(Context.POWER_SERVICE); PowerManager powerManaer = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManaer.newWakeLock( mWakeLock = powerManaer.newWakeLock(
PowerManager.SCREEN_DIM_WAKE_LOCK, "ViewArrivalsActivity"); PowerManager.SCREEN_DIM_WAKE_LOCK, "ViewArrivalsActivity");
mWakeLock.acquire(); mWakeLock.acquire();
if (mArrivalsAdapter != null && !mArrivalsAdapter.isEmpty()) {
mIsAutoUpdating = true;
}
runAutoUpdate();
} else if (mWakeLock != null) { } else if (mWakeLock != null) {
mWakeLock.release(); mWakeLock.release();
} }
} }
private void fetchLatestArrivals() { private void fetchLatestArrivals() {
if (!hasWindowFocus())
return;
mGetArrivalsTask = new GetRealTimeArrivalsTask() { mGetArrivalsTask = new GetRealTimeArrivalsTask() {
@Override @Override
public void onResult(RealTimeArrivals result) { public void onResult(RealTimeArrivals result) {
Log.i(TAG, "Processing data from server");
processLatestArrivals(result); processLatestArrivals(result);
Log.i(TAG, "Done processing data from server");
} }
@Override @Override
@ -108,6 +158,7 @@ public class ViewArrivalsActivity extends ListActivity {
Toast.LENGTH_SHORT).show(); Toast.LENGTH_SHORT).show();
} }
}; };
Log.i(TAG, "Fetching data from server");
mGetArrivalsTask.execute(new GetRealTimeArrivalsTask.Params(mOrigin, mGetArrivalsTask.execute(new GetRealTimeArrivalsTask.Params(mOrigin,
mDestination)); mDestination));
} }
@ -119,6 +170,7 @@ public class ViewArrivalsActivity extends ListActivity {
return; return;
} }
boolean needsBetterAccuracy = false;
Arrival firstArrival = null; Arrival firstArrival = null;
final List<Arrival> arrivals = result.getArrivals(); final List<Arrival> arrivals = result.getArrivals();
if (mArrivalsAdapter.getCount() > 0) { if (mArrivalsAdapter.getCount() > 0) {
@ -148,6 +200,9 @@ public class ViewArrivalsActivity extends ListActivity {
if (firstArrival == null) { if (firstArrival == null) {
firstArrival = existingArrival; firstArrival = existingArrival;
} }
if (existingArrival.getUncertaintySeconds() > UNCERTAINTY_THRESHOLD) {
needsBetterAccuracy = true;
}
} }
} else { } else {
for (Arrival arrival : arrivals) { for (Arrival arrival : arrivals) {
@ -156,12 +211,13 @@ public class ViewArrivalsActivity extends ListActivity {
} }
mArrivalsAdapter.add(arrival); mArrivalsAdapter.add(arrival);
} }
needsBetterAccuracy = true;
} }
mArrivalsAdapter.notifyDataSetChanged(); mArrivalsAdapter.notifyDataSetChanged();
if (hasWindowFocus() && firstArrival != null) { if (hasWindowFocus() && firstArrival != null) {
if (firstArrival.getUncertaintySeconds() > 17 if (needsBetterAccuracy
|| firstArrival.getMinutes() == 0) { || firstArrival.hasArrived()) {
// Get more data in 20s // Get more data in 20s
mListTitleView.postDelayed(new Runnable() { mListTitleView.postDelayed(new Runnable() {
@Override @Override
@ -169,27 +225,29 @@ public class ViewArrivalsActivity extends ListActivity {
fetchLatestArrivals(); fetchLatestArrivals();
} }
}, 20000); }, 20000);
Log.i(TAG, "Scheduled another data fetch in 20s");
} else { } else {
// Get more when next train arrives // Get more when next train arrives
final int interval = firstArrival.getMinSecondsLeft() * 1000;
mListTitleView.postDelayed(new Runnable() { mListTitleView.postDelayed(new Runnable() {
@Override @Override
public void run() { public void run() {
fetchLatestArrivals(); fetchLatestArrivals();
} }
}, firstArrival.getMinSecondsLeft() * 1000); }, interval);
Log.i(TAG, "Scheduled another data fetch in " + interval / 1000
+ "s");
} }
if (!mIsAutoUpdating) { if (!mIsAutoUpdating) {
mIsAutoUpdating = true; mIsAutoUpdating = true;
runAutoUpdate();
} }
} else { } else {
mIsAutoUpdating = false; mIsAutoUpdating = false;
} }
} }
private void runAutoUpdate() { private void runAutoUpdate() {
if (mIsAutoUpdating) { if (mIsAutoUpdating && mArrivalsAdapter != null) {
mArrivalsAdapter.notifyDataSetChanged(); mArrivalsAdapter.notifyDataSetChanged();
} }
if (hasWindowFocus()) { if (hasWindowFocus()) {
@ -198,4 +256,30 @@ public class ViewArrivalsActivity extends ListActivity {
mIsAutoUpdating = false; mIsAutoUpdating = false;
} }
} }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.route_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId();
if (itemId == R.id.view_on_bart_site_button) {
startActivity(new Intent(
Intent.ACTION_VIEW,
Uri.parse("http://m.bart.gov/schedules/qp_results.aspx?type=departure&date=today&time="
+ DateFormat.format("h:mmaa",
System.currentTimeMillis()) + "&orig="
+ mOrigin.abbreviation
+ "&dest="
+ mDestination.abbreviation)));
mFetchArrivalsOnNextFocus = true;
return true;
} else {
return super.onOptionsItemSelected(item);
}
}
} }

View File

@ -1,8 +1,11 @@
package com.dougkeen.bart.data; package com.dougkeen.bart.data;
import android.os.Parcel;
import android.os.Parcelable;
import com.dougkeen.bart.Station; import com.dougkeen.bart.Station;
public class Arrival implements Comparable<Arrival> { public class Arrival implements Parcelable, Comparable<Arrival> {
public Arrival() { public Arrival() {
super(); super();
} }
@ -20,6 +23,10 @@ public class Arrival implements Comparable<Arrival> {
this.minutes = minutes; this.minutes = minutes;
} }
public Arrival(Parcel in) {
readFromParcel(in);
}
private Station destination; private Station destination;
private String destinationColor; private String destinationColor;
private String platform; private String platform;
@ -93,6 +100,10 @@ public class Arrival implements Comparable<Arrival> {
this.trainLength = trainLength; this.trainLength = trainLength;
} }
public String getTrainLengthText() {
return trainLength + " car train";
}
public boolean getRequiresTransfer() { public boolean getRequiresTransfer() {
return requiresTransfer; return requiresTransfer;
} }
@ -142,14 +153,24 @@ public class Arrival implements Comparable<Arrival> {
.currentTimeMillis()) / 1000); .currentTimeMillis()) / 1000);
} }
public boolean hasArrived() {
return getMinutes() == 0 || getMeanSecondsLeft() < 0;
}
public void calculateEstimates(long originalEstimateTime) { public void calculateEstimates(long originalEstimateTime) {
setMinEstimate(originalEstimateTime + (getMinutes() * 60 * 1000)); setMinEstimate(originalEstimateTime + (getMinutes() * 60 * 1000));
setMaxEstimate(getMinEstimate() + (59 * 1000)); setMaxEstimate(getMinEstimate() + (59 * 1000));
} }
public void mergeEstimate(Arrival arrival) { public void mergeEstimate(Arrival arrival) {
setMinEstimate(Math.max(getMinEstimate(), arrival.getMinEstimate())); final long newMin = Math
setMaxEstimate(Math.min(getMaxEstimate(), arrival.getMaxEstimate())); .max(getMinEstimate(), arrival.getMinEstimate());
final long newMax = Math
.min(getMaxEstimate(), arrival.getMaxEstimate());
if (newMax > newMin) { // We can never have 0 or negative uncertainty
setMinEstimate(newMin);
setMaxEstimate(newMax);
}
} }
@Override @Override
@ -193,27 +214,79 @@ public class Arrival implements Comparable<Arrival> {
return delta > -60000 && delta < 60000; return delta > -60000 && delta < 60000;
} }
@Override public String getCountdownText() {
public String toString() {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append(destination);
if(requiresTransfer) {
builder.append(" (w/ xfer)");
}
builder.append(", ");
builder.append(trainLength);
int secondsLeft = getMeanSecondsLeft(); int secondsLeft = getMeanSecondsLeft();
if (getMinutes() == 0 || secondsLeft < 0) { if (hasArrived()) {
builder.append(" car train has arrived"); builder.append("Arrived");
} else { } else {
builder.append(" car train in ");
builder.append(secondsLeft / 60); builder.append(secondsLeft / 60);
builder.append("m, "); builder.append("m, ");
builder.append(secondsLeft % 60); builder.append(secondsLeft % 60);
builder.append("s, ±");
builder.append(getUncertaintySeconds());
builder.append("s"); builder.append("s");
} }
return builder.toString(); return builder.toString();
} }
}
public String getUncertaintyText() {
if (hasArrived()) {
return "";
} else {
return "" + getUncertaintySeconds() + "s)";
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(destination);
if (requiresTransfer) {
builder.append(" (w/ xfer)");
}
builder.append(", ");
builder.append(getCountdownText());
return builder.toString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(destination.abbreviation);
dest.writeString(destinationColor);
dest.writeString(platform);
dest.writeString(direction);
dest.writeByte((byte) (bikeAllowed ? 1 : 0));
dest.writeInt(trainLength);
dest.writeByte((byte) (requiresTransfer ? 1 : 0));
dest.writeInt(minutes);
dest.writeLong(minEstimate);
dest.writeLong(maxEstimate);
}
private void readFromParcel(Parcel in) {
destination = Station.getByAbbreviation(in.readString());
destinationColor = in.readString();
platform = in.readString();
direction = in.readString();
bikeAllowed = in.readByte() != 0;
trainLength = in.readInt();
requiresTransfer = in.readByte() != 0;
minutes = in.readInt();
minEstimate = in.readLong();
maxEstimate = in.readLong();
}
public static final Parcelable.Creator<Arrival> CREATOR = new Parcelable.Creator<Arrival>() {
public Arrival createFromParcel(Parcel in) {
return new Arrival(in);
}
public Arrival[] newArray(int size) {
return new Arrival[size];
}
};
}

View File

@ -26,7 +26,7 @@ public class BartContentProvider extends ContentProvider {
/** /**
* The default sort order for events * The default sort order for events
*/ */
private static final String DEFAULT_SORT_ORDER = FavoritesColumns.FROM_STATION.string private static final String DEFAULT_SORT_ORDER = RoutesColumns.FROM_STATION.string
+ " DESC"; + " DESC";
static { static {
@ -35,12 +35,12 @@ public class BartContentProvider extends ContentProvider {
sUriMatcher.addURI(Constants.AUTHORITY, "favorites/#", FAVORITE_ID); sUriMatcher.addURI(Constants.AUTHORITY, "favorites/#", FAVORITE_ID);
sFavoritesProjectionMap = new HashMap<String, String>(); sFavoritesProjectionMap = new HashMap<String, String>();
sFavoritesProjectionMap.put(FavoritesColumns._ID.string, sFavoritesProjectionMap.put(RoutesColumns._ID.string,
FavoritesColumns._ID.string); RoutesColumns._ID.string);
sFavoritesProjectionMap.put(FavoritesColumns.FROM_STATION.string, sFavoritesProjectionMap.put(RoutesColumns.FROM_STATION.string,
FavoritesColumns.FROM_STATION.string); RoutesColumns.FROM_STATION.string);
sFavoritesProjectionMap.put(FavoritesColumns.TO_STATION.string, sFavoritesProjectionMap.put(RoutesColumns.TO_STATION.string,
FavoritesColumns.TO_STATION.string); RoutesColumns.TO_STATION.string);
} }
private DatabaseHelper mDatabaseHelper; private DatabaseHelper mDatabaseHelper;
@ -81,7 +81,7 @@ public class BartContentProvider extends ContentProvider {
} else if (match == FAVORITE_ID) { } else if (match == FAVORITE_ID) {
qb.setTables(DatabaseHelper.FAVORITES_TABLE_NAME); qb.setTables(DatabaseHelper.FAVORITES_TABLE_NAME);
qb.setProjectionMap(sFavoritesProjectionMap); qb.setProjectionMap(sFavoritesProjectionMap);
qb.appendWhere(FavoritesColumns._ID + " = " qb.appendWhere(RoutesColumns._ID + " = "
+ uri.getPathSegments().get(1)); + uri.getPathSegments().get(1));
} else { } else {
throw new IllegalArgumentException("Unknown URI " + uri); throw new IllegalArgumentException("Unknown URI " + uri);
@ -121,12 +121,12 @@ public class BartContentProvider extends ContentProvider {
long rowId = -1; long rowId = -1;
Cursor cursor = db Cursor cursor = db
.query(DatabaseHelper.FAVORITES_TABLE_NAME, .query(DatabaseHelper.FAVORITES_TABLE_NAME,
new String[] { FavoritesColumns._ID.string }, new String[] { RoutesColumns._ID.string },
FavoritesColumns.FROM_STATION + "=? AND " RoutesColumns.FROM_STATION + "=? AND "
+ FavoritesColumns.TO_STATION + "=?", + RoutesColumns.TO_STATION + "=?",
new String[] { new String[] {
values.getAsString(FavoritesColumns.FROM_STATION.string), values.getAsString(RoutesColumns.FROM_STATION.string),
values.getAsString(FavoritesColumns.TO_STATION.string) }, values.getAsString(RoutesColumns.TO_STATION.string) },
null, null,
null, null,
null); null);
@ -139,7 +139,7 @@ public class BartContentProvider extends ContentProvider {
} }
if (rowId < 0) { if (rowId < 0) {
rowId = db.insert(DatabaseHelper.FAVORITES_TABLE_NAME, rowId = db.insert(DatabaseHelper.FAVORITES_TABLE_NAME,
FavoritesColumns.FROM_STATION.string, values); RoutesColumns.FROM_STATION.string, values);
} }
if (rowId > 0) { if (rowId > 0) {
Uri eventUri = ContentUris.withAppendedId( Uri eventUri = ContentUris.withAppendedId(
@ -174,7 +174,7 @@ public class BartContentProvider extends ContentProvider {
} 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(DatabaseHelper.FAVORITES_TABLE_NAME,
FavoritesColumns._ID + " = " RoutesColumns._ID + " = "
+ favoriteId + favoriteId
+ (!TextUtils.isEmpty(where) ? " AND (" + where + (!TextUtils.isEmpty(where) ? " AND (" + where
+ ')' : ""), whereArgs); + ')' : ""), whereArgs);

View File

@ -13,7 +13,7 @@ public final class CursorUtils {
} }
} }
public static final String getString(Cursor cursor, FavoritesColumns column) { public static final String getString(Cursor cursor, RoutesColumns column) {
return cursor.getString(cursor.getColumnIndex(column.string)); return cursor.getString(cursor.getColumnIndex(column.string));
} }

View File

@ -18,9 +18,9 @@ public class DatabaseHelper extends SQLiteOpenHelper {
@Override @Override
public void onCreate(SQLiteDatabase db) { public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + FAVORITES_TABLE_NAME + " (" + db.execSQL("CREATE TABLE " + FAVORITES_TABLE_NAME + " (" +
FavoritesColumns._ID.getColumnDef() + " PRIMARY KEY, " + RoutesColumns._ID.getColumnDef() + " PRIMARY KEY, " +
FavoritesColumns.FROM_STATION.getColumnDef() + ", " + RoutesColumns.FROM_STATION.getColumnDef() + ", " +
FavoritesColumns.TO_STATION.getColumnDef() + ");"); RoutesColumns.TO_STATION.getColumnDef() + ");");
} }
@Override @Override

View File

@ -1,12 +1,12 @@
package com.dougkeen.bart.data; package com.dougkeen.bart.data;
public enum FavoritesColumns { public enum RoutesColumns {
_ID("_id", "INTEGER"), _ID("_id", "INTEGER"),
FROM_STATION("FROM_STATION", "TEXT"), FROM_STATION("FROM_STATION", "TEXT"),
TO_STATION("TO_STATION", "TEXT"); TO_STATION("TO_STATION", "TEXT");
// This class cannot be instantiated // This class cannot be instantiated
private FavoritesColumns(String string, String type) { private RoutesColumns(String string, String type) {
this.string = string; this.string = string;
this.sqliteType = type; this.sqliteType = type;
} }

109
xfer.svg Normal file
View File

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32px"
height="32px"
id="svg2383"
sodipodi:version="0.32"
inkscape:version="0.46"
sodipodi:docname="xfer.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape">
<defs
id="defs2385">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 16 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="32 : 16 : 1"
inkscape:persp3d-origin="16 : 10.666667 : 1"
id="perspective2391" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="22.395604"
inkscape:cx="14.100126"
inkscape:cy="15.964773"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="1920"
inkscape:window-height="1125"
inkscape:window-x="1911"
inkscape:window-y="-9" />
<metadata
id="metadata2388">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<path
style="fill:#29716d;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 6.117272,8.0220801 L 0.58047106,15.702158 L 26.255152,15.702158 L 26.255152,11.460255 L 6.117272,11.504906 L 6.117272,8.0220801 z"
id="path2393"
sodipodi:nodetypes="cccccc"
inkscape:export-filename="C:\Users\dkeen\Workspaces\AndroidExperiments\DontMissTheBart\res\drawable\xfer.png"
inkscape:export-xdpi="117.18631"
inkscape:export-ydpi="117.18631" />
<path
style="fill:#143635;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 25.964917,23.382236 L 31.501718,15.702158 L 5.8270365,15.702158 L 5.8270365,19.944061 L 25.964917,19.89941 L 25.964917,23.382236 z"
id="path3165"
sodipodi:nodetypes="cccccc"
inkscape:export-filename="C:\Users\dkeen\Workspaces\AndroidExperiments\DontMissTheBart\res\drawable\xfer.png"
inkscape:export-xdpi="117.18631"
inkscape:export-ydpi="117.18631" />
<flowRoot
xml:space="preserve"
id="flowRoot3167"
style="font-size:4;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
transform="translate(8.0372916,9.5554467)"
inkscape:export-filename="C:\Users\dkeen\Workspaces\AndroidExperiments\DontMissTheBart\res\drawable\xfer.png"
inkscape:export-xdpi="117.18631"
inkscape:export-ydpi="117.18631"><flowRegion
id="flowRegion3169"><rect
id="rect3171"
width="17.63739"
height="10.359176"
x="3.2595682"
y="4.2266922"
style="font-size:4px;fill:#000000" /></flowRegion><flowPara
id="flowPara3173"
style="font-size:4" /></flowRoot> <text
xml:space="preserve"
style="font-size:4px;font-style:normal;font-weight:normal;line-height:125%;fill:#f2f3ed;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
x="10.001963"
y="18.068693"
id="text3175"
sodipodi:linespacing="125%"
inkscape:export-xdpi="117.18631"
inkscape:export-ydpi="117.18631"
inkscape:export-filename="C:\Users\dkeen\Workspaces\AndroidExperiments\DontMissTheBart\res\drawable\xfer.png"><tspan
sodipodi:role="line"
id="tspan3177"
x="10.001963"
y="18.068693"
style="font-size:6px;font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Helvetica Inserat LT Std;-inkscape-font-specification:Helvetica Inserat LT Std Ultra-Bold;fill:#f2f3ed">XFER</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB