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"?>
|
||||
<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_height="fill_parent" >
|
||||
|
||||
@ -30,10 +31,12 @@
|
||||
style="@style/BikeIcon"
|
||||
android:src="@drawable/bike" />
|
||||
|
||||
<TextView
|
||||
<com.dougkeen.bart.controls.CountdownTextView
|
||||
android:id="@+id/countdown"
|
||||
style="@style/DepartureCountdownText"
|
||||
android:gravity="right" />
|
||||
android:gravity="right"
|
||||
android:width="90dp"
|
||||
bart:tickInterval="1" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
@ -43,12 +46,13 @@
|
||||
android:layout_below="@id/topRow"
|
||||
android:src="@drawable/xfer" />
|
||||
|
||||
<TextSwitcher
|
||||
<com.dougkeen.bart.controls.TimedTextSwitcher
|
||||
android:id="@+id/trainLengthText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/topRow"
|
||||
android:layout_toRightOf="@id/destinationColorBar" />
|
||||
android:layout_toRightOf="@id/destinationColorBar"
|
||||
bart:tickInterval="3" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/uncertainty"
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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_height="fill_parent"
|
||||
android:orientation="vertical" >
|
||||
@ -13,6 +14,82 @@
|
||||
android:paddingRight="5dp"
|
||||
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
|
||||
android:id="@android:id/list"
|
||||
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>
|
@ -16,16 +16,23 @@
|
||||
|
||||
<resources>
|
||||
|
||||
<attr name="tickInterval" format="integer" />
|
||||
|
||||
<declare-styleable name="AppTheme">
|
||||
<attr name="actionbarCompatTitleStyle" format="reference" />
|
||||
<attr name="actionbarCompatItemStyle" format="reference" />
|
||||
<attr name="actionbarCompatItemHomeStyle" format="reference" />
|
||||
<attr name="actionbarCompatProgressIndicatorStyle" format="reference" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="BezelImageView">
|
||||
<attr name="maskDrawable" format="reference" />
|
||||
<attr name="borderDrawable" format="reference" />
|
||||
</declare-styleable>
|
||||
<declare-styleable name="CountdownTextView">
|
||||
<attr name="tickInterval" />
|
||||
</declare-styleable>
|
||||
<declare-styleable name="TimedTextSwitcher">
|
||||
<attr name="tickInterval" />
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
@ -32,4 +32,7 @@
|
||||
<string name="departures">Departures</string>
|
||||
<string name="ok">OK</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>
|
||||
|
@ -94,7 +94,6 @@
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:textSize">20dp</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
<item name="android:width">90dp</item>
|
||||
</style>
|
||||
|
||||
<style name="DepartureUncertaintyText">
|
||||
|
@ -18,12 +18,13 @@ import android.widget.TextSwitcher;
|
||||
import android.widget.TextView;
|
||||
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.TextProvider;
|
||||
|
||||
public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
|
||||
|
||||
private int refreshCounter = 1;
|
||||
|
||||
public DepartureArrayAdapter(Context context, int textViewResourceId,
|
||||
Departure[] objects) {
|
||||
super(context, textViewResourceId, objects);
|
||||
@ -53,10 +54,6 @@ public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
|
||||
super(context, textViewResourceId);
|
||||
}
|
||||
|
||||
public void incrementRefreshCounter() {
|
||||
refreshCounter++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View view;
|
||||
@ -67,39 +64,46 @@ public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
|
||||
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
|
||||
.getDestination().toString());
|
||||
|
||||
TextSwitcher textSwitcher = (TextSwitcher) view
|
||||
TimedTextSwitcher textSwitcher = (TimedTextSwitcher) view
|
||||
.findViewById(R.id.trainLengthText);
|
||||
initTextSwitcher(textSwitcher);
|
||||
|
||||
textSwitcher.setCurrentText(departure.getTrainLengthText());
|
||||
textSwitcher.setTextProviders(new TextProvider[] { new TextProvider() {
|
||||
@Override
|
||||
public String getText() {
|
||||
final String estimatedArrivalTimeText = departure
|
||||
.getEstimatedArrivalTimeText(getContext());
|
||||
String arrivalText = "Est. arrival " + estimatedArrivalTimeText;
|
||||
if (StringUtils.isBlank(estimatedArrivalTimeText)) {
|
||||
textSwitcher.setCurrentText(departure.getTrainLengthText());
|
||||
} else if (refreshCounter % 6 < 3) {
|
||||
String trainLengthText = departure.getTrainLengthText();
|
||||
if (refreshCounter % 6 == 0) {
|
||||
textSwitcher.setText(trainLengthText);
|
||||
return "";
|
||||
} else {
|
||||
textSwitcher.setCurrentText(trainLengthText);
|
||||
}
|
||||
} else {
|
||||
if (refreshCounter % 6 == 3) {
|
||||
textSwitcher.setText(arrivalText);
|
||||
} else {
|
||||
textSwitcher.setCurrentText(arrivalText);
|
||||
return "Est. arrival " + estimatedArrivalTimeText;
|
||||
}
|
||||
}
|
||||
}, new TextProvider() {
|
||||
@Override
|
||||
public String getText() {
|
||||
return departure.getTrainLengthText();
|
||||
}
|
||||
} });
|
||||
|
||||
ImageView colorBar = (ImageView) view
|
||||
.findViewById(R.id.destinationColorBar);
|
||||
((GradientDrawable) colorBar.getDrawable()).setColor(Color
|
||||
.parseColor(departure.getDestinationColor()));
|
||||
((TextView) view.findViewById(R.id.countdown)).setText(departure
|
||||
.getCountdownText());
|
||||
CountdownTextView countdownTextView = (CountdownTextView) view
|
||||
.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
|
||||
.getUncertaintyText());
|
||||
if (departure.isBikeAllowed()) {
|
||||
@ -130,7 +134,9 @@ public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
|
||||
});
|
||||
|
||||
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.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
@ -16,14 +18,20 @@ import android.os.PowerManager;
|
||||
import android.text.format.DateFormat;
|
||||
import android.text.util.Linkify;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView.AdapterContextMenuInfo;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
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.model.Constants;
|
||||
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.Station;
|
||||
import com.dougkeen.bart.model.StationPair;
|
||||
import com.dougkeen.bart.model.TextProvider;
|
||||
import com.dougkeen.bart.networktasks.GetRealTimeDeparturesTask;
|
||||
import com.dougkeen.bart.networktasks.GetScheduleInformationTask;
|
||||
|
||||
@ -46,6 +55,9 @@ public class ViewDeparturesActivity extends ActionBarListActivity {
|
||||
private int mAverageTripLength;
|
||||
private int mAverageTripSampleCount;
|
||||
|
||||
private Departure mSelectedDeparture;
|
||||
private Departure mBoardedDeparture;
|
||||
|
||||
private DepartureArrayAdapter mDeparturesAdapter;
|
||||
|
||||
private ScheduleInformation mLatestScheduleInfo;
|
||||
@ -55,14 +67,6 @@ public class ViewDeparturesActivity extends ActionBarListActivity {
|
||||
private AsyncTask<StationPair, Integer, RealTimeDepartures> mGetDeparturesTask;
|
||||
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 boolean mDepartureFetchIsPending;
|
||||
@ -104,21 +108,32 @@ public class ViewDeparturesActivity extends ActionBarListActivity {
|
||||
|
||||
mDeparturesAdapter = new DepartureArrayAdapter(this,
|
||||
R.layout.departure_listing);
|
||||
if (savedInstanceState != null
|
||||
&& savedInstanceState.containsKey("departures")) {
|
||||
if (savedInstanceState != null) {
|
||||
|
||||
if (savedInstanceState.containsKey("departures")) {
|
||||
for (Parcelable departure : savedInstanceState
|
||||
.getParcelableArray("departures")) {
|
||||
mDeparturesAdapter.add((Departure) departure);
|
||||
}
|
||||
}
|
||||
if (savedInstanceState.containsKey("boardedDeparture")) {
|
||||
mBoardedDeparture = (Departure) savedInstanceState
|
||||
.getParcelable("boardedDeparture");
|
||||
}
|
||||
}
|
||||
setListAdapter(mDeparturesAdapter);
|
||||
|
||||
registerForContextMenu(getListView());
|
||||
|
||||
findViewById(R.id.missingDepartureText).setVisibility(View.VISIBLE);
|
||||
|
||||
refreshBoardedDeparture();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
cancelDataFetch();
|
||||
Ticker.getInstance().stopTicking();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@ -147,6 +162,13 @@ public class ViewDeparturesActivity extends ActionBarListActivity {
|
||||
departures[i] = mDeparturesAdapter.getItem(i);
|
||||
}
|
||||
outState.putParcelableArray("departures", departures);
|
||||
outState.putParcelable("boardedDeparture", mBoardedDeparture);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
Ticker.getInstance().startTicking();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -161,10 +183,7 @@ public class ViewDeparturesActivity extends ActionBarListActivity {
|
||||
.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK,
|
||||
"ViewDeparturesActivity");
|
||||
mWakeLock.acquire();
|
||||
if (mDeparturesAdapter != null && !mDeparturesAdapter.isEmpty()) {
|
||||
mIsAutoUpdating = true;
|
||||
}
|
||||
runAutoUpdate();
|
||||
refreshBoardedDeparture();
|
||||
} else if (mWakeLock != null) {
|
||||
mWakeLock.release();
|
||||
}
|
||||
@ -336,11 +355,6 @@ public class ViewDeparturesActivity extends ActionBarListActivity {
|
||||
|
||||
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) {
|
||||
return mEmptyView.postDelayed(runnable, delayMillis);
|
||||
}
|
||||
@ -557,4 +552,84 @@ public class ViewDeparturesActivity extends ActionBarListActivity {
|
||||
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();
|
||||
}
|
||||
|
||||
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) {
|
||||
if (getEstimatedTripTime() > 0 || arrivalTimeOverride > 0) {
|
||||
return DateFormat.getTimeFormat(context).format(
|
||||
|
@ -2,7 +2,9 @@ package com.dougkeen.bart.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
@ -199,6 +201,19 @@ public enum Station {
|
||||
|
||||
public List<Route> getTransferRoutes(Station dest) {
|
||||
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) {
|
||||
// Try getting to the destination's inbound xfer station first
|
||||
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