Refactored countdowns, implemented "Your train" selection
This commit is contained in:
parent
8bbdec5328
commit
ab7ab0a491
@ -1,5 +1,6 @@
|
|||||||
<?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"
|
||||||
|
xmlns:bart="http://schemas.android.com/apk/res/com.dougkeen.bart"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent" >
|
android:layout_height="fill_parent" >
|
||||||
|
|
||||||
@ -30,10 +31,12 @@
|
|||||||
style="@style/BikeIcon"
|
style="@style/BikeIcon"
|
||||||
android:src="@drawable/bike" />
|
android:src="@drawable/bike" />
|
||||||
|
|
||||||
<TextView
|
<com.dougkeen.bart.controls.CountdownTextView
|
||||||
android:id="@+id/countdown"
|
android:id="@+id/countdown"
|
||||||
style="@style/DepartureCountdownText"
|
style="@style/DepartureCountdownText"
|
||||||
android:gravity="right" />
|
android:gravity="right"
|
||||||
|
android:width="90dp"
|
||||||
|
bart:tickInterval="1" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
@ -43,12 +46,13 @@
|
|||||||
android:layout_below="@id/topRow"
|
android:layout_below="@id/topRow"
|
||||||
android:src="@drawable/xfer" />
|
android:src="@drawable/xfer" />
|
||||||
|
|
||||||
<TextSwitcher
|
<com.dougkeen.bart.controls.TimedTextSwitcher
|
||||||
android:id="@+id/trainLengthText"
|
android:id="@+id/trainLengthText"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/topRow"
|
android:layout_below="@id/topRow"
|
||||||
android:layout_toRightOf="@id/destinationColorBar" />
|
android:layout_toRightOf="@id/destinationColorBar"
|
||||||
|
bart:tickInterval="3" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/uncertainty"
|
android:id="@+id/uncertainty"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:bart="http://schemas.android.com/apk/res/com.dougkeen.bart"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="fill_parent"
|
||||||
android:orientation="vertical" >
|
android:orientation="vertical" >
|
||||||
@ -13,6 +14,82 @@
|
|||||||
android:paddingRight="5dp"
|
android:paddingRight="5dp"
|
||||||
android:textSize="24dp" />
|
android:textSize="24dp" />
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/yourTrainSection"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:background="#222"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:visibility="gone" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/yourTrainHeader"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:text="@string/your_train"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:textSize="20dp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/yourTrainDestinationColorBar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:layout_below="@id/yourTrainHeader"
|
||||||
|
android:src="@drawable/basic_rectangle" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/yourTrainDestinationText"
|
||||||
|
style="@style/DepartureDestinationText"
|
||||||
|
android:layout_below="@id/yourTrainHeader"
|
||||||
|
android:layout_toRightOf="@id/yourTrainDestinationColorBar"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:singleLine="true" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/yourTrainBikeIcon"
|
||||||
|
style="@style/BikeIcon"
|
||||||
|
android:layout_below="@id/yourTrainHeader"
|
||||||
|
android:layout_toRightOf="@id/yourTrainDestinationText"
|
||||||
|
android:src="@drawable/bike" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/yourTrainXferIcon"
|
||||||
|
style="@style/XferIcon"
|
||||||
|
android:layout_below="@id/yourTrainBikeIcon"
|
||||||
|
android:layout_toRightOf="@id/yourTrainDestinationText"
|
||||||
|
android:src="@drawable/xfer" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/yourTrainTrainLengthText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/yourTrainDestinationText"
|
||||||
|
android:layout_toRightOf="@id/yourTrainDestinationColorBar"
|
||||||
|
android:paddingLeft="5dp" />
|
||||||
|
|
||||||
|
<com.dougkeen.bart.controls.CountdownTextView
|
||||||
|
android:id="@+id/yourTrainDepartureCountdown"
|
||||||
|
style="@style/DepartureCountdownText"
|
||||||
|
android:layout_alignLeft="@id/yourTrainSection"
|
||||||
|
android:layout_alignRight="@id/yourTrainSection"
|
||||||
|
android:layout_below="@id/yourTrainTrainLengthText"
|
||||||
|
bart:tickInterval="1" />
|
||||||
|
|
||||||
|
<com.dougkeen.bart.controls.CountdownTextView
|
||||||
|
android:id="@+id/yourTrainArrivalCountdown"
|
||||||
|
style="@style/DepartureCountdownText"
|
||||||
|
android:layout_alignLeft="@id/yourTrainSection"
|
||||||
|
android:layout_alignRight="@id/yourTrainSection"
|
||||||
|
android:layout_below="@id/yourTrainDepartureCountdown"
|
||||||
|
android:ellipsize="end"
|
||||||
|
bart:tickInterval="5" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
<ListView
|
<ListView
|
||||||
android:id="@android:id/list"
|
android:id="@android:id/list"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
|
4
res/menu/departure_context_menu.xml
Normal file
4
res/menu/departure_context_menu.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||||
|
<item android:title="@string/getting_on_this_train" android:id="@+id/boardTrain"></item>
|
||||||
|
</menu>
|
@ -12,20 +12,27 @@
|
|||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
|
<attr name="tickInterval" format="integer" />
|
||||||
|
|
||||||
<declare-styleable name="AppTheme">
|
<declare-styleable name="AppTheme">
|
||||||
<attr name="actionbarCompatTitleStyle" format="reference" />
|
<attr name="actionbarCompatTitleStyle" format="reference" />
|
||||||
<attr name="actionbarCompatItemStyle" format="reference" />
|
<attr name="actionbarCompatItemStyle" format="reference" />
|
||||||
<attr name="actionbarCompatItemHomeStyle" format="reference" />
|
<attr name="actionbarCompatItemHomeStyle" format="reference" />
|
||||||
<attr name="actionbarCompatProgressIndicatorStyle" format="reference" />
|
<attr name="actionbarCompatProgressIndicatorStyle" format="reference" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
<declare-styleable name="BezelImageView">
|
<declare-styleable name="BezelImageView">
|
||||||
<attr name="maskDrawable" format="reference" />
|
<attr name="maskDrawable" format="reference" />
|
||||||
<attr name="borderDrawable" format="reference" />
|
<attr name="borderDrawable" format="reference" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
<declare-styleable name="CountdownTextView">
|
||||||
|
<attr name="tickInterval" />
|
||||||
|
</declare-styleable>
|
||||||
|
<declare-styleable name="TimedTextSwitcher">
|
||||||
|
<attr name="tickInterval" />
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
@ -32,4 +32,7 @@
|
|||||||
<string name="departures">Departures</string>
|
<string name="departures">Departures</string>
|
||||||
<string name="ok">OK</string>
|
<string name="ok">OK</string>
|
||||||
<string name="quick_departure_lookup">Quick departure lookup</string>
|
<string name="quick_departure_lookup">Quick departure lookup</string>
|
||||||
|
<string name="getting_on_this_train">I\'m getting on this train</string>
|
||||||
|
<string name="departure_options">Departure options</string>
|
||||||
|
<string name="your_train">Your train</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -94,7 +94,6 @@
|
|||||||
<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">20dp</item>
|
||||||
<item name="android:singleLine">true</item>
|
<item name="android:singleLine">true</item>
|
||||||
<item name="android:width">90dp</item>
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="DepartureUncertaintyText">
|
<style name="DepartureUncertaintyText">
|
||||||
|
@ -18,12 +18,13 @@ import android.widget.TextSwitcher;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.ViewSwitcher.ViewFactory;
|
import android.widget.ViewSwitcher.ViewFactory;
|
||||||
|
|
||||||
|
import com.dougkeen.bart.controls.CountdownTextView;
|
||||||
|
import com.dougkeen.bart.controls.TimedTextSwitcher;
|
||||||
import com.dougkeen.bart.model.Departure;
|
import com.dougkeen.bart.model.Departure;
|
||||||
|
import com.dougkeen.bart.model.TextProvider;
|
||||||
|
|
||||||
public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
|
public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
|
||||||
|
|
||||||
private int refreshCounter = 1;
|
|
||||||
|
|
||||||
public DepartureArrayAdapter(Context context, int textViewResourceId,
|
public DepartureArrayAdapter(Context context, int textViewResourceId,
|
||||||
Departure[] objects) {
|
Departure[] objects) {
|
||||||
super(context, textViewResourceId, objects);
|
super(context, textViewResourceId, objects);
|
||||||
@ -53,10 +54,6 @@ public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
|
|||||||
super(context, textViewResourceId);
|
super(context, textViewResourceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void incrementRefreshCounter() {
|
|
||||||
refreshCounter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
View view;
|
View view;
|
||||||
@ -67,39 +64,46 @@ public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
|
|||||||
view = inflater.inflate(R.layout.departure_listing, parent, false);
|
view = inflater.inflate(R.layout.departure_listing, parent, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Departure departure = getItem(position);
|
final Departure departure = getItem(position);
|
||||||
((TextView) view.findViewById(R.id.destinationText)).setText(departure
|
((TextView) view.findViewById(R.id.destinationText)).setText(departure
|
||||||
.getDestination().toString());
|
.getDestination().toString());
|
||||||
|
|
||||||
TextSwitcher textSwitcher = (TextSwitcher) view
|
TimedTextSwitcher textSwitcher = (TimedTextSwitcher) view
|
||||||
.findViewById(R.id.trainLengthText);
|
.findViewById(R.id.trainLengthText);
|
||||||
initTextSwitcher(textSwitcher);
|
initTextSwitcher(textSwitcher);
|
||||||
|
|
||||||
|
textSwitcher.setCurrentText(departure.getTrainLengthText());
|
||||||
|
textSwitcher.setTextProviders(new TextProvider[] { new TextProvider() {
|
||||||
|
@Override
|
||||||
|
public String getText() {
|
||||||
final String estimatedArrivalTimeText = departure
|
final String estimatedArrivalTimeText = departure
|
||||||
.getEstimatedArrivalTimeText(getContext());
|
.getEstimatedArrivalTimeText(getContext());
|
||||||
String arrivalText = "Est. arrival " + estimatedArrivalTimeText;
|
|
||||||
if (StringUtils.isBlank(estimatedArrivalTimeText)) {
|
if (StringUtils.isBlank(estimatedArrivalTimeText)) {
|
||||||
textSwitcher.setCurrentText(departure.getTrainLengthText());
|
return "";
|
||||||
} else if (refreshCounter % 6 < 3) {
|
|
||||||
String trainLengthText = departure.getTrainLengthText();
|
|
||||||
if (refreshCounter % 6 == 0) {
|
|
||||||
textSwitcher.setText(trainLengthText);
|
|
||||||
} else {
|
} else {
|
||||||
textSwitcher.setCurrentText(trainLengthText);
|
return "Est. arrival " + estimatedArrivalTimeText;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (refreshCounter % 6 == 3) {
|
|
||||||
textSwitcher.setText(arrivalText);
|
|
||||||
} else {
|
|
||||||
textSwitcher.setCurrentText(arrivalText);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}, new TextProvider() {
|
||||||
|
@Override
|
||||||
|
public String getText() {
|
||||||
|
return departure.getTrainLengthText();
|
||||||
|
}
|
||||||
|
} });
|
||||||
|
|
||||||
ImageView colorBar = (ImageView) view
|
ImageView colorBar = (ImageView) view
|
||||||
.findViewById(R.id.destinationColorBar);
|
.findViewById(R.id.destinationColorBar);
|
||||||
((GradientDrawable) colorBar.getDrawable()).setColor(Color
|
((GradientDrawable) colorBar.getDrawable()).setColor(Color
|
||||||
.parseColor(departure.getDestinationColor()));
|
.parseColor(departure.getDestinationColor()));
|
||||||
((TextView) view.findViewById(R.id.countdown)).setText(departure
|
CountdownTextView countdownTextView = (CountdownTextView) view
|
||||||
.getCountdownText());
|
.findViewById(R.id.countdown);
|
||||||
|
countdownTextView.setText(departure.getCountdownText());
|
||||||
|
countdownTextView.setTextProvider(new TextProvider() {
|
||||||
|
@Override
|
||||||
|
public String getText() {
|
||||||
|
return departure.getCountdownText();
|
||||||
|
}
|
||||||
|
});
|
||||||
((TextView) view.findViewById(R.id.uncertainty)).setText(departure
|
((TextView) view.findViewById(R.id.uncertainty)).setText(departure
|
||||||
.getUncertaintyText());
|
.getUncertaintyText());
|
||||||
if (departure.isBikeAllowed()) {
|
if (departure.isBikeAllowed()) {
|
||||||
@ -130,7 +134,9 @@ public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
textSwitcher.setInAnimation(AnimationUtils.loadAnimation(
|
textSwitcher.setInAnimation(AnimationUtils.loadAnimation(
|
||||||
getContext(), android.R.anim.fade_in));
|
getContext(), android.R.anim.slide_in_left));
|
||||||
|
textSwitcher.setOutAnimation(AnimationUtils.loadAnimation(
|
||||||
|
getContext(), android.R.anim.slide_out_right));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ import android.content.ContentValues;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.GradientDrawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@ -16,14 +18,20 @@ import android.os.PowerManager;
|
|||||||
import android.text.format.DateFormat;
|
import android.text.format.DateFormat;
|
||||||
import android.text.util.Linkify;
|
import android.text.util.Linkify;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.ContextMenu;
|
||||||
|
import android.view.ContextMenu.ContextMenuInfo;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView.AdapterContextMenuInfo;
|
||||||
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.dougkeen.bart.actionbarcompat.ActionBarListActivity;
|
import com.dougkeen.bart.actionbarcompat.ActionBarListActivity;
|
||||||
|
import com.dougkeen.bart.controls.CountdownTextView;
|
||||||
|
import com.dougkeen.bart.controls.Ticker;
|
||||||
import com.dougkeen.bart.data.RoutesColumns;
|
import com.dougkeen.bart.data.RoutesColumns;
|
||||||
import com.dougkeen.bart.model.Constants;
|
import com.dougkeen.bart.model.Constants;
|
||||||
import com.dougkeen.bart.model.Departure;
|
import com.dougkeen.bart.model.Departure;
|
||||||
@ -32,6 +40,7 @@ import com.dougkeen.bart.model.ScheduleInformation;
|
|||||||
import com.dougkeen.bart.model.ScheduleItem;
|
import com.dougkeen.bart.model.ScheduleItem;
|
||||||
import com.dougkeen.bart.model.Station;
|
import com.dougkeen.bart.model.Station;
|
||||||
import com.dougkeen.bart.model.StationPair;
|
import com.dougkeen.bart.model.StationPair;
|
||||||
|
import com.dougkeen.bart.model.TextProvider;
|
||||||
import com.dougkeen.bart.networktasks.GetRealTimeDeparturesTask;
|
import com.dougkeen.bart.networktasks.GetRealTimeDeparturesTask;
|
||||||
import com.dougkeen.bart.networktasks.GetScheduleInformationTask;
|
import com.dougkeen.bart.networktasks.GetScheduleInformationTask;
|
||||||
|
|
||||||
@ -46,6 +55,9 @@ public class ViewDeparturesActivity extends ActionBarListActivity {
|
|||||||
private int mAverageTripLength;
|
private int mAverageTripLength;
|
||||||
private int mAverageTripSampleCount;
|
private int mAverageTripSampleCount;
|
||||||
|
|
||||||
|
private Departure mSelectedDeparture;
|
||||||
|
private Departure mBoardedDeparture;
|
||||||
|
|
||||||
private DepartureArrayAdapter mDeparturesAdapter;
|
private DepartureArrayAdapter mDeparturesAdapter;
|
||||||
|
|
||||||
private ScheduleInformation mLatestScheduleInfo;
|
private ScheduleInformation mLatestScheduleInfo;
|
||||||
@ -55,14 +67,6 @@ public class ViewDeparturesActivity extends ActionBarListActivity {
|
|||||||
private AsyncTask<StationPair, Integer, RealTimeDepartures> mGetDeparturesTask;
|
private AsyncTask<StationPair, Integer, RealTimeDepartures> mGetDeparturesTask;
|
||||||
private AsyncTask<StationPair, Integer, ScheduleInformation> mGetScheduleInformationTask;
|
private AsyncTask<StationPair, Integer, ScheduleInformation> mGetScheduleInformationTask;
|
||||||
|
|
||||||
private boolean mIsAutoUpdating = false;
|
|
||||||
|
|
||||||
private final Runnable AUTO_UPDATE_RUNNABLE = new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
runAutoUpdate();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private PowerManager.WakeLock mWakeLock;
|
private PowerManager.WakeLock mWakeLock;
|
||||||
|
|
||||||
private boolean mDepartureFetchIsPending;
|
private boolean mDepartureFetchIsPending;
|
||||||
@ -104,21 +108,32 @@ public class ViewDeparturesActivity extends ActionBarListActivity {
|
|||||||
|
|
||||||
mDeparturesAdapter = new DepartureArrayAdapter(this,
|
mDeparturesAdapter = new DepartureArrayAdapter(this,
|
||||||
R.layout.departure_listing);
|
R.layout.departure_listing);
|
||||||
if (savedInstanceState != null
|
if (savedInstanceState != null) {
|
||||||
&& savedInstanceState.containsKey("departures")) {
|
|
||||||
|
if (savedInstanceState.containsKey("departures")) {
|
||||||
for (Parcelable departure : savedInstanceState
|
for (Parcelable departure : savedInstanceState
|
||||||
.getParcelableArray("departures")) {
|
.getParcelableArray("departures")) {
|
||||||
mDeparturesAdapter.add((Departure) departure);
|
mDeparturesAdapter.add((Departure) departure);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (savedInstanceState.containsKey("boardedDeparture")) {
|
||||||
|
mBoardedDeparture = (Departure) savedInstanceState
|
||||||
|
.getParcelable("boardedDeparture");
|
||||||
|
}
|
||||||
|
}
|
||||||
setListAdapter(mDeparturesAdapter);
|
setListAdapter(mDeparturesAdapter);
|
||||||
|
|
||||||
|
registerForContextMenu(getListView());
|
||||||
|
|
||||||
findViewById(R.id.missingDepartureText).setVisibility(View.VISIBLE);
|
findViewById(R.id.missingDepartureText).setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
refreshBoardedDeparture();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
cancelDataFetch();
|
cancelDataFetch();
|
||||||
|
Ticker.getInstance().stopTicking();
|
||||||
super.onPause();
|
super.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,6 +162,13 @@ public class ViewDeparturesActivity extends ActionBarListActivity {
|
|||||||
departures[i] = mDeparturesAdapter.getItem(i);
|
departures[i] = mDeparturesAdapter.getItem(i);
|
||||||
}
|
}
|
||||||
outState.putParcelableArray("departures", departures);
|
outState.putParcelableArray("departures", departures);
|
||||||
|
outState.putParcelable("boardedDeparture", mBoardedDeparture);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
Ticker.getInstance().startTicking();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -161,10 +183,7 @@ public class ViewDeparturesActivity extends ActionBarListActivity {
|
|||||||
.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK,
|
.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK,
|
||||||
"ViewDeparturesActivity");
|
"ViewDeparturesActivity");
|
||||||
mWakeLock.acquire();
|
mWakeLock.acquire();
|
||||||
if (mDeparturesAdapter != null && !mDeparturesAdapter.isEmpty()) {
|
refreshBoardedDeparture();
|
||||||
mIsAutoUpdating = true;
|
|
||||||
}
|
|
||||||
runAutoUpdate();
|
|
||||||
} else if (mWakeLock != null) {
|
} else if (mWakeLock != null) {
|
||||||
mWakeLock.release();
|
mWakeLock.release();
|
||||||
}
|
}
|
||||||
@ -336,11 +355,6 @@ public class ViewDeparturesActivity extends ActionBarListActivity {
|
|||||||
|
|
||||||
scheduleDepartureFetch(interval);
|
scheduleDepartureFetch(interval);
|
||||||
}
|
}
|
||||||
if (!mIsAutoUpdating) {
|
|
||||||
mIsAutoUpdating = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mIsAutoUpdating = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -506,25 +520,6 @@ public class ViewDeparturesActivity extends ActionBarListActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private long mLastAutoUpdate = 0;
|
|
||||||
|
|
||||||
private void runAutoUpdate() {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
if (now - mLastAutoUpdate < 950) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (mIsAutoUpdating && mDeparturesAdapter != null) {
|
|
||||||
mDeparturesAdapter.incrementRefreshCounter();
|
|
||||||
mDeparturesAdapter.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
mLastAutoUpdate = now;
|
|
||||||
if (hasWindowFocus()) {
|
|
||||||
postDelayed(AUTO_UPDATE_RUNNABLE, 1000);
|
|
||||||
} else {
|
|
||||||
mIsAutoUpdating = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean postDelayed(Runnable runnable, long delayMillis) {
|
private boolean postDelayed(Runnable runnable, long delayMillis) {
|
||||||
return mEmptyView.postDelayed(runnable, delayMillis);
|
return mEmptyView.postDelayed(runnable, delayMillis);
|
||||||
}
|
}
|
||||||
@ -557,4 +552,84 @@ public class ViewDeparturesActivity extends ActionBarListActivity {
|
|||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateContextMenu(ContextMenu menu, View v,
|
||||||
|
ContextMenuInfo menuInfo) {
|
||||||
|
super.onCreateContextMenu(menu, v, menuInfo);
|
||||||
|
|
||||||
|
MenuInflater inflater = getMenuInflater();
|
||||||
|
inflater.inflate(R.menu.departure_context_menu, menu);
|
||||||
|
|
||||||
|
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
|
||||||
|
mSelectedDeparture = (Departure) getListAdapter()
|
||||||
|
.getItem(info.position);
|
||||||
|
menu.setHeaderTitle(R.string.departure_options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onContextItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.boardTrain) {
|
||||||
|
mBoardedDeparture = mSelectedDeparture;
|
||||||
|
refreshBoardedDeparture();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onContextItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshBoardedDeparture() {
|
||||||
|
if (mBoardedDeparture == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
final Departure departure = mBoardedDeparture;
|
||||||
|
findViewById(R.id.yourTrainSection).setVisibility(View.VISIBLE);
|
||||||
|
((TextView) findViewById(R.id.yourTrainDestinationText))
|
||||||
|
.setText(departure.getDestination().toString());
|
||||||
|
|
||||||
|
((TextView) findViewById(R.id.yourTrainTrainLengthText))
|
||||||
|
.setText(departure.getTrainLengthText());
|
||||||
|
|
||||||
|
ImageView colorBar = (ImageView) findViewById(R.id.yourTrainDestinationColorBar);
|
||||||
|
((GradientDrawable) colorBar.getDrawable()).setColor(Color
|
||||||
|
.parseColor(departure.getDestinationColor()));
|
||||||
|
if (departure.isBikeAllowed()) {
|
||||||
|
((ImageView) findViewById(R.id.yourTrainBikeIcon))
|
||||||
|
.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
((ImageView) findViewById(R.id.yourTrainBikeIcon))
|
||||||
|
.setVisibility(View.INVISIBLE);
|
||||||
|
}
|
||||||
|
if (departure.getRequiresTransfer()) {
|
||||||
|
((ImageView) findViewById(R.id.yourTrainXferIcon))
|
||||||
|
.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
((ImageView) findViewById(R.id.yourTrainXferIcon))
|
||||||
|
.setVisibility(View.INVISIBLE);
|
||||||
|
}
|
||||||
|
CountdownTextView departureCountdown = (CountdownTextView) findViewById(R.id.yourTrainDepartureCountdown);
|
||||||
|
CountdownTextView arrivalCountdown = (CountdownTextView) findViewById(R.id.yourTrainArrivalCountdown);
|
||||||
|
|
||||||
|
departureCountdown.setText("Leaves in " + departure.getCountdownText()
|
||||||
|
+ " " + departure.getUncertaintyText());
|
||||||
|
departureCountdown.setTextProvider(new TextProvider() {
|
||||||
|
@Override
|
||||||
|
public String getText() {
|
||||||
|
if (departure.hasDeparted()) {
|
||||||
|
return "Departed";
|
||||||
|
} else {
|
||||||
|
return "Leaves in " + departure.getCountdownText() + " "
|
||||||
|
+ departure.getUncertaintyText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
arrivalCountdown
|
||||||
|
.setText(departure.getEstimatedArrivalMinutesLeftText());
|
||||||
|
arrivalCountdown.setTextProvider(new TextProvider() {
|
||||||
|
@Override
|
||||||
|
public String getText() {
|
||||||
|
return departure.getEstimatedArrivalMinutesLeftText();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
57
src/com/dougkeen/bart/controls/CountdownTextView.java
Normal file
57
src/com/dougkeen/bart/controls/CountdownTextView.java
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package com.dougkeen.bart.controls;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.dougkeen.bart.model.TextProvider;
|
||||||
|
|
||||||
|
public class CountdownTextView extends TextView implements
|
||||||
|
Ticker.TickSubscriber {
|
||||||
|
|
||||||
|
private TextProvider mTextProvider;
|
||||||
|
private int mTickInterval;
|
||||||
|
|
||||||
|
public CountdownTextView(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CountdownTextView(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
setInstanceVarsFromAttrs(attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CountdownTextView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
setInstanceVarsFromAttrs(attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setInstanceVarsFromAttrs(AttributeSet attrs) {
|
||||||
|
int tickInterval = attrs.getAttributeIntValue(
|
||||||
|
"http://schemas.android.com/apk/res/com.dougkeen.bart",
|
||||||
|
"tickInterval", 0);
|
||||||
|
if (tickInterval > 0) {
|
||||||
|
setTickInterval(tickInterval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTextProvider(TextProvider provider) {
|
||||||
|
mTextProvider = provider;
|
||||||
|
Ticker.getInstance().addSubscriber(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTickInterval() {
|
||||||
|
return mTickInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTickInterval(int tickInterval) {
|
||||||
|
this.mTickInterval = tickInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTick() {
|
||||||
|
setText(mTextProvider.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
110
src/com/dougkeen/bart/controls/Ticker.java
Normal file
110
src/com/dougkeen/bart/controls/Ticker.java
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package com.dougkeen.bart.controls;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public class Ticker {
|
||||||
|
public static interface TickSubscriber {
|
||||||
|
int getTickInterval();
|
||||||
|
|
||||||
|
void onTick();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Ticker sInstance;
|
||||||
|
|
||||||
|
private WeakHashMap<TickSubscriber, Object> mSubscribers;
|
||||||
|
|
||||||
|
private TickerEngine mEngine;
|
||||||
|
|
||||||
|
private static class TickerEngine implements Runnable {
|
||||||
|
|
||||||
|
private static final int TICK_INTERVAL_MILLIS = 1000;
|
||||||
|
private Ticker publisher;
|
||||||
|
private Handler mHandler;
|
||||||
|
private boolean mPendingRequest = false;
|
||||||
|
private boolean mForceStop = false;
|
||||||
|
private long mTickCount = 0;
|
||||||
|
|
||||||
|
public TickerEngine(Ticker publisher) {
|
||||||
|
this.publisher = publisher;
|
||||||
|
this.mHandler = new Handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
mPendingRequest = false;
|
||||||
|
if (mForceStop) {
|
||||||
|
mForceStop = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.w("Ticker", "Tick #: " + mTickCount);
|
||||||
|
long startTimeNanos = System.nanoTime();
|
||||||
|
Iterator<TickSubscriber> iterator = publisher.mSubscribers.keySet()
|
||||||
|
.iterator();
|
||||||
|
boolean stillHasListeners = false;
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
TickSubscriber subscriber = iterator.next();
|
||||||
|
if (subscriber == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
stillHasListeners = true;
|
||||||
|
if (subscriber.getTickInterval() > 0
|
||||||
|
&& mTickCount % subscriber.getTickInterval() == 0)
|
||||||
|
subscriber.onTick();
|
||||||
|
}
|
||||||
|
long endTimeNanos = System.nanoTime();
|
||||||
|
|
||||||
|
if (stillHasListeners && !mPendingRequest) {
|
||||||
|
mHandler.postDelayed(this, TICK_INTERVAL_MILLIS
|
||||||
|
- ((endTimeNanos - startTimeNanos) / 1000000));
|
||||||
|
mPendingRequest = true;
|
||||||
|
mTickCount++;
|
||||||
|
} else {
|
||||||
|
mPendingRequest = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOn() {
|
||||||
|
return mPendingRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
mForceStop = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
public synchronized static Ticker getInstance() {
|
||||||
|
if (sInstance == null) {
|
||||||
|
sInstance = new Ticker();
|
||||||
|
}
|
||||||
|
return sInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSubscriber(TickSubscriber subscriber) {
|
||||||
|
if (!mSubscribers.containsKey(subscriber) && subscriber != null) {
|
||||||
|
mSubscribers.put(subscriber, null);
|
||||||
|
startTicking();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Ticker() {
|
||||||
|
mSubscribers = new WeakHashMap<TickSubscriber, Object>();
|
||||||
|
mEngine = new TickerEngine(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startTicking() {
|
||||||
|
if (!mEngine.isOn())
|
||||||
|
mEngine.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopTicking() {
|
||||||
|
if (mEngine.isOn())
|
||||||
|
mEngine.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
63
src/com/dougkeen/bart/controls/TimedTextSwitcher.java
Normal file
63
src/com/dougkeen/bart/controls/TimedTextSwitcher.java
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package com.dougkeen.bart.controls;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import com.dougkeen.bart.model.TextProvider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.TextSwitcher;
|
||||||
|
|
||||||
|
public class TimedTextSwitcher extends TextSwitcher implements
|
||||||
|
Ticker.TickSubscriber {
|
||||||
|
|
||||||
|
public TimedTextSwitcher(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
setInstanceVarsFromAttrs(attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TimedTextSwitcher(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setInstanceVarsFromAttrs(AttributeSet attrs) {
|
||||||
|
int tickInterval = attrs.getAttributeIntValue(
|
||||||
|
"http://schemas.android.com/apk/res/com.dougkeen.bart",
|
||||||
|
"tickInterval", 0);
|
||||||
|
if (tickInterval > 0) {
|
||||||
|
setTickInterval(tickInterval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int mTickInterval;
|
||||||
|
private TextProvider[] mTextProviderArray;
|
||||||
|
private int mCurrentIndex = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTickInterval() {
|
||||||
|
return mTickInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTickInterval(int tickInterval) {
|
||||||
|
this.mTickInterval = tickInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTextProviders(TextProvider[] textProviders) {
|
||||||
|
mTextProviderArray = textProviders;
|
||||||
|
Ticker.getInstance().addSubscriber(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String mLastText;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTick() {
|
||||||
|
String text = mTextProviderArray[mCurrentIndex].getText();
|
||||||
|
if (StringUtils.isNotBlank(text)
|
||||||
|
&& !StringUtils.equalsIgnoreCase(text, mLastText)) {
|
||||||
|
mLastText = text;
|
||||||
|
setText(text);
|
||||||
|
}
|
||||||
|
mCurrentIndex = (mCurrentIndex + 1) % mTextProviderArray.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -219,6 +219,30 @@ public class Departure implements Parcelable, Comparable<Departure> {
|
|||||||
return getMeanEstimate() + getEstimatedTripTime();
|
return getMeanEstimate() + getEstimatedTripTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getEstimatedArrivalMinutesLeft() {
|
||||||
|
long millisLeft = getEstimatedArrivalTime()
|
||||||
|
- System.currentTimeMillis();
|
||||||
|
if (millisLeft < 0) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
// Add ~30s to emulate rounding
|
||||||
|
return (millisLeft + 29999) / (60 * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEstimatedArrivalMinutesLeftText() {
|
||||||
|
long minutesLeft = getEstimatedArrivalMinutesLeft();
|
||||||
|
if (minutesLeft < 0) {
|
||||||
|
return "Arrived";
|
||||||
|
} else if (minutesLeft == 0) {
|
||||||
|
return "Estimated arrival in < 1 min.";
|
||||||
|
} else if (minutesLeft == 1) {
|
||||||
|
return "Estimated arrival in 1 min.";
|
||||||
|
} else {
|
||||||
|
return "Estimated arrival in " + minutesLeft + " mins.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public String getEstimatedArrivalTimeText(Context context) {
|
public String getEstimatedArrivalTimeText(Context context) {
|
||||||
if (getEstimatedTripTime() > 0 || arrivalTimeOverride > 0) {
|
if (getEstimatedTripTime() > 0 || arrivalTimeOverride > 0) {
|
||||||
return DateFormat.getTimeFormat(context).format(
|
return DateFormat.getTimeFormat(context).format(
|
||||||
|
@ -2,7 +2,9 @@ package com.dougkeen.bart.model;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@ -199,6 +201,19 @@ public enum Station {
|
|||||||
|
|
||||||
public List<Route> getTransferRoutes(Station dest) {
|
public List<Route> getTransferRoutes(Station dest) {
|
||||||
List<Route> returnList = new ArrayList<Route>();
|
List<Route> returnList = new ArrayList<Route>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kind of gimpy logic... no transfers from lake or 12th if headed to
|
||||||
|
* somewhere between woak and daly
|
||||||
|
*/
|
||||||
|
if (this.equals(LAKE) || this.equals(_12TH)) {
|
||||||
|
int destIndex = Line.BLUE.stations.indexOf(dest);
|
||||||
|
if (destIndex >= Line.BLUE.stations.indexOf(DALY)
|
||||||
|
&& destIndex <= Line.BLUE.stations.indexOf(WOAK)) {
|
||||||
|
return returnList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (dest.getInboundTransferStation() != null) {
|
if (dest.getInboundTransferStation() != null) {
|
||||||
// Try getting to the destination's inbound xfer station first
|
// Try getting to the destination's inbound xfer station first
|
||||||
returnList.addAll(getDirectRoutesForDestination(this,
|
returnList.addAll(getDirectRoutesForDestination(this,
|
||||||
|
7
src/com/dougkeen/bart/model/TextProvider.java
Normal file
7
src/com/dougkeen/bart/model/TextProvider.java
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package com.dougkeen.bart.model;
|
||||||
|
|
||||||
|
public interface TextProvider {
|
||||||
|
|
||||||
|
String getText();
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user