Seems like it works
This commit is contained in:
commit
434cc249ed
7
.classpath
Normal file
7
.classpath
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" path="src"/>
|
||||||
|
<classpathentry kind="src" path="gen"/>
|
||||||
|
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||||
|
<classpathentry kind="output" path="bin"/>
|
||||||
|
</classpath>
|
33
.project
Normal file
33
.project
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>DontMissTheBart</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
50
AndroidManifest.xml
Normal file
50
AndroidManifest.xml
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.dougkeen.bart" android:versionCode="2"
|
||||||
|
android:versionName="1.0">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
|
||||||
|
<uses-sdk android:minSdkVersion="7" />
|
||||||
|
|
||||||
|
<application android:icon="@drawable/icon" android:label="@string/app_name"
|
||||||
|
android:debuggable="true">
|
||||||
|
<activity android:name=".FavoritesDashboardActivity"
|
||||||
|
android:label="@string/app_name">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<action android:name="android.intent.action.EDIT" />
|
||||||
|
<action android:name="android.intent.action.PICK" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="vnd.android.cursor.dir/com.dougkeen.bart.favorite" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.GET_CONTENT" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="vnd.android.cursor.item/com.dougkeen.bart.favorite" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity android:name=".AddFavoriteActivity" android:label="@string/app_name">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.INSERT" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="vnd.android.cursor.dir/com.dougkeen.bart.favorite" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity android:name=".ViewArrivalsActivity"
|
||||||
|
android:label="@string/app_name">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="vnd.android.cursor.item/com.dougkeen.bart.favorite" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<provider android:name=".data.BartContentProvider"
|
||||||
|
android:authorities="com.dougkeen.bart.dataprovider" android:label="Don\'t Miss The Bart data provider" />
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
11
default.properties
Normal file
11
default.properties
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# This file is automatically generated by Android Tools.
|
||||||
|
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||||
|
#
|
||||||
|
# This file must be checked in Version Control Systems.
|
||||||
|
#
|
||||||
|
# To customize properties used by the Ant build system use,
|
||||||
|
# "build.properties", and override values to adapt the script to your
|
||||||
|
# project structure.
|
||||||
|
|
||||||
|
# Project target.
|
||||||
|
target=android-7
|
36
proguard.cfg
Normal file
36
proguard.cfg
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
-optimizationpasses 5
|
||||||
|
-dontusemixedcaseclassnames
|
||||||
|
-dontskipnonpubliclibraryclasses
|
||||||
|
-dontpreverify
|
||||||
|
-verbose
|
||||||
|
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
|
||||||
|
|
||||||
|
-keep public class * extends android.app.Activity
|
||||||
|
-keep public class * extends android.app.Application
|
||||||
|
-keep public class * extends android.app.Service
|
||||||
|
-keep public class * extends android.content.BroadcastReceiver
|
||||||
|
-keep public class * extends android.content.ContentProvider
|
||||||
|
-keep public class * extends android.app.backup.BackupAgentHelper
|
||||||
|
-keep public class * extends android.preference.Preference
|
||||||
|
-keep public class com.android.vending.licensing.ILicensingService
|
||||||
|
|
||||||
|
-keepclasseswithmembernames class * {
|
||||||
|
native <methods>;
|
||||||
|
}
|
||||||
|
|
||||||
|
-keepclasseswithmembernames class * {
|
||||||
|
public <init>(android.content.Context, android.util.AttributeSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
-keepclasseswithmembernames class * {
|
||||||
|
public <init>(android.content.Context, android.util.AttributeSet, int);
|
||||||
|
}
|
||||||
|
|
||||||
|
-keepclassmembers enum * {
|
||||||
|
public static **[] values();
|
||||||
|
public static ** valueOf(java.lang.String);
|
||||||
|
}
|
||||||
|
|
||||||
|
-keep class * implements android.os.Parcelable {
|
||||||
|
public static final android.os.Parcelable$Creator *;
|
||||||
|
}
|
BIN
res/drawable-hdpi/icon.png
Normal file
BIN
res/drawable-hdpi/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.0 KiB |
BIN
res/drawable-ldpi/icon.png
Normal file
BIN
res/drawable-ldpi/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
BIN
res/drawable-mdpi/icon.png
Normal file
BIN
res/drawable-mdpi/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
5
res/drawable/basic_rectangle.xml
Normal file
5
res/drawable/basic_rectangle.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<size android:height="15dp" android:width="60dp" />
|
||||||
|
</shape>
|
33
res/layout/add_favorite.xml
Normal file
33
res/layout/add_favorite.xml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent" android:layout_height="fill_parent"
|
||||||
|
android:paddingLeft="5dp" android:paddingRight="5dp">
|
||||||
|
<TextView android:id="@+id/form_header" android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" android:textSize="24dp"
|
||||||
|
android:textStyle="bold" android:layout_alignParentTop="true"
|
||||||
|
android:text="@string/add_favorite_route" android:paddingBottom="10dp" />
|
||||||
|
<TextView android:id="@+id/origin_label" android:text="@string/origin"
|
||||||
|
android:layout_width="wrap_content" android:layout_height="wrap_content"
|
||||||
|
android:textSize="15dp" android:layout_below="@+id/form_header"
|
||||||
|
android:layout_alignLeft="@+id/form_header"></TextView>
|
||||||
|
<Spinner android:id="@+id/origin_spinner" android:layout_below="@id/origin_label"
|
||||||
|
android:layout_height="wrap_content" android:layout_width="fill_parent" />
|
||||||
|
<TextView android:id="@+id/destination_label" android:text="@string/destination"
|
||||||
|
android:layout_width="wrap_content" android:layout_height="wrap_content"
|
||||||
|
android:textSize="15dp" android:layout_below="@+id/origin_spinner"
|
||||||
|
android:layout_alignLeft="@+id/origin_spinner"></TextView>
|
||||||
|
<Spinner android:id="@+id/destination_spinner"
|
||||||
|
android:layout_below="@id/destination_label" android:layout_height="wrap_content"
|
||||||
|
android:layout_width="fill_parent" />
|
||||||
|
<LinearLayout android:layout_height="wrap_content"
|
||||||
|
android:layout_width="fill_parent" android:layout_below="@id/destination_spinner"
|
||||||
|
style="@style/ButtonBar" android:id="@+id/buttonBar"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
<Button android:text="@string/save" android:layout_weight="1"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/saveButton"
|
||||||
|
android:layout_width="fill_parent"></Button>
|
||||||
|
<Button android:text="@string/cancel" android:layout_weight="1"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/cancelButton"
|
||||||
|
android:layout_width="fill_parent"></Button>
|
||||||
|
</LinearLayout>
|
||||||
|
</RelativeLayout>
|
12
res/layout/arrival_listing.xml
Normal file
12
res/layout/arrival_listing.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
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"
|
||||||
|
android:layout_width="wrap_content" android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/destinationText" />
|
||||||
|
<TextView android:layout_centerVertical="true"
|
||||||
|
android:layout_alignParentRight="true" android:id="@+id/countdown"
|
||||||
|
style="@style/ArrivalCountdownText" />
|
||||||
|
</RelativeLayout>
|
13
res/layout/favorite_listing.xml
Normal file
13
res/layout/favorite_listing.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent" android:layout_height="fill_parent"
|
||||||
|
android:paddingLeft="5dp" android:paddingRight="5dp">
|
||||||
|
<TextView android:id="@+id/originText" style="@style/FavoriteListingTextView"
|
||||||
|
android:layout_alignParentTop="true" android:text="Origin" />
|
||||||
|
<TextView style="@style/FavoriteListingTextView" android:text="to"
|
||||||
|
android:layout_below="@id/originText" android:paddingLeft="15dp"
|
||||||
|
android:paddingRight="8dp" android:id="@+id/to" />
|
||||||
|
<TextView android:id="@+id/destinationText" style="@style/FavoriteListingTextView"
|
||||||
|
android:layout_below="@id/originText" android:layout_toRightOf="@id/to"
|
||||||
|
android:text="Destination" />
|
||||||
|
</RelativeLayout>
|
14
res/layout/main.xml
Normal file
14
res/layout/main.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical" android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent">
|
||||||
|
<TextView android:id="@+id/listTitle" android:layout_width="fill_parent"
|
||||||
|
android:textSize="24dp" android:textStyle="bold"
|
||||||
|
android:layout_height="wrap_content" android:paddingLeft="5dp"
|
||||||
|
android:paddingRight="5dp" android:paddingBottom="10dp" />
|
||||||
|
<ListView android:layout_height="wrap_content"
|
||||||
|
android:layout_width="fill_parent" android:layout_weight="1"
|
||||||
|
android:id="@android:id/list" />
|
||||||
|
<TextView android:id="@android:id/empty" android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content" android:layout_weight="1" />
|
||||||
|
</LinearLayout>
|
8
res/layout/simple_spinner_item.xml
Normal file
8
res/layout/simple_spinner_item.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent" android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="15dip" android:paddingBottom="15dip"
|
||||||
|
android:paddingLeft="5dip" android:paddingRight="5dip" android:id="@android:id/text1"
|
||||||
|
android:textColor="#cccccc" android:ellipsize="marquee"
|
||||||
|
android:singleLine="true">
|
||||||
|
</TextView>
|
5
res/menu/favorites_menu.xml
Normal file
5
res/menu/favorites_menu.xml
Normal 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/add_favorite" android:icon="@android:drawable/ic_menu_add"
|
||||||
|
android:id="@+id/add_favorite_menu_button"></item>
|
||||||
|
</menu>
|
4
res/values/colors.xml
Normal file
4
res/values/colors.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="black">#FF000000</color>
|
||||||
|
</resources>
|
18
res/values/strings.xml
Normal file
18
res/values/strings.xml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Don\'t Miss The Bart</string>
|
||||||
|
<string name="favorite_routes">Favorite Routes</string>
|
||||||
|
<string name="empty_favorites_list_message">Press the menu button and select "Add
|
||||||
|
favorite" to
|
||||||
|
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="destination">Destination</string>
|
||||||
|
<string name="save">Save</string>
|
||||||
|
<string name="cancel">Cancel</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_origin">You must select an origin station</string>
|
||||||
|
<string name="arrival_wait_message">Please wait while real time arrival data is loaded...</string>
|
||||||
|
</resources>
|
34
res/values/styles.xml
Normal file
34
res/values/styles.xml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Base application theme is the default theme. -->
|
||||||
|
<style name="Theme" parent="android:Theme"></style>
|
||||||
|
|
||||||
|
<style name="ButtonBar">
|
||||||
|
<item name="android:layout_width">fill_parent</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:paddingTop">10dp</item>
|
||||||
|
<item name="android:paddingBottom">10dp</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="FavoriteListingTextView">
|
||||||
|
<item name="android:layout_width">wrap_content</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:textSize">20dp</item>
|
||||||
|
<item name="android:ellipsize">end</item>
|
||||||
|
<item name="android:singleLine">true</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="ArrivalDestinationText">
|
||||||
|
<item name="android:layout_width">wrap_content</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:textSize">20dp</item>
|
||||||
|
<item name="android:singleLine">true</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="ArrivalCountdownText">
|
||||||
|
<item name="android:layout_width">wrap_content</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:textSize">20dp</item>
|
||||||
|
<item name="android:singleLine">true</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
92
src/com/dougkeen/bart/AddFavoriteActivity.java
Normal file
92
src/com/dougkeen/bart/AddFavoriteActivity.java
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
package com.dougkeen.bart;
|
||||||
|
|
||||||
|
import com.dougkeen.bart.data.FavoritesColumns;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
import android.widget.SpinnerAdapter;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
public class AddFavoriteActivity extends Activity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
setContentView(R.layout.add_favorite);
|
||||||
|
|
||||||
|
SpinnerAdapter originSpinnerAdapter = new ArrayAdapter<Station>(this,
|
||||||
|
R.layout.simple_spinner_item, Station.values());
|
||||||
|
((Spinner) findViewById(R.id.origin_spinner))
|
||||||
|
.setAdapter(originSpinnerAdapter);
|
||||||
|
|
||||||
|
SpinnerAdapter destinationSpinnerAdapter = new ArrayAdapter<Station>(
|
||||||
|
this,
|
||||||
|
R.layout.simple_spinner_item, Station.values());
|
||||||
|
((Spinner) findViewById(R.id.destination_spinner))
|
||||||
|
.setAdapter(destinationSpinnerAdapter);
|
||||||
|
|
||||||
|
Button saveButton = (Button) findViewById(R.id.saveButton);
|
||||||
|
saveButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
onSaveButtonClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Button cancelButton = (Button) findViewById(R.id.cancelButton);
|
||||||
|
cancelButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
setResult(RESULT_CANCELED);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onSaveButtonClick() {
|
||||||
|
Station origin = (Station) ((Spinner) findViewById(R.id.origin_spinner))
|
||||||
|
.getSelectedItem();
|
||||||
|
Station destination = (Station) ((Spinner) findViewById(R.id.destination_spinner))
|
||||||
|
.getSelectedItem();
|
||||||
|
|
||||||
|
if (origin == null) {
|
||||||
|
Toast.makeText(this, com.dougkeen.bart.R.string.error_null_origin,
|
||||||
|
Toast.LENGTH_LONG);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (destination == null) {
|
||||||
|
Toast.makeText(this,
|
||||||
|
com.dougkeen.bart.R.string.error_null_destination,
|
||||||
|
Toast.LENGTH_LONG);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (origin.equals(destination)) {
|
||||||
|
Toast.makeText(
|
||||||
|
this,
|
||||||
|
com.dougkeen.bart.R.string.error_matching_origin_and_destination,
|
||||||
|
Toast.LENGTH_LONG);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(FavoritesColumns.FROM_STATION.string, origin.abbreviation);
|
||||||
|
values.put(FavoritesColumns.TO_STATION.string, destination.abbreviation);
|
||||||
|
|
||||||
|
Uri newUri = getContentResolver().insert(
|
||||||
|
Constants.FAVORITE_CONTENT_URI, values);
|
||||||
|
setResult(RESULT_OK, (new Intent()).setAction(newUri.toString()));
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
11
src/com/dougkeen/bart/Constants.java
Normal file
11
src/com/dougkeen/bart/Constants.java
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package com.dougkeen.bart;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
public class Constants {
|
||||||
|
public static final String AUTHORITY = "com.dougkeen.bart.dataprovider";
|
||||||
|
public static final String FAVORITE_CONTENT_TYPE = "vnd.android.cursor.dir/com.dougkeen.bart.favorite";
|
||||||
|
public static final String FAVORITE_CONTENT_ITEM_TYPE = "vnd.android.cursor.item/com.dougkeen.bart.favorite";
|
||||||
|
public static final Uri FAVORITE_CONTENT_URI = Uri.parse("content://"
|
||||||
|
+ AUTHORITY + "/favorites");
|
||||||
|
}
|
95
src/com/dougkeen/bart/EtdContentHandler.java
Normal file
95
src/com/dougkeen/bart/EtdContentHandler.java
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package com.dougkeen.bart;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.xml.sax.Attributes;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
import org.xml.sax.helpers.DefaultHandler;
|
||||||
|
|
||||||
|
import com.dougkeen.bart.data.Arrival;
|
||||||
|
import com.dougkeen.bart.data.RealTimeArrivals;
|
||||||
|
|
||||||
|
public class EtdContentHandler extends DefaultHandler {
|
||||||
|
public EtdContentHandler(Station origin, Station destination,
|
||||||
|
List<Route> routes) {
|
||||||
|
super();
|
||||||
|
realTimeArrivals = new RealTimeArrivals(origin, destination, routes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static List<String> TAGS = Arrays.asList("date", "time",
|
||||||
|
"abbreviation", "minutes", "platform", "direction", "length",
|
||||||
|
"hexcolor", "bikeflag");
|
||||||
|
|
||||||
|
private RealTimeArrivals realTimeArrivals;
|
||||||
|
|
||||||
|
public RealTimeArrivals getRealTimeArrivals() {
|
||||||
|
return realTimeArrivals;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String date;
|
||||||
|
private String currentDestination;
|
||||||
|
private String currentValue;
|
||||||
|
private Arrival currentArrival;
|
||||||
|
private boolean isParsingTag;
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
if (localName.equals("estimate")) {
|
||||||
|
currentArrival = new Arrival();
|
||||||
|
currentArrival.setDestination(Station
|
||||||
|
.getByAbbreviation(currentDestination));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endElement(String uri, String localName, String qName)
|
||||||
|
throws SAXException {
|
||||||
|
if (localName.equals("date")) {
|
||||||
|
date = currentValue;
|
||||||
|
} else if (localName.equals("time")) {
|
||||||
|
realTimeArrivals.setTime(Date.parse(date + " " + currentValue));
|
||||||
|
} else if (localName.equals("abbreviation")) {
|
||||||
|
currentDestination = currentValue;
|
||||||
|
} else if (localName.equals("minutes")) {
|
||||||
|
if (currentValue.equalsIgnoreCase("arrived")) {
|
||||||
|
currentArrival.setMinutes(0);
|
||||||
|
} else {
|
||||||
|
currentArrival.setMinutes(Integer.parseInt(currentValue));
|
||||||
|
}
|
||||||
|
} else if (localName.equals("platform")) {
|
||||||
|
currentArrival.setPlatform(currentValue);
|
||||||
|
} else if (localName.equals("direction")) {
|
||||||
|
currentArrival.setDirection(currentValue);
|
||||||
|
} else if (localName.equals("length")) {
|
||||||
|
currentArrival.setTrainLength(Integer.parseInt(currentValue));
|
||||||
|
} else if (localName.equals("hexcolor")) {
|
||||||
|
currentArrival.setDestinationColor("#ff"
|
||||||
|
+ currentValue.substring(1));
|
||||||
|
} else if (localName.equals("bikeflag")) {
|
||||||
|
currentArrival.setBikeAllowed(currentValue.equalsIgnoreCase("1"));
|
||||||
|
} else if (localName.equals("estimate")) {
|
||||||
|
realTimeArrivals.addArrival(currentArrival);
|
||||||
|
currentArrival = null;
|
||||||
|
} else if (localName.equals("etd")) {
|
||||||
|
currentDestination = null;
|
||||||
|
} else if (localName.equals("station")) {
|
||||||
|
realTimeArrivals.sortArrivals();
|
||||||
|
}
|
||||||
|
isParsingTag = false;
|
||||||
|
currentValue = null;
|
||||||
|
}
|
||||||
|
}
|
83
src/com/dougkeen/bart/FavoritesDashboardActivity.java
Normal file
83
src/com/dougkeen/bart/FavoritesDashboardActivity.java
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package com.dougkeen.bart;
|
||||||
|
|
||||||
|
import com.dougkeen.bart.data.FavoritesColumns;
|
||||||
|
|
||||||
|
import android.app.ListActivity;
|
||||||
|
import android.content.ContentUris;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.SimpleCursorAdapter;
|
||||||
|
import android.widget.SimpleCursorAdapter.ViewBinder;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
public class FavoritesDashboardActivity extends ListActivity {
|
||||||
|
protected Cursor mQuery;
|
||||||
|
|
||||||
|
/** Called when the activity is first created. */
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
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[] {
|
||||||
|
FavoritesColumns._ID.string,
|
||||||
|
FavoritesColumns.FROM_STATION.string,
|
||||||
|
FavoritesColumns.TO_STATION.string }, null, null,
|
||||||
|
FavoritesColumns._ID.string);
|
||||||
|
|
||||||
|
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
|
||||||
|
R.layout.favorite_listing,
|
||||||
|
mQuery,
|
||||||
|
new String[] { FavoritesColumns.FROM_STATION.string,
|
||||||
|
FavoritesColumns.TO_STATION.string },
|
||||||
|
new int[] { R.id.originText,
|
||||||
|
R.id.destinationText });
|
||||||
|
adapter.setViewBinder(new ViewBinder() {
|
||||||
|
@Override
|
||||||
|
public boolean setViewValue(View view, Cursor cursor,
|
||||||
|
int columnIndex) {
|
||||||
|
((TextView) view).setText(Station.getByAbbreviation(cursor
|
||||||
|
.getString(columnIndex)).name);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setListAdapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
MenuInflater inflater = getMenuInflater();
|
||||||
|
inflater.inflate(R.menu.favorites_menu, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
int itemId = item.getItemId();
|
||||||
|
if (itemId == R.id.add_favorite_menu_button) {
|
||||||
|
startActivity(new Intent(Intent.ACTION_INSERT,
|
||||||
|
Constants.FAVORITE_CONTENT_URI));
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onListItemClick(ListView l, View v, int position, long id) {
|
||||||
|
startActivity(new Intent(Intent.ACTION_VIEW,
|
||||||
|
ContentUris.withAppendedId(Constants.FAVORITE_CONTENT_URI, id)));
|
||||||
|
}
|
||||||
|
}
|
109
src/com/dougkeen/bart/GetRealTimeArrivalsTask.java
Normal file
109
src/com/dougkeen/bart/GetRealTimeArrivalsTask.java
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package com.dougkeen.bart;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
import com.dougkeen.bart.data.RealTimeArrivals;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Xml;
|
||||||
|
|
||||||
|
public abstract class GetRealTimeArrivalsTask extends
|
||||||
|
AsyncTask<GetRealTimeArrivalsTask.Params, Integer, RealTimeArrivals> {
|
||||||
|
|
||||||
|
private final static String API_KEY = "5LD9-IAYI-TRAT-MHHW";
|
||||||
|
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 = 3;
|
||||||
|
|
||||||
|
private IOException mIOException;
|
||||||
|
|
||||||
|
private List<Route> mRoutes;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected RealTimeArrivals doInBackground(Params... paramsArray) {
|
||||||
|
// Always expect one param
|
||||||
|
Params params = paramsArray[0];
|
||||||
|
|
||||||
|
mRoutes = params.origin.getRoutesForDestination(params.destination);
|
||||||
|
|
||||||
|
URL sourceUrl;
|
||||||
|
try {
|
||||||
|
sourceUrl = new URL(String.format(API_URL,
|
||||||
|
params.origin.abbreviation, mRoutes.get(0).getDirection()));
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getArrivalsFromNetwork(params, sourceUrl, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RealTimeArrivals getArrivalsFromNetwork(Params params,
|
||||||
|
URL sourceUrl, int attemptNumber) {
|
||||||
|
try {
|
||||||
|
EtdContentHandler handler = new EtdContentHandler(params.origin,
|
||||||
|
params.destination, mRoutes);
|
||||||
|
Xml.parse(sourceUrl.openStream(), Xml.findEncodingByName("UTF-8"),
|
||||||
|
handler);
|
||||||
|
final RealTimeArrivals realTimeArrivals = handler
|
||||||
|
.getRealTimeArrivals();
|
||||||
|
return realTimeArrivals;
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (attemptNumber < MAX_ATTEMPTS - 1) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(5000);
|
||||||
|
} catch (InterruptedException interrupt) {
|
||||||
|
// Ignore... just go on to next attempt
|
||||||
|
}
|
||||||
|
return getArrivalsFromNetwork(params, sourceUrl,
|
||||||
|
attemptNumber++);
|
||||||
|
} else {
|
||||||
|
mIOException = e;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (SAXException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Params {
|
||||||
|
public Params(Station origin, Station destination) {
|
||||||
|
super();
|
||||||
|
this.origin = origin;
|
||||||
|
this.destination = destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Station origin;
|
||||||
|
private Station destination;
|
||||||
|
|
||||||
|
public Station getOrigin() {
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Station getDestination() {
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(RealTimeArrivals result) {
|
||||||
|
if (result != null) {
|
||||||
|
onResult(result);
|
||||||
|
} else {
|
||||||
|
onNetworkError(mIOException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void onResult(RealTimeArrivals result);
|
||||||
|
|
||||||
|
public abstract void onNetworkError(IOException e);
|
||||||
|
}
|
108
src/com/dougkeen/bart/Line.java
Normal file
108
src/com/dougkeen/bart/Line.java
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package com.dougkeen.bart;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
public enum Line {
|
||||||
|
RED(false, Station.MLBR, Station.SBRN, Station.SSAN, Station.COLM,
|
||||||
|
Station.DALY, Station.BALB, Station.GLEN, Station._24TH,
|
||||||
|
Station._16TH, Station.CIVC, Station.POWL, Station.MONT,
|
||||||
|
Station.EMBR, Station.WOAK, Station._12TH, Station._19TH,
|
||||||
|
Station.MCAR, Station.ASHB, Station.DBRK, Station.NBRK,
|
||||||
|
Station.PLZA, Station.DELN, Station.RICH),
|
||||||
|
ORANGE(false, Station.FRMT, Station.UCTY, Station.SHAY,
|
||||||
|
Station.HAYW, Station.BAYF, Station.SANL, Station.COLS,
|
||||||
|
Station.FTVL, Station.LAKE, Station._12TH, Station._19TH,
|
||||||
|
Station.MCAR, Station.ASHB, Station.DBRK, Station.NBRK,
|
||||||
|
Station.PLZA, Station.DELN, Station.RICH),
|
||||||
|
YELLOW(false, Station.MLBR, Station.SFIA, Station.SBRN,
|
||||||
|
Station.SSAN, Station.COLM, Station.DALY, Station.BALB,
|
||||||
|
Station.GLEN, Station._24TH, Station._16TH, Station.CIVC,
|
||||||
|
Station.POWL, Station.MONT, Station.EMBR, Station.WOAK,
|
||||||
|
Station._12TH, Station._19TH, Station.MCAR, Station.ROCK,
|
||||||
|
Station.ORIN, Station.LAFY, Station.WCRK, Station.PHIL,
|
||||||
|
Station.CONC, Station.NCON),
|
||||||
|
BLUE(true, Station.DALY, Station.BALB, Station.GLEN, Station._24TH,
|
||||||
|
Station._16TH, Station.CIVC, Station.POWL, Station.MONT,
|
||||||
|
Station.EMBR, Station.WOAK, Station.LAKE, Station.FTVL,
|
||||||
|
Station.COLS, Station.SANL, Station.BAYF, Station.CAST,
|
||||||
|
Station.WDUB, Station.DUBL),
|
||||||
|
GREEN(true, Station.DALY, Station.BALB, Station.GLEN, Station._24TH,
|
||||||
|
Station._16TH, Station.CIVC, Station.POWL, Station.MONT,
|
||||||
|
Station.EMBR, Station.WOAK, Station.LAKE, Station.FTVL,
|
||||||
|
Station.COLS, Station.SANL, Station.BAYF, Station.HAYW,
|
||||||
|
Station.SHAY, Station.UCTY, Station.FRMT),
|
||||||
|
YELLOW_ORANGE_TRANSFER(YELLOW, ORANGE, Station.MLBR, Station.SFIA,
|
||||||
|
Station.SBRN, Station.SSAN, Station.COLM, Station.DALY,
|
||||||
|
Station.BALB, Station.GLEN, Station._24TH, Station._16TH,
|
||||||
|
Station.CIVC, Station.POWL, Station.MONT, Station.EMBR,
|
||||||
|
Station.WOAK, Station._12TH, Station._19TH,
|
||||||
|
Station.MCAR, Station.ASHB, Station.DBRK, Station.NBRK,
|
||||||
|
Station.PLZA, Station.DELN, Station.RICH);
|
||||||
|
|
||||||
|
public final List<Station> stations;
|
||||||
|
|
||||||
|
protected final boolean directionMayInvert;
|
||||||
|
|
||||||
|
protected final boolean requiresTransfer;
|
||||||
|
|
||||||
|
protected final Line transferLine1;
|
||||||
|
|
||||||
|
protected final Line transferLine2;
|
||||||
|
|
||||||
|
private Line(boolean directionMayInvert,
|
||||||
|
Station... stationArray) {
|
||||||
|
this.requiresTransfer = false;
|
||||||
|
this.directionMayInvert = directionMayInvert;
|
||||||
|
stations = Arrays.asList(stationArray);
|
||||||
|
this.transferLine1 = null;
|
||||||
|
this.transferLine2 = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Line(Line transferLine1, Line transferLine2,
|
||||||
|
Station... stationArray) {
|
||||||
|
this.requiresTransfer = true;
|
||||||
|
this.directionMayInvert = false;
|
||||||
|
stations = Arrays.asList(stationArray);
|
||||||
|
this.transferLine1 = transferLine1;
|
||||||
|
this.transferLine2 = transferLine2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Collection<Line> getLinesForStation(Station station) {
|
||||||
|
Collection<Line> lines = new ArrayList<Line>();
|
||||||
|
for (Line line : Line.values()) {
|
||||||
|
if (line.stations.contains(station)) {
|
||||||
|
lines.add(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<Station> getPotentialDestinations(Station station) {
|
||||||
|
Set<Station> destinations = new TreeSet<Station>();
|
||||||
|
|
||||||
|
for (Line line : getLinesForStation(station)) {
|
||||||
|
destinations.addAll(line.stations);
|
||||||
|
}
|
||||||
|
|
||||||
|
destinations.remove(station);
|
||||||
|
|
||||||
|
return destinations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean trainDestinationIsApplicable(Station station) {
|
||||||
|
if (transferLine1 != null && transferLine1.stations.contains(station)) {
|
||||||
|
return true;
|
||||||
|
} else if (transferLine2 != null
|
||||||
|
&& transferLine2.stations.contains(station)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return stations.contains(station);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
66
src/com/dougkeen/bart/Route.java
Normal file
66
src/com/dougkeen/bart/Route.java
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package com.dougkeen.bart;
|
||||||
|
|
||||||
|
public class Route {
|
||||||
|
private Station origin;
|
||||||
|
private Station destination;
|
||||||
|
private Line line;
|
||||||
|
private boolean requiresTransfer;
|
||||||
|
private String direction;
|
||||||
|
|
||||||
|
public Station getOrigin() {
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrigin(Station origin) {
|
||||||
|
this.origin = origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Station getDestination() {
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDestination(Station destination) {
|
||||||
|
this.destination = destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Line getLine() {
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLine(Line line) {
|
||||||
|
this.line = line;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasTransfer() {
|
||||||
|
return requiresTransfer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTransfer(boolean requiresTransfer) {
|
||||||
|
this.requiresTransfer = requiresTransfer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDirection() {
|
||||||
|
return direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDirection(String direction) {
|
||||||
|
this.direction = direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append("Route [origin=");
|
||||||
|
builder.append(origin);
|
||||||
|
builder.append(", destination=");
|
||||||
|
builder.append(destination);
|
||||||
|
builder.append(", line=");
|
||||||
|
builder.append(line);
|
||||||
|
builder.append(", requiresTransfer=");
|
||||||
|
builder.append(requiresTransfer);
|
||||||
|
builder.append(", direction=");
|
||||||
|
builder.append(direction);
|
||||||
|
builder.append("]");
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
165
src/com/dougkeen/bart/Station.java
Normal file
165
src/com/dougkeen/bart/Station.java
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
package com.dougkeen.bart;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public enum Station {
|
||||||
|
_12TH("12th", "12th St./Oakland City Center", false, "bayf"),
|
||||||
|
_16TH("16th", "16th St. Mission", false),
|
||||||
|
_19TH("19th", "19th St./Oakland", false, "bayf"),
|
||||||
|
_24TH("24th", "24th St. Mission", false),
|
||||||
|
ASHB("ashb", "Ashby", false, "mcar"),
|
||||||
|
BALB("balb", "Balboa Park", false),
|
||||||
|
BAYF("bayf", "Bay Fair", true, "mcar"),
|
||||||
|
CAST("cast", "Castro Valley", false, "bayf"),
|
||||||
|
CIVC("civc", "Civic Center", false),
|
||||||
|
COLS("cols", "Coliseum/Oakland Airport", true, "mcar"),
|
||||||
|
COLM("colm", "Colma", false, "balb", "balb"),
|
||||||
|
CONC("conc", "Concord", false, "mcar"),
|
||||||
|
DALY("daly", "Daly City", false),
|
||||||
|
DBRK("dbrk", "Downtown Berkeley", false, "mcar"),
|
||||||
|
DUBL("dubl", "Dublin/Pleasanton", false, "bayf"),
|
||||||
|
DELN("deln", "El Cerrito del Norte", false, "mcar"),
|
||||||
|
PLZA("plza", "El Cerrito Plaza", false, "mcar"),
|
||||||
|
EMBR("embr", "Embarcadero", false),
|
||||||
|
FRMT("frmt", "Fremont", true, "bayf"),
|
||||||
|
FTVL("ftvl", "Fruitvale", true, "mcar"),
|
||||||
|
GLEN("glen", "Glen Park", false),
|
||||||
|
HAYW("hayw", "Hayward", true, "bayf"),
|
||||||
|
LAFY("lafy", "Lafayette", false, "mcar"),
|
||||||
|
LAKE("lake", "Lake Merritt", true, "mcar"),
|
||||||
|
MCAR("mcar", "MacArthur", false, "bayf"),
|
||||||
|
MLBR("mlbr", "Millbrae", false, "balb", "balb"),
|
||||||
|
MONT("mont", "Montgomery St.", false),
|
||||||
|
NBRK("nbrk", "North Berkeley", false, "mcar"),
|
||||||
|
NCON("ncon", "North Concord/Martinez", false, "mcar"),
|
||||||
|
ORIN("orin", "Orinda", false, "mcar"),
|
||||||
|
PITT("pitt", "Pittsburg/Bay Point", false, "mcar"),
|
||||||
|
PHIL("phil", "Pleasant Hill", false, "mcar"),
|
||||||
|
POWL("powl", "Powell St.", false),
|
||||||
|
RICH("rich", "Richmond", false, "mcar"),
|
||||||
|
ROCK("rock", "Rockridge", false, "mcar"),
|
||||||
|
SBRN("sbrn", "San Bruno", false, "balb", "balb"),
|
||||||
|
SANL("sanl", "San Leandro", true, "mcar"),
|
||||||
|
SFIA("sfia", "SFO Airport", false, "sbrn", "balb"),
|
||||||
|
SHAY("shay", "South Hayward", true, "bayf"),
|
||||||
|
SSAN("ssan", "South San Francisco", false, "balb", "balb"),
|
||||||
|
UCTY("ucty", "Union City", true, "bayf"),
|
||||||
|
WCRK("wcrk", "Walnut Creek", false, "mcar"),
|
||||||
|
WDUB("wdub", "West Dublin/Pleasanton", false, "bayf"),
|
||||||
|
WOAK("woak", "West Oakland", false);
|
||||||
|
|
||||||
|
public final String abbreviation;
|
||||||
|
public final String name;
|
||||||
|
public final boolean invertDirection;
|
||||||
|
protected final String inboundTransferStation;
|
||||||
|
protected final String outboundTransferStation;
|
||||||
|
|
||||||
|
private Station(String abbreviation, String name, boolean invertDirection) {
|
||||||
|
this.abbreviation = abbreviation;
|
||||||
|
this.name = name;
|
||||||
|
this.invertDirection = invertDirection;
|
||||||
|
this.inboundTransferStation = null;
|
||||||
|
this.outboundTransferStation = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Station(String abbreviation, String name, boolean invertDirection,
|
||||||
|
String transferStation) {
|
||||||
|
this.abbreviation = abbreviation;
|
||||||
|
this.name = name;
|
||||||
|
this.invertDirection = invertDirection;
|
||||||
|
this.inboundTransferStation = transferStation;
|
||||||
|
this.outboundTransferStation = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Station(String abbreviation, String name, boolean invertDirection,
|
||||||
|
String inboundTransferStation, String outboundTransferStation) {
|
||||||
|
this.abbreviation = abbreviation;
|
||||||
|
this.name = name;
|
||||||
|
this.invertDirection = invertDirection;
|
||||||
|
this.inboundTransferStation = inboundTransferStation;
|
||||||
|
this.outboundTransferStation = outboundTransferStation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Station getByAbbreviation(String abbr) {
|
||||||
|
if (abbr == null) {
|
||||||
|
return null;
|
||||||
|
} else if (Character.isDigit(abbr.charAt(0))) {
|
||||||
|
return Station.valueOf("_" + abbr.toUpperCase());
|
||||||
|
} else {
|
||||||
|
return Station.valueOf(abbr.toUpperCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Station getInboundTransferStation() {
|
||||||
|
return getByAbbreviation(inboundTransferStation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Station getOutboundTransferStation() {
|
||||||
|
return getByAbbreviation(outboundTransferStation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValidEndpointForDestination(Station dest, Station endpoint) {
|
||||||
|
for (Line line : Line.values()) {
|
||||||
|
int origIndex = line.stations.indexOf(this);
|
||||||
|
if (origIndex < 0)
|
||||||
|
continue;
|
||||||
|
int destIndex = line.stations.indexOf(dest);
|
||||||
|
if (destIndex < 0)
|
||||||
|
continue;
|
||||||
|
int endpointIndex = line.stations.indexOf(endpoint);
|
||||||
|
if (endpointIndex >= 0)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Route> getRoutesForDestination(Station dest) {
|
||||||
|
return getRoutesForDestination(dest, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Route> getRoutesForDestination(Station dest, boolean isTransfer) {
|
||||||
|
if (dest == null)
|
||||||
|
return null;
|
||||||
|
Boolean isNorth = null;
|
||||||
|
List<Route> returnList = new ArrayList<Route>();
|
||||||
|
for (Line line : Line.values()) {
|
||||||
|
int origIndex = line.stations.indexOf(this);
|
||||||
|
if (origIndex < 0)
|
||||||
|
continue;
|
||||||
|
int destIndex = line.stations.indexOf(dest);
|
||||||
|
if (destIndex < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
isNorth = (origIndex < destIndex);
|
||||||
|
if (line.directionMayInvert && this.invertDirection) {
|
||||||
|
isNorth = !isNorth;
|
||||||
|
}
|
||||||
|
Route route = new Route();
|
||||||
|
route.setOrigin(this);
|
||||||
|
route.setDestination(dest);
|
||||||
|
route.setDirection(isNorth ? "n" : "s");
|
||||||
|
route.setLine(line);
|
||||||
|
if (isTransfer || line.requiresTransfer) {
|
||||||
|
route.setTransfer(true);
|
||||||
|
} else {
|
||||||
|
route.setTransfer(false);
|
||||||
|
}
|
||||||
|
returnList.add(route);
|
||||||
|
}
|
||||||
|
if (isNorth == null) {
|
||||||
|
if (outboundTransferStation != null) {
|
||||||
|
returnList.addAll(getOutboundTransferStation()
|
||||||
|
.getRoutesForDestination(dest, true));
|
||||||
|
} else {
|
||||||
|
returnList.addAll(getRoutesForDestination(dest
|
||||||
|
.getInboundTransferStation(), true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return returnList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
195
src/com/dougkeen/bart/ViewArrivalsActivity.java
Normal file
195
src/com/dougkeen/bart/ViewArrivalsActivity.java
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
package com.dougkeen.bart;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import android.app.ListActivity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.dougkeen.bart.GetRealTimeArrivalsTask.Params;
|
||||||
|
import com.dougkeen.bart.data.Arrival;
|
||||||
|
import com.dougkeen.bart.data.FavoritesColumns;
|
||||||
|
import com.dougkeen.bart.data.RealTimeArrivals;
|
||||||
|
|
||||||
|
public class ViewArrivalsActivity extends ListActivity {
|
||||||
|
|
||||||
|
private Uri mUri;
|
||||||
|
|
||||||
|
private Station mOrigin;
|
||||||
|
private Station mDestination;
|
||||||
|
|
||||||
|
private ArrayAdapter<Arrival> mArrivalsAdapter;
|
||||||
|
|
||||||
|
private TextView mListTitleView;
|
||||||
|
|
||||||
|
private AsyncTask<Params, Integer, RealTimeArrivals> mGetArrivalsTask;
|
||||||
|
|
||||||
|
private boolean mIsAutoUpdating = false;
|
||||||
|
|
||||||
|
private final Runnable AUTO_UPDATE_RUNNABLE = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
runAutoUpdate();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private PowerManager.WakeLock mWakeLock;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.main);
|
||||||
|
|
||||||
|
final Intent intent = getIntent();
|
||||||
|
|
||||||
|
String action = intent.getAction();
|
||||||
|
|
||||||
|
if (Intent.ACTION_VIEW.equals(action)) {
|
||||||
|
mUri = intent.getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
Cursor cursor = managedQuery(mUri, new String[] {
|
||||||
|
FavoritesColumns.FROM_STATION.string,
|
||||||
|
FavoritesColumns.TO_STATION.string }, null, null, null);
|
||||||
|
|
||||||
|
if (!cursor.moveToFirst()) {
|
||||||
|
throw new IllegalStateException("URI not found: " + mUri.toString());
|
||||||
|
}
|
||||||
|
mOrigin = Station.getByAbbreviation(cursor.getString(0));
|
||||||
|
mDestination = Station.getByAbbreviation(cursor.getString(1));
|
||||||
|
|
||||||
|
String header = mOrigin.name + " to " + mDestination.name;
|
||||||
|
|
||||||
|
mListTitleView = (TextView) findViewById(R.id.listTitle);
|
||||||
|
mListTitleView.setText(header);
|
||||||
|
((TextView) findViewById(android.R.id.empty))
|
||||||
|
.setText(R.string.arrival_wait_message);
|
||||||
|
|
||||||
|
mArrivalsAdapter = new ArrayAdapter<Arrival>(
|
||||||
|
this, R.layout.simple_spinner_item);
|
||||||
|
setListAdapter(mArrivalsAdapter);
|
||||||
|
|
||||||
|
fetchLatestArrivals();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWindowFocusChanged(boolean hasFocus) {
|
||||||
|
super.onWindowFocusChanged(hasFocus);
|
||||||
|
if (hasFocus) {
|
||||||
|
PowerManager powerManaer = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||||
|
mWakeLock = powerManaer.newWakeLock(
|
||||||
|
PowerManager.SCREEN_DIM_WAKE_LOCK, "ViewArrivalsActivity");
|
||||||
|
mWakeLock.acquire();
|
||||||
|
} else if (mWakeLock != null) {
|
||||||
|
mWakeLock.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetchLatestArrivals() {
|
||||||
|
mGetArrivalsTask = new GetRealTimeArrivalsTask() {
|
||||||
|
@Override
|
||||||
|
public void onResult(RealTimeArrivals result) {
|
||||||
|
processLatestArrivals(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNetworkError(IOException e) {
|
||||||
|
Toast.makeText(ViewArrivalsActivity.this, e.getMessage(),
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mGetArrivalsTask.execute(new GetRealTimeArrivalsTask.Params(mOrigin,
|
||||||
|
mDestination));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void processLatestArrivals(RealTimeArrivals result) {
|
||||||
|
Arrival firstArrival = null;
|
||||||
|
final List<Arrival> arrivals = result.getArrivals();
|
||||||
|
if (mArrivalsAdapter.getCount() > 0) {
|
||||||
|
int adapterIndex = -1;
|
||||||
|
for (Arrival arrival : arrivals) {
|
||||||
|
adapterIndex++;
|
||||||
|
Arrival existingArrival = null;
|
||||||
|
if (adapterIndex < mArrivalsAdapter.getCount()) {
|
||||||
|
existingArrival = mArrivalsAdapter.getItem(adapterIndex);
|
||||||
|
}
|
||||||
|
while (existingArrival != null
|
||||||
|
&& !arrival.equals(existingArrival)) {
|
||||||
|
mArrivalsAdapter.remove(existingArrival);
|
||||||
|
if (adapterIndex < mArrivalsAdapter.getCount()) {
|
||||||
|
existingArrival = mArrivalsAdapter
|
||||||
|
.getItem(adapterIndex);
|
||||||
|
} else {
|
||||||
|
existingArrival = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (existingArrival != null) {
|
||||||
|
existingArrival.mergeEstimate(arrival);
|
||||||
|
} else {
|
||||||
|
mArrivalsAdapter.add(arrival);
|
||||||
|
existingArrival = arrival;
|
||||||
|
}
|
||||||
|
if (firstArrival == null) {
|
||||||
|
firstArrival = existingArrival;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (Arrival arrival : arrivals) {
|
||||||
|
if (firstArrival == null) {
|
||||||
|
firstArrival = arrival;
|
||||||
|
}
|
||||||
|
mArrivalsAdapter.add(arrival);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mArrivalsAdapter.notifyDataSetChanged();
|
||||||
|
|
||||||
|
if (hasWindowFocus() && firstArrival != null) {
|
||||||
|
if (firstArrival.getUncertaintySeconds() > 17
|
||||||
|
|| firstArrival.getMinutes() == 0) {
|
||||||
|
// Get more data in 20s
|
||||||
|
mListTitleView.postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
fetchLatestArrivals();
|
||||||
|
}
|
||||||
|
}, 20000);
|
||||||
|
} else {
|
||||||
|
// Get more when next train arrives
|
||||||
|
mListTitleView.postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
fetchLatestArrivals();
|
||||||
|
}
|
||||||
|
}, firstArrival.getMinSecondsLeft() * 1000);
|
||||||
|
}
|
||||||
|
if (!mIsAutoUpdating) {
|
||||||
|
mIsAutoUpdating = true;
|
||||||
|
runAutoUpdate();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mIsAutoUpdating = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runAutoUpdate() {
|
||||||
|
if (mIsAutoUpdating) {
|
||||||
|
mArrivalsAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
if (hasWindowFocus()) {
|
||||||
|
mListTitleView.postDelayed(AUTO_UPDATE_RUNNABLE, 1000);
|
||||||
|
} else {
|
||||||
|
mIsAutoUpdating = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
219
src/com/dougkeen/bart/data/Arrival.java
Normal file
219
src/com/dougkeen/bart/data/Arrival.java
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
package com.dougkeen.bart.data;
|
||||||
|
|
||||||
|
import com.dougkeen.bart.Station;
|
||||||
|
|
||||||
|
public class Arrival implements Comparable<Arrival> {
|
||||||
|
public Arrival() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Arrival(String destinationAbbr, String destinationColor,
|
||||||
|
String platform, String direction, boolean bikeAllowed,
|
||||||
|
int trainLength, int minutes) {
|
||||||
|
super();
|
||||||
|
this.destination = Station.getByAbbreviation(destinationAbbr);
|
||||||
|
this.destinationColor = destinationColor;
|
||||||
|
this.platform = platform;
|
||||||
|
this.direction = direction;
|
||||||
|
this.bikeAllowed = bikeAllowed;
|
||||||
|
this.trainLength = trainLength;
|
||||||
|
this.minutes = minutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Station destination;
|
||||||
|
private String destinationColor;
|
||||||
|
private String platform;
|
||||||
|
private String direction;
|
||||||
|
private boolean bikeAllowed;
|
||||||
|
private int trainLength;
|
||||||
|
private boolean requiresTransfer;
|
||||||
|
|
||||||
|
private int minutes;
|
||||||
|
|
||||||
|
private long minEstimate;
|
||||||
|
private long maxEstimate;
|
||||||
|
|
||||||
|
public Station getDestination() {
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDestination(Station destination) {
|
||||||
|
this.destination = destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDestinationName() {
|
||||||
|
if (destination != null)
|
||||||
|
return destination.name;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDestinationAbbreviation() {
|
||||||
|
if (destination != null)
|
||||||
|
return destination.abbreviation;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDestinationColor() {
|
||||||
|
return destinationColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDestinationColor(String destinationColor) {
|
||||||
|
this.destinationColor = destinationColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPlatform() {
|
||||||
|
return platform;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlatform(String platform) {
|
||||||
|
this.platform = platform;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDirection() {
|
||||||
|
return direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDirection(String direction) {
|
||||||
|
this.direction = direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBikeAllowed() {
|
||||||
|
return bikeAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBikeAllowed(boolean bikeAllowed) {
|
||||||
|
this.bikeAllowed = bikeAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTrainLength() {
|
||||||
|
return trainLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTrainLength(int trainLength) {
|
||||||
|
this.trainLength = trainLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getRequiresTransfer() {
|
||||||
|
return requiresTransfer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRequiresTransfer(boolean requiresTransfer) {
|
||||||
|
this.requiresTransfer = requiresTransfer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMinutes() {
|
||||||
|
return minutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMinutes(int minutes) {
|
||||||
|
this.minutes = minutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getMinEstimate() {
|
||||||
|
return minEstimate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMinEstimate(long minEstimate) {
|
||||||
|
this.minEstimate = minEstimate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getMaxEstimate() {
|
||||||
|
return maxEstimate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxEstimate(long maxEstimate) {
|
||||||
|
this.maxEstimate = maxEstimate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getUncertaintySeconds() {
|
||||||
|
return (int) (maxEstimate - minEstimate + 1000) / 2000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMinSecondsLeft() {
|
||||||
|
return (int) ((getMinEstimate() - System.currentTimeMillis()) / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxSecondsLeft() {
|
||||||
|
return (int) ((getMaxEstimate() - System.currentTimeMillis()) / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMeanSecondsLeft() {
|
||||||
|
return (int) (((getMinEstimate() + getMaxEstimate()) / 2 - System
|
||||||
|
.currentTimeMillis()) / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void calculateEstimates(long originalEstimateTime) {
|
||||||
|
setMinEstimate(originalEstimateTime + (getMinutes() * 60 * 1000));
|
||||||
|
setMaxEstimate(getMinEstimate() + (59 * 1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mergeEstimate(Arrival arrival) {
|
||||||
|
setMinEstimate(Math.max(getMinEstimate(), arrival.getMinEstimate()));
|
||||||
|
setMaxEstimate(Math.min(getMaxEstimate(), arrival.getMaxEstimate()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Arrival another) {
|
||||||
|
return (this.getMinutes() > another.getMinutes()) ? 1 : (
|
||||||
|
(this.getMinutes() == another.getMinutes()) ? 0 : -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
Arrival other = (Arrival) obj;
|
||||||
|
if (bikeAllowed != other.bikeAllowed)
|
||||||
|
return false;
|
||||||
|
if (destination != other.destination)
|
||||||
|
return false;
|
||||||
|
if (destinationColor == null) {
|
||||||
|
if (other.destinationColor != null)
|
||||||
|
return false;
|
||||||
|
} else if (!destinationColor.equals(other.destinationColor))
|
||||||
|
return false;
|
||||||
|
if (direction == null) {
|
||||||
|
if (other.direction != null)
|
||||||
|
return false;
|
||||||
|
} else if (!direction.equals(other.direction))
|
||||||
|
return false;
|
||||||
|
if (platform == null) {
|
||||||
|
if (other.platform != null)
|
||||||
|
return false;
|
||||||
|
} else if (!platform.equals(other.platform))
|
||||||
|
return false;
|
||||||
|
if (trainLength != other.trainLength)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
long delta = (getMinEstimate() - other.getMinEstimate());
|
||||||
|
return delta > -60000 && delta < 60000;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append(destination);
|
||||||
|
if(requiresTransfer) {
|
||||||
|
builder.append(" (w/ xfer)");
|
||||||
|
}
|
||||||
|
builder.append(", ");
|
||||||
|
builder.append(trainLength);
|
||||||
|
int secondsLeft = getMeanSecondsLeft();
|
||||||
|
if (getMinutes() == 0 || secondsLeft < 0) {
|
||||||
|
builder.append(" car train has arrived");
|
||||||
|
} else {
|
||||||
|
builder.append(" car train in ");
|
||||||
|
builder.append(secondsLeft / 60);
|
||||||
|
builder.append("m, ");
|
||||||
|
builder.append(secondsLeft % 60);
|
||||||
|
builder.append("s, ±");
|
||||||
|
builder.append(getUncertaintySeconds());
|
||||||
|
builder.append("s");
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
188
src/com/dougkeen/bart/data/BartContentProvider.java
Normal file
188
src/com/dougkeen/bart/data/BartContentProvider.java
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
package com.dougkeen.bart.data;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import com.dougkeen.bart.Constants;
|
||||||
|
|
||||||
|
import android.content.ContentProvider;
|
||||||
|
import android.content.ContentUris;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.UriMatcher;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.SQLException;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteQueryBuilder;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
public class BartContentProvider extends ContentProvider {
|
||||||
|
|
||||||
|
private static final UriMatcher sUriMatcher;
|
||||||
|
private static HashMap<String, String> sFavoritesProjectionMap;
|
||||||
|
|
||||||
|
private static final int FAVORITES = 1;
|
||||||
|
private static final int FAVORITE_ID = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default sort order for events
|
||||||
|
*/
|
||||||
|
private static final String DEFAULT_SORT_ORDER = FavoritesColumns.FROM_STATION.string
|
||||||
|
+ " DESC";
|
||||||
|
|
||||||
|
static {
|
||||||
|
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||||
|
sUriMatcher.addURI(Constants.AUTHORITY, "favorites", FAVORITES);
|
||||||
|
sUriMatcher.addURI(Constants.AUTHORITY, "favorites/#", FAVORITE_ID);
|
||||||
|
|
||||||
|
sFavoritesProjectionMap = new HashMap<String, String>();
|
||||||
|
sFavoritesProjectionMap.put(FavoritesColumns._ID.string,
|
||||||
|
FavoritesColumns._ID.string);
|
||||||
|
sFavoritesProjectionMap.put(FavoritesColumns.FROM_STATION.string,
|
||||||
|
FavoritesColumns.FROM_STATION.string);
|
||||||
|
sFavoritesProjectionMap.put(FavoritesColumns.TO_STATION.string,
|
||||||
|
FavoritesColumns.TO_STATION.string);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DatabaseHelper mDatabaseHelper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreate() {
|
||||||
|
mDatabaseHelper = new DatabaseHelper(getContext());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType(Uri uri) {
|
||||||
|
int match = sUriMatcher.match(uri);
|
||||||
|
if (match == FAVORITES) {
|
||||||
|
return Constants.FAVORITE_CONTENT_TYPE;
|
||||||
|
} else if (match == FAVORITE_ID) {
|
||||||
|
return Constants.FAVORITE_CONTENT_ITEM_TYPE;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unknown URI " + uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor query(Uri uri, String[] projection, String selection,
|
||||||
|
String[] selectionArgs,
|
||||||
|
String sortOrder) {
|
||||||
|
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
|
||||||
|
|
||||||
|
SQLiteDatabase db = mDatabaseHelper.getReadableDatabase();
|
||||||
|
|
||||||
|
String orderBy = sortOrder;
|
||||||
|
|
||||||
|
int match = sUriMatcher.match(uri);
|
||||||
|
|
||||||
|
if (match == FAVORITES) {
|
||||||
|
qb.setTables(DatabaseHelper.FAVORITES_TABLE_NAME);
|
||||||
|
qb.setProjectionMap(sFavoritesProjectionMap);
|
||||||
|
} else if (match == FAVORITE_ID) {
|
||||||
|
qb.setTables(DatabaseHelper.FAVORITES_TABLE_NAME);
|
||||||
|
qb.setProjectionMap(sFavoritesProjectionMap);
|
||||||
|
qb.appendWhere(FavoritesColumns._ID + " = "
|
||||||
|
+ uri.getPathSegments().get(1));
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unknown URI " + uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no sort order is specified use the default
|
||||||
|
if (TextUtils.isEmpty(orderBy)) {
|
||||||
|
orderBy = DEFAULT_SORT_ORDER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the database and run the query
|
||||||
|
Cursor cursor = qb.query(db, projection, selection, selectionArgs,
|
||||||
|
null, null, orderBy);
|
||||||
|
|
||||||
|
// Tell the cursor what uri to watch, so it knows when its source data
|
||||||
|
// changes
|
||||||
|
cursor.setNotificationUri(getContext().getContentResolver(), uri);
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Uri insert(Uri uri, ContentValues initialValues) {
|
||||||
|
// TODO: Hook this up to the REST service?
|
||||||
|
|
||||||
|
ContentValues values;
|
||||||
|
if (initialValues != null) {
|
||||||
|
values = new ContentValues(initialValues);
|
||||||
|
} else {
|
||||||
|
values = new ContentValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
|
||||||
|
|
||||||
|
// Validate the requested uri
|
||||||
|
int match = sUriMatcher.match(uri);
|
||||||
|
if (match == FAVORITES) {
|
||||||
|
long rowId = -1;
|
||||||
|
Cursor cursor = db
|
||||||
|
.query(DatabaseHelper.FAVORITES_TABLE_NAME,
|
||||||
|
new String[] { FavoritesColumns._ID.string },
|
||||||
|
FavoritesColumns.FROM_STATION + "=? AND "
|
||||||
|
+ FavoritesColumns.TO_STATION + "=?",
|
||||||
|
new String[] {
|
||||||
|
values.getAsString(FavoritesColumns.FROM_STATION.string),
|
||||||
|
values.getAsString(FavoritesColumns.TO_STATION.string) },
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
try {
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
rowId = cursor.getLong(0);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
CursorUtils.closeCursorQuietly(cursor);
|
||||||
|
}
|
||||||
|
if (rowId < 0) {
|
||||||
|
rowId = db.insert(DatabaseHelper.FAVORITES_TABLE_NAME,
|
||||||
|
FavoritesColumns.FROM_STATION.string, values);
|
||||||
|
}
|
||||||
|
if (rowId > 0) {
|
||||||
|
Uri eventUri = ContentUris.withAppendedId(
|
||||||
|
Constants.FAVORITE_CONTENT_URI, rowId);
|
||||||
|
getContext().getContentResolver().notifyChange(eventUri, null,
|
||||||
|
false);
|
||||||
|
return eventUri;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unknown URI " + uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new SQLException("Failed to insert row into " + uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int update(Uri uri, ContentValues values, String where,
|
||||||
|
String[] whereArgs) {
|
||||||
|
return 0;
|
||||||
|
// No updating supported yet
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int delete(Uri uri, String where, String[] whereArgs) {
|
||||||
|
// TODO: Sync with REST service?
|
||||||
|
SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
|
||||||
|
int count;
|
||||||
|
int match = sUriMatcher.match(uri);
|
||||||
|
if (match == FAVORITES) {
|
||||||
|
count = db.delete(DatabaseHelper.FAVORITES_TABLE_NAME, where,
|
||||||
|
whereArgs);
|
||||||
|
} else if (match == FAVORITE_ID) {
|
||||||
|
String favoriteId = uri.getPathSegments().get(1);
|
||||||
|
count = db.delete(DatabaseHelper.FAVORITES_TABLE_NAME,
|
||||||
|
FavoritesColumns._ID + " = "
|
||||||
|
+ favoriteId
|
||||||
|
+ (!TextUtils.isEmpty(where) ? " AND (" + where
|
||||||
|
+ ')' : ""), whereArgs);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unknown URI " + uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
getContext().getContentResolver().notifyChange(uri, null);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
15
src/com/dougkeen/bart/data/CursorUtils.java
Normal file
15
src/com/dougkeen/bart/data/CursorUtils.java
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package com.dougkeen.bart.data;
|
||||||
|
|
||||||
|
import android.database.Cursor;
|
||||||
|
|
||||||
|
public final class CursorUtils {
|
||||||
|
private CursorUtils() {
|
||||||
|
// Static only class
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final void closeCursorQuietly(Cursor cursor) {
|
||||||
|
if (cursor != null && !cursor.isClosed()) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
src/com/dougkeen/bart/data/DatabaseHelper.java
Normal file
29
src/com/dougkeen/bart/data/DatabaseHelper.java
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package com.dougkeen.bart.data;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
|
||||||
|
public class DatabaseHelper extends SQLiteOpenHelper {
|
||||||
|
|
||||||
|
private static final String DATABASE_NAME = "bart.dougkeen.db";
|
||||||
|
private static final int DATABASE_VERSION = 1;
|
||||||
|
|
||||||
|
public static final String FAVORITES_TABLE_NAME = "Favorites";
|
||||||
|
|
||||||
|
public DatabaseHelper(Context context) {
|
||||||
|
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(SQLiteDatabase db) {
|
||||||
|
db.execSQL("CREATE TABLE " + FAVORITES_TABLE_NAME + " (" +
|
||||||
|
FavoritesColumns._ID.getColumnDef() + " PRIMARY KEY, " +
|
||||||
|
FavoritesColumns.FROM_STATION.getColumnDef() + ", " +
|
||||||
|
FavoritesColumns.TO_STATION.getColumnDef() + ");");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
|
}
|
||||||
|
}
|
20
src/com/dougkeen/bart/data/FavoritesColumns.java
Normal file
20
src/com/dougkeen/bart/data/FavoritesColumns.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package com.dougkeen.bart.data;
|
||||||
|
|
||||||
|
public enum FavoritesColumns {
|
||||||
|
_ID("_id", "INTEGER"),
|
||||||
|
FROM_STATION("FROM_STATION", "TEXT"),
|
||||||
|
TO_STATION("TO_STATION", "TEXT");
|
||||||
|
|
||||||
|
// This class cannot be instantiated
|
||||||
|
private FavoritesColumns(String string, String type) {
|
||||||
|
this.string = string;
|
||||||
|
this.sqliteType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final String string;
|
||||||
|
public final String sqliteType;
|
||||||
|
|
||||||
|
protected String getColumnDef() {
|
||||||
|
return string + " " + sqliteType;
|
||||||
|
}
|
||||||
|
}
|
100
src/com/dougkeen/bart/data/RealTimeArrivals.java
Normal file
100
src/com/dougkeen/bart/data/RealTimeArrivals.java
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package com.dougkeen.bart.data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.dougkeen.bart.Route;
|
||||||
|
import com.dougkeen.bart.Station;
|
||||||
|
|
||||||
|
public class RealTimeArrivals {
|
||||||
|
public RealTimeArrivals(Station origin, Station destination,
|
||||||
|
List<Route> routes) {
|
||||||
|
this.origin = origin;
|
||||||
|
this.destination = destination;
|
||||||
|
this.routes = routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Station origin;
|
||||||
|
private Station destination;
|
||||||
|
private long time;
|
||||||
|
|
||||||
|
private List<Arrival> arrivals;
|
||||||
|
|
||||||
|
private List<Route> routes;
|
||||||
|
|
||||||
|
public Station getOrigin() {
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrigin(Station origin) {
|
||||||
|
this.origin = origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Station getDestination() {
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDestination(Station destination) {
|
||||||
|
this.destination = destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTime() {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTime(long time) {
|
||||||
|
this.time = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Arrival> getArrivals() {
|
||||||
|
if (arrivals == null) {
|
||||||
|
arrivals = new ArrayList<Arrival>();
|
||||||
|
}
|
||||||
|
return arrivals;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setArrivals(List<Arrival> arrivals) {
|
||||||
|
this.arrivals = arrivals;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addArrival(Arrival arrival) {
|
||||||
|
Station destination = Station.getByAbbreviation(arrival
|
||||||
|
.getDestinationAbbreviation());
|
||||||
|
for (Route route : routes) {
|
||||||
|
if (route.getLine().trainDestinationIsApplicable(destination)) {
|
||||||
|
arrival.setRequiresTransfer(route.hasTransfer());
|
||||||
|
getArrivals().add(arrival);
|
||||||
|
arrival.calculateEstimates(time);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sortArrivals() {
|
||||||
|
Collections.sort(getArrivals());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Route> getRoutes() {
|
||||||
|
return routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoutes(List<Route> routes) {
|
||||||
|
this.routes = routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append("RealTimeArrivals [origin=");
|
||||||
|
builder.append(origin);
|
||||||
|
builder.append(", destination=");
|
||||||
|
builder.append(destination);
|
||||||
|
builder.append(", time=");
|
||||||
|
builder.append(time);
|
||||||
|
builder.append(", arrivals=");
|
||||||
|
builder.append(arrivals);
|
||||||
|
builder.append("]");
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user