Merge pull request #6 from FuegoFro/clean_up_your_train_view

Reorganize train info views and general code cleanup
This commit is contained in:
Doug Keen 2015-10-18 15:52:31 -07:00
commit 4b8b7af55a
13 changed files with 424 additions and 350 deletions

View File

@ -17,6 +17,7 @@ import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.Parcelable; import android.os.Parcelable;
import android.os.Vibrator; import android.os.Vibrator;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.view.ActionMode; import android.support.v7.view.ActionMode;
import android.text.format.DateFormat; import android.text.format.DateFormat;
@ -48,6 +49,7 @@ import com.dougkeen.bart.services.EtdService;
import com.dougkeen.bart.services.EtdService.EtdServiceBinder; import com.dougkeen.bart.services.EtdService.EtdServiceBinder;
import com.dougkeen.bart.services.EtdService.EtdServiceListener; import com.dougkeen.bart.services.EtdService.EtdServiceListener;
import com.dougkeen.bart.services.EtdService_; import com.dougkeen.bart.services.EtdService_;
import com.dougkeen.util.Assert;
import com.dougkeen.util.Observer; import com.dougkeen.util.Observer;
import com.dougkeen.util.WakeLocker; import com.dougkeen.util.WakeLocker;
@ -86,8 +88,7 @@ public class ViewDeparturesActivity extends AppCompatActivity implements
mProgress = (ProgressBar) findViewById(android.R.id.progress); mProgress = (ProgressBar) findViewById(android.R.id.progress);
mDeparturesAdapter = new DepartureArrayAdapter(this, mDeparturesAdapter = new DepartureArrayAdapter(this);
R.layout.departure_listing);
setListAdapter(mDeparturesAdapter); setListAdapter(mDeparturesAdapter);
final ListView listView = getListView(); final ListView listView = getListView();
@ -127,9 +128,9 @@ public class ViewDeparturesActivity extends AppCompatActivity implements
} }
if (savedInstanceState != null) { if (savedInstanceState != null) {
if (savedInstanceState.containsKey("departures")) { Parcelable[] departuresArray = savedInstanceState.getParcelableArray("departures");
for (Parcelable departure : savedInstanceState if (departuresArray != null) {
.getParcelableArray("departures")) { for (Parcelable departure : departuresArray) {
mDeparturesAdapter.add((Departure) departure); mDeparturesAdapter.add((Departure) departure);
} }
mDeparturesAdapter.notifyDataSetChanged(); mDeparturesAdapter.notifyDataSetChanged();
@ -151,8 +152,9 @@ public class ViewDeparturesActivity extends AppCompatActivity implements
} }
refreshBoardedDeparture(false); refreshBoardedDeparture(false);
getSupportActionBar().setHomeButtonEnabled(true); ActionBar supportActionBar = Assert.notNull(getSupportActionBar());
getSupportActionBar().setDisplayHomeAsUpEnabled(true); supportActionBar.setHomeButtonEnabled(true);
supportActionBar.setDisplayHomeAsUpEnabled(true);
if (bartRunnerApplication.shouldPlayAlarmRingtone()) { if (bartRunnerApplication.shouldPlayAlarmRingtone()) {
soundTheAlarm(); soundTheAlarm();
@ -440,7 +442,7 @@ public class ViewDeparturesActivity extends AppCompatActivity implements
return; return;
} }
mYourTrainSection.updateFromBoardedDeparture(); mYourTrainSection.updateFromBoardedDeparture(boardedDeparture);
if (currentVisibility != View.VISIBLE) { if (currentVisibility != View.VISIBLE) {
showYourTrainSection(animate); showYourTrainSection(animate);

View File

@ -0,0 +1,46 @@
package com.dougkeen.bart.controls;
import android.content.Context;
import android.support.v7.widget.LinearLayoutCompat;
import android.util.AttributeSet;
import android.widget.Checkable;
import com.dougkeen.bart.R;
/**
* A {@link android.widget.LinearLayout} that implements {@link Checkable} and changes
* its background color when checked.
*/
public class CheckableLinearLayout extends LinearLayoutCompat implements Checkable {
private boolean mChecked;
public CheckableLinearLayout(Context context) {
super(context);
}
public CheckableLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CheckableLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean isChecked() {
return mChecked;
}
@Override
public void setChecked(boolean checked) {
mChecked = checked;
int colorRes = isChecked() ? R.color.blue_selection : android.R.color.transparent;
setBackgroundResource(colorRes);
}
@Override
public void toggle() {
setChecked(!isChecked());
}
}

View File

@ -1,42 +0,0 @@
package com.dougkeen.bart.controls;
import android.content.Context;
import android.view.LayoutInflater;
import android.widget.Checkable;
import android.widget.RelativeLayout;
import com.dougkeen.bart.R;
public class DepartureListItemLayout extends RelativeLayout implements
Checkable {
public DepartureListItemLayout(Context context) {
super(context);
LayoutInflater.from(context).inflate(R.layout.departure_listing, this,
true);
}
private boolean mChecked;
@Override
public boolean isChecked() {
return mChecked;
}
@Override
public void setChecked(boolean checked) {
mChecked = checked;
if (isChecked()) {
setBackgroundDrawable(getContext().getResources().getDrawable(
R.color.blue_selection));
} else {
setBackgroundDrawable(getContext().getResources().getDrawable(
android.R.color.transparent));
}
}
@Override
public void toggle() {
setChecked(!isChecked());
}
}

View File

@ -10,8 +10,7 @@ import android.widget.TextSwitcher;
import com.dougkeen.bart.R; import com.dougkeen.bart.R;
import com.dougkeen.bart.model.TextProvider; import com.dougkeen.bart.model.TextProvider;
public class TimedTextSwitcher extends TextSwitcher implements public class TimedTextSwitcher extends TextSwitcher implements Ticker.TickSubscriber {
Ticker.TickSubscriber {
public TimedTextSwitcher(Context context, AttributeSet attrs) { public TimedTextSwitcher(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);

View File

@ -2,41 +2,51 @@ package com.dougkeen.bart.controls;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.Checkable; import android.widget.Checkable;
import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import com.dougkeen.bart.BartRunnerApplication;
import com.dougkeen.bart.R; import com.dougkeen.bart.R;
import com.dougkeen.bart.model.Departure; import com.dougkeen.bart.model.Departure;
import com.dougkeen.bart.model.TextProvider; import com.dougkeen.bart.model.TextProvider;
import com.dougkeen.util.Observer; import com.dougkeen.util.Observer;
public class YourTrainLayout extends RelativeLayout implements Checkable { public class YourTrainLayout extends FrameLayout implements Checkable {
private final TextView destinationText;
private final TextView trainLength;
private final View colorBar;
private final ImageView bikeIcon;
private final View xferIcon;
private final CountdownTextView departureCountdown;
private final CountdownTextView arrivalCountdown;
private final TextView alarmText;
public YourTrainLayout(Context context) { public YourTrainLayout(Context context) {
super(context); this(context, null);
assignLayout(context); }
public YourTrainLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} }
public YourTrainLayout(Context context, AttributeSet attrs, int defStyle) { public YourTrainLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle); super(context, attrs, defStyle);
assignLayout(context);
}
public YourTrainLayout(Context context, AttributeSet attrs) {
super(context, attrs);
assignLayout(context);
}
public void assignLayout(Context context) {
LayoutInflater.from(context).inflate(R.layout.your_train, this, true); LayoutInflater.from(context).inflate(R.layout.your_train, this, true);
destinationText = (TextView) findViewById(R.id.yourTrainDestinationText);
trainLength = (TextView) findViewById(R.id.yourTrainTrainLengthText);
colorBar = findViewById(R.id.yourTrainDestinationColorBar);
bikeIcon = (ImageView) findViewById(R.id.yourTrainBikeIcon);
xferIcon = findViewById(R.id.yourTrainXferIcon);
departureCountdown = (CountdownTextView) findViewById(R.id.yourTrainDepartureCountdown);
arrivalCountdown = (CountdownTextView) findViewById(R.id.yourTrainArrivalCountdown);
alarmText = (TextView) findViewById(R.id.alarmText);
} }
private boolean mChecked; private boolean mChecked;
@ -75,13 +85,8 @@ public class YourTrainLayout extends RelativeLayout implements Checkable {
} }
private void setBackground() { private void setBackground() {
if (isChecked()) { int colorRes = isChecked() ? R.color.blue_selection : R.color.gray;
setBackgroundDrawable(getContext().getResources().getDrawable( setBackgroundResource(colorRes);
R.color.blue_selection));
} else {
setBackgroundDrawable(getContext().getResources().getDrawable(
R.color.gray));
}
} }
@Override @Override
@ -89,11 +94,10 @@ public class YourTrainLayout extends RelativeLayout implements Checkable {
setChecked(!isChecked()); setChecked(!isChecked());
} }
public void updateFromBoardedDeparture() { public void updateFromBoardedDeparture(final Departure boardedDeparture) {
final Departure boardedDeparture = ((BartRunnerApplication) ((Activity) getContext()) if (boardedDeparture == null) {
.getApplication()).getBoardedDeparture();
if (boardedDeparture == null)
return; return;
}
if (!boardedDeparture.equals(mDeparture)) { if (!boardedDeparture.equals(mDeparture)) {
if (mDeparture != null) { if (mDeparture != null) {
@ -110,36 +114,23 @@ public class YourTrainLayout extends RelativeLayout implements Checkable {
mDeparture = boardedDeparture; mDeparture = boardedDeparture;
((TextView) findViewById(R.id.yourTrainDestinationText)) destinationText.setText(boardedDeparture.getTrainDestination().toString());
.setText(boardedDeparture.getTrainDestination().toString()); trainLength.setText(boardedDeparture.getTrainLengthAndPlatform());
((TextView) findViewById(R.id.yourTrainTrainLengthText)) colorBar.setBackgroundColor(boardedDeparture.getTrainDestinationColor());
.setText(boardedDeparture.getTrainLengthAndPlatform());
ImageView colorBar = (ImageView) findViewById(R.id.yourTrainDestinationColorBar);
((GradientDrawable) colorBar.getDrawable()).setColor(Color
.parseColor(boardedDeparture.getTrainDestinationColor()));
ImageView bikeIcon = (ImageView) findViewById(R.id.yourTrainBikeIcon);
if (boardedDeparture.isBikeAllowed()) { if (boardedDeparture.isBikeAllowed()) {
bikeIcon.setImageDrawable(getResources().getDrawable( bikeIcon.setImageResource(R.drawable.bike);
R.drawable.bike));
} else { } else {
bikeIcon.setImageDrawable(getResources().getDrawable( bikeIcon.setImageResource(R.drawable.nobike);
R.drawable.nobike));
} }
if (boardedDeparture.getRequiresTransfer()) { if (boardedDeparture.getRequiresTransfer()) {
findViewById(R.id.yourTrainXferIcon) xferIcon.setVisibility(View.VISIBLE);
.setVisibility(View.VISIBLE);
} else { } else {
findViewById(R.id.yourTrainXferIcon) xferIcon.setVisibility(View.INVISIBLE);
.setVisibility(View.INVISIBLE);
} }
updateAlarmIndicator(); updateAlarmIndicator();
CountdownTextView departureCountdown = (CountdownTextView) findViewById(R.id.yourTrainDepartureCountdown);
CountdownTextView arrivalCountdown = (CountdownTextView) findViewById(R.id.yourTrainArrivalCountdown);
final TextProvider textProvider = new TextProvider() { final TextProvider textProvider = new TextProvider() {
@Override @Override
public String getText(long tickNumber) { public String getText(long tickNumber) {
@ -169,11 +160,10 @@ public class YourTrainLayout extends RelativeLayout implements Checkable {
private void updateAlarmIndicator() { private void updateAlarmIndicator() {
if (!mDeparture.isAlarmPending()) { if (!mDeparture.isAlarmPending()) {
findViewById(R.id.alarmText).setVisibility(GONE); alarmText.setVisibility(GONE);
} else { } else {
findViewById(R.id.alarmText).setVisibility(VISIBLE); alarmText.setVisibility(VISIBLE);
((TextView) findViewById(R.id.alarmText)).setText(String alarmText.setText(String.valueOf(mDeparture.getAlarmLeadTimeMinutes()));
.valueOf(mDeparture.getAlarmLeadTimeMinutes()));
} }
} }

View File

@ -1,13 +1,8 @@
package com.dougkeen.bart.data; package com.dougkeen.bart.data;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.content.res.Resources;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -21,65 +16,36 @@ import android.widget.ViewSwitcher.ViewFactory;
import com.dougkeen.bart.R; import com.dougkeen.bart.R;
import com.dougkeen.bart.controls.CountdownTextView; import com.dougkeen.bart.controls.CountdownTextView;
import com.dougkeen.bart.controls.DepartureListItemLayout; import com.dougkeen.bart.controls.CheckableLinearLayout;
import com.dougkeen.bart.controls.TimedTextSwitcher; import com.dougkeen.bart.controls.TimedTextSwitcher;
import com.dougkeen.bart.model.Departure; import com.dougkeen.bart.model.Departure;
import com.dougkeen.bart.model.TextProvider; import com.dougkeen.bart.model.TextProvider;
import org.apache.commons.lang3.StringUtils;
public class DepartureArrayAdapter extends ArrayAdapter<Departure> { public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
private Drawable noBikeDrawable; private Drawable noBikeDrawable;
private Drawable bikeDrawable; private Drawable bikeDrawable;
public DepartureArrayAdapter(Context context, int textViewResourceId, @SuppressWarnings("deprecation")
Departure[] objects) { public DepartureArrayAdapter(Context context) {
super(context, textViewResourceId, objects); super(context, 0 /* resource, unused since we override getView */);
assignBikeDrawables(); Resources resources = context.getResources();
} // We need to use the deprecated getDrawable since the newer version that
// replaces it was only made available in API 21.
private void assignBikeDrawables() { noBikeDrawable = resources.getDrawable(R.drawable.nobike);
noBikeDrawable = getContext().getResources().getDrawable( bikeDrawable = resources.getDrawable(R.drawable.bike);
R.drawable.nobike);
bikeDrawable = getContext().getResources().getDrawable(R.drawable.bike);
}
public DepartureArrayAdapter(Context context, int resource,
int textViewResourceId, Departure[] objects) {
super(context, resource, textViewResourceId, objects);
assignBikeDrawables();
}
public DepartureArrayAdapter(Context context, int resource,
int textViewResourceId, List<Departure> objects) {
super(context, resource, textViewResourceId, objects);
assignBikeDrawables();
}
public DepartureArrayAdapter(Context context, int resource,
int textViewResourceId) {
super(context, resource, textViewResourceId);
assignBikeDrawables();
}
public DepartureArrayAdapter(Context context, int textViewResourceId,
List<Departure> objects) {
super(context, textViewResourceId, objects);
assignBikeDrawables();
}
public DepartureArrayAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
assignBikeDrawables();
} }
@Override @Override
public View getView(int position, View convertView, ViewGroup parent) { public View getView(int position, View convertView, ViewGroup parent) {
View view; View view;
if (convertView != null if (convertView != null && convertView instanceof CheckableLinearLayout) {
&& convertView instanceof DepartureListItemLayout) {
view = convertView; view = convertView;
} else { } else {
view = new DepartureListItemLayout(getContext()); view = LayoutInflater.from(getContext()).inflate(
R.layout.departure_listing, parent, false /* attachToRoot */);
} }
final Departure departure = getItem(position); final Departure departure = getItem(position);
@ -132,10 +98,8 @@ public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
}); });
} }
ImageView colorBar = (ImageView) view view.findViewById(R.id.destinationColorBar)
.findViewById(R.id.destinationColorBar); .setBackgroundColor(departure.getTrainDestinationColor());
((GradientDrawable) colorBar.getDrawable()).setColor(Color
.parseColor(departure.getTrainDestinationColor()));
CountdownTextView countdownTextView = (CountdownTextView) view CountdownTextView countdownTextView = (CountdownTextView) view
.findViewById(R.id.countdown); .findViewById(R.id.countdown);
countdownTextView.setText(departure.getCountdownText()); countdownTextView.setText(departure.getCountdownText());
@ -171,7 +135,7 @@ public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
} }
}); });
} }
ImageView bikeIcon = (ImageView) view.findViewById(R.id.bikeIcon); ImageView bikeIcon = (ImageView) view.findViewById(R.id.bikeIcon);
if (departure.isBikeAllowed()) { if (departure.isBikeAllowed()) {
bikeIcon.setImageDrawable(bikeDrawable); bikeIcon.setImageDrawable(bikeDrawable);
@ -179,11 +143,9 @@ public class DepartureArrayAdapter extends ArrayAdapter<Departure> {
bikeIcon.setImageDrawable(noBikeDrawable); bikeIcon.setImageDrawable(noBikeDrawable);
} }
if (departure.getRequiresTransfer()) { if (departure.getRequiresTransfer()) {
view.findViewById(R.id.xferIcon) view.findViewById(R.id.xferIcon).setVisibility(View.VISIBLE);
.setVisibility(View.VISIBLE);
} else { } else {
view.findViewById(R.id.xferIcon) view.findViewById(R.id.xferIcon).setVisibility(View.INVISIBLE);
.setVisibility(View.INVISIBLE);
} }
return view; return view;

View File

@ -1,18 +1,14 @@
package com.dougkeen.bart.model; package com.dougkeen.bart.model;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.lang3.time.DateFormatUtils;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.app.Notification; import android.app.Notification;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Color;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.ColorInt;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompat.Builder; import android.support.v4.app.NotificationCompat.Builder;
import android.util.Log; import android.util.Log;
@ -23,6 +19,12 @@ import com.dougkeen.bart.activities.ViewDeparturesActivity;
import com.dougkeen.bart.services.BoardedDepartureService; import com.dougkeen.bart.services.BoardedDepartureService;
import com.dougkeen.util.Observable; import com.dougkeen.util.Observable;
import org.apache.commons.lang3.time.DateFormatUtils;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Departure implements Parcelable, Comparable<Departure> { public class Departure implements Parcelable, Comparable<Departure> {
private static final int MINIMUM_MERGE_OVERLAP_MILLIS = 5000; private static final int MINIMUM_MERGE_OVERLAP_MILLIS = 5000;
private static final int EXPIRE_MINUTES_AFTER_ARRIVAL = 1; private static final int EXPIRE_MINUTES_AFTER_ARRIVAL = 1;
@ -133,8 +135,9 @@ public class Departure implements Parcelable, Comparable<Departure> {
this.line = line; this.line = line;
} }
public String getTrainDestinationColor() { @ColorInt
return destinationColor; public int getTrainDestinationColor() {
return Color.parseColor(destinationColor);
} }
public void setTrainDestinationColor(String destinationColor) { public void setTrainDestinationColor(String destinationColor) {
@ -627,33 +630,36 @@ public class Departure implements Parcelable, Comparable<Departure> {
final Intent cancelAlarmIntent = new Intent(context, final Intent cancelAlarmIntent = new Intent(context,
BoardedDepartureService.class); BoardedDepartureService.class);
cancelAlarmIntent.putExtra("cancelNotifications", true); cancelAlarmIntent.putExtra("cancelNotifications", true);
String title = getOrigin().shortName + " to " + getPassengerDestination().shortName;
Builder notificationBuilder = new NotificationCompat.Builder(context) Builder notificationBuilder = new NotificationCompat.Builder(context)
.setOngoing(true) .setOngoing(true)
.setSmallIcon(R.drawable.ic_stat_notification) .setSmallIcon(R.drawable.ic_stat_notification)
.setContentTitle( .setContentTitle(title)
getOrigin().shortName + " to "
+ getPassengerDestination().shortName)
.setContentIntent(getNotificationIntent(context)).setWhen(0); .setContentIntent(getNotificationIntent(context)).setWhen(0);
if (android.os.Build.VERSION.SDK_INT >= 16) { if (android.os.Build.VERSION.SDK_INT >= 16) {
notificationBuilder.setPriority(NotificationCompat.PRIORITY_HIGH) notificationBuilder
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentText(minutesText + " until departure"); .setContentText(minutesText + " until departure");
if (isAlarmPending()) { if (isAlarmPending()) {
notificationBuilder.addAction( PendingIntent pendingIntent = PendingIntent.getService(
R.drawable.ic_action_cancel_alarm, context, 0, cancelAlarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
"Cancel alarm", String subText = "Alarm " + getAlarmLeadTimeMinutes() + " minutes before departure";
PendingIntent.getService(context, 0, cancelAlarmIntent,
PendingIntent.FLAG_UPDATE_CURRENT)).setSubText( notificationBuilder
"Alarm " + getAlarmLeadTimeMinutes() .addAction(R.drawable.ic_action_cancel_alarm, "Cancel alarm", pendingIntent)
+ " minutes before departure"); .setSubText(subText);
} }
} else if (isAlarmPending()) { } else if (isAlarmPending()) {
notificationBuilder.setContentText(minutesText String text = minutesText
+ " to departure (alarm at " + getAlarmLeadTimeMinutes() + " to departure (alarm at " + getAlarmLeadTimeMinutes()
+ " min" + ((getAlarmLeadTimeMinutes() == 1) ? "" : "s") + " min" + ((getAlarmLeadTimeMinutes() == 1) ? "" : "s")
+ ")"); + ")";
notificationBuilder.setContentText(text);
} else { } else {
notificationBuilder notificationBuilder.setContentText(minutesText + " until departure");
.setContentText(minutesText + " until departure");
} }
return notificationBuilder.build(); return notificationBuilder.build();

View File

@ -4,7 +4,6 @@ import java.lang.ref.WeakReference;
import java.util.List; import java.util.List;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.app.NotificationManager;
import android.app.Service; import android.app.Service;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
@ -15,6 +14,7 @@ import android.os.HandlerThread;
import android.os.IBinder; import android.os.IBinder;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.support.v4.app.NotificationManagerCompat;
import com.dougkeen.bart.BartRunnerApplication; import com.dougkeen.bart.BartRunnerApplication;
import com.dougkeen.bart.model.Departure; import com.dougkeen.bart.model.Departure;
@ -34,7 +34,7 @@ public class BoardedDepartureService extends Service implements
private boolean mBound = false; private boolean mBound = false;
private EtdService mEtdService; private EtdService mEtdService;
private StationPair mStationPair; private StationPair mStationPair;
private NotificationManager mNotificationManager; private NotificationManagerCompat mNotificationManager;
private AlarmManager mAlarmManager; private AlarmManager mAlarmManager;
private Handler mHandler; private Handler mHandler;
private boolean mHasShutDown = false; private boolean mHasShutDown = false;
@ -91,7 +91,7 @@ public class BoardedDepartureService extends Service implements
bindService(EtdService_.intent(this).get(), mConnection, bindService(EtdService_.intent(this).get(), mConnection,
Context.BIND_AUTO_CREATE); Context.BIND_AUTO_CREATE);
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mNotificationManager = NotificationManagerCompat.from(this);
mAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); mAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
mHandler = new Handler(); mHandler = new Handler();
super.onCreate(); super.onCreate();
@ -302,8 +302,7 @@ public class BoardedDepartureService extends Service implements
.getBoardedDeparture(); .getBoardedDeparture();
if (boardedDeparture != null) { if (boardedDeparture != null) {
mNotificationManager.notify(DEPARTURE_NOTIFICATION_ID, mNotificationManager.notify(DEPARTURE_NOTIFICATION_ID,
boardedDeparture boardedDeparture.createNotification(getApplicationContext()));
.createNotification(getApplicationContext()));
} }
} }

View File

@ -0,0 +1,13 @@
package com.dougkeen.util;
public final class Assert {
// Uninstantiable
private Assert() {}
public static <T> T notNull(T obj) {
if (obj == null) {
throw new AssertionError("Expected object to be non-null");
}
return obj;
}
}

View File

@ -1,77 +1,111 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<merge <com.dougkeen.bart.controls.CheckableLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bart="http://schemas.android.com/apk/res-auto" xmlns:bart="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent"> android:layout_height="fill_parent"
android:gravity="center_vertical"
android:orientation="horizontal"
>
<ImageView <!-- This layout is used when the height is >= 480 dp, eg usually in portrait mode. -->
<View
android:id="@+id/destinationColorBar" android:id="@+id/destinationColorBar"
android:layout_width="wrap_content" android:layout_width="15dp"
android:layout_height="64dp" android:layout_height="64dp"
android:layout_alignParentLeft="true" tools:background="#F00"
android:layout_centerVertical="true" />
android:src="@drawable/basic_rectangle" />
<!-- Holds the destination, the train info, and the arrival time -->
<LinearLayout <LinearLayout
android:id="@+id/topRow" android:layout_width="0dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentTop="true" android:layout_marginLeft="5dp"
android:layout_toRightOf="@id/destinationColorBar"> android:layout_marginRight="5dp"
android:layout_weight="1"
android:orientation="vertical"
>
<TextView <TextView
android:id="@+id/destinationText" android:id="@+id/destinationText"
style="@style/DepartureDestinationText" style="@style/DepartureDestinationText"
android:layout_weight="1"
android:ellipsize="marquee" android:ellipsize="marquee"
android:singleLine="true" /> android:singleLine="true"
tools:text="Richmond"
/>
<TextView
android:id="@+id/trainLengthText"
style="@style/DepartureUncertaintyText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="9 cars, platform 2"
/>
<TextView
android:id="@+id/estimatedArrival"
style="@style/DepartureUncertaintyText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Arrives ~11:12 AM (20 mins)"
/>
</LinearLayout>
<!-- Holds the bike and xfer icons -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<ImageView <ImageView
android:id="@+id/bikeIcon" android:id="@+id/bikeIcon"
style="@style/BikeIcon" /> style="@style/TrainInfoIcon"
tools:src="@drawable/bike"
/>
<ImageView
android:id="@+id/xferIcon"
style="@style/TrainInfoIcon"
android:src="@drawable/xfer"
/>
</LinearLayout>
<!-- Holds the departure countdown, uncertainty, and departure time -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:gravity="right"
android:orientation="vertical"
>
<com.dougkeen.bart.controls.CountdownTextView <com.dougkeen.bart.controls.CountdownTextView
android:id="@+id/countdown" android:id="@+id/countdown"
style="@style/DepartureCountdownText" style="@style/DepartureCountdownText"
android:gravity="right"
android:width="90dp" android:width="90dp"
bart:tickInterval="1" /> android:gravity="right"
bart:tickInterval="1"
tools:text="9m, 57s"
/>
<TextView
android:id="@+id/uncertainty"
style="@style/DepartureUncertaintyText"
tools:text="(+8s)"
/>
<TextView
android:id="@+id/departureTime"
style="@style/DepartureUncertaintyText"
tools:text="11:20 AM"
/>
</LinearLayout> </LinearLayout>
<ImageView </com.dougkeen.bart.controls.CheckableLinearLayout>
android:id="@+id/xferIcon"
style="@style/XferIcon"
android:layout_alignParentRight="true"
android:layout_below="@id/topRow"
android:src="@drawable/xfer" />
<TextView
android:id="@+id/trainLengthText"
style="@style/DepartureUncertaintyText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/topRow"
android:layout_toRightOf="@id/destinationColorBar" />
<TextView
android:id="@+id/uncertainty"
style="@style/DepartureUncertaintyText"
android:layout_alignParentRight="true"
android:layout_below="@id/topRow" />
<TextView
android:id="@+id/estimatedArrival"
style="@style/DepartureUncertaintyText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/trainLengthText"
android:layout_toRightOf="@id/destinationColorBar" />
<TextView
android:id="@+id/departureTime"
style="@style/DepartureUncertaintyText"
android:layout_alignParentRight="true"
android:layout_below="@id/uncertainty" />
</merge>

View File

@ -1,64 +1,98 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<merge <com.dougkeen.bart.controls.CheckableLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bart="http://schemas.android.com/apk/res-auto" xmlns:bart="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent"> android:layout_height="fill_parent"
android:gravity="center_vertical"
android:orientation="horizontal"
>
<ImageView <!-- This layout is used when the height is less than 480 dp (eg usually in landscape mode) -->
<View
android:id="@+id/destinationColorBar" android:id="@+id/destinationColorBar"
android:layout_width="wrap_content" android:layout_width="15dp"
android:layout_height="45dp" android:layout_height="45dp"
android:layout_alignParentLeft="true" tools:background="#F00"
android:layout_centerVertical="true" />
android:src="@drawable/basic_rectangle" />
<!-- Holds destination and train info/arrival -->
<LinearLayout <LinearLayout
android:id="@+id/topRow" android:layout_width="wrap_content"
android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentTop="true" android:layout_marginLeft="5dp"
android:layout_toRightOf="@id/destinationColorBar"> android:layout_marginRight="5dp"
android:layout_weight="1"
android:orientation="vertical"
>
<TextView <TextView
android:id="@+id/destinationText" android:id="@+id/destinationText"
style="@style/DepartureDestinationText" style="@style/DepartureDestinationText"
android:layout_weight="1"
android:ellipsize="marquee" android:ellipsize="marquee"
android:singleLine="true" /> android:singleLine="true"
tools:text="Richmond"
/>
<com.dougkeen.bart.controls.TimedTextSwitcher
android:id="@+id/trainLengthText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
bart:tickInterval="1"
tools:text="9 cars, platform 2"
/>
</LinearLayout>
<!-- Holds bike and xfer icons -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<ImageView <ImageView
android:id="@+id/bikeIcon" android:id="@+id/bikeIcon"
style="@style/BikeIcon" /> style="@style/TrainInfoIcon"
tools:src="@drawable/bike"
/>
<ImageView
android:id="@+id/xferIcon"
style="@style/TrainInfoIcon"
android:src="@drawable/xfer"
/>
</LinearLayout>
<!-- Holds departure countdown and uncertainty/departure time-->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:gravity="right"
android:orientation="vertical"
>
<com.dougkeen.bart.controls.CountdownTextView <com.dougkeen.bart.controls.CountdownTextView
android:id="@+id/countdown" android:id="@+id/countdown"
style="@style/DepartureCountdownText" style="@style/DepartureCountdownText"
android:gravity="right"
android:width="90dp" android:width="90dp"
bart:tickInterval="1" /> android:gravity="right"
bart:tickInterval="1"
tools:text="9m, 57s"
/>
<com.dougkeen.bart.controls.TimedTextSwitcher
android:id="@+id/uncertainty"
style="@style/DepartureUncertaintyText"
bart:tickInterval="1"
tools:text="(+8s)"
/>
</LinearLayout> </LinearLayout>
<ImageView </com.dougkeen.bart.controls.CheckableLinearLayout>
android:id="@+id/xferIcon"
style="@style/XferIcon"
android:layout_alignParentRight="true"
android:layout_below="@id/topRow"
android:src="@drawable/xfer" />
<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"
bart:tickInterval="1" />
<com.dougkeen.bart.controls.TimedTextSwitcher
android:id="@+id/uncertainty"
style="@style/DepartureUncertaintyText"
android:layout_alignParentRight="true"
android:layout_below="@id/topRow"
bart:tickInterval="1" />
</merge>

View File

@ -1,82 +1,121 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<merge <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bart="http://schemas.android.com/apk/res-auto" > xmlns:bart="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView <!-- Holds the "Your Train" title and the alarm indicator -->
android:id="@+id/alarmText" <FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<TextView
android:id="@+id/yourTrainHeader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/your_train"
android:textAllCaps="true"
android:textSize="20dp"
android:textStyle="bold"
/>
<TextView
android:id="@+id/alarmText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:drawableLeft="@drawable/ic_action_alarm"
android:gravity="center_vertical"
android:textSize="16dp"
android:visibility="gone"
tools:text="5"
/>
</FrameLayout>
<!-- Holds the color bar, destination text, train info, bike icon, and xfer icon -->
<LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentRight="true" android:orientation="horizontal"
android:layout_alignParentTop="true" >
android:drawableLeft="@drawable/ic_action_alarm"
android:gravity="center_vertical"
android:textSize="16dp"
android:visibility="gone"></TextView>
<TextView <View
android:id="@+id/yourTrainHeader" android:id="@+id/yourTrainDestinationColorBar"
android:layout_width="wrap_content" android:layout_width="15dp"
android:layout_height="wrap_content" android:layout_height="45dp"
android:layout_alignParentTop="true" android:layout_gravity="center_vertical"
android:layout_centerHorizontal="true" tools:background="#F00"
android:text="@string/your_train" />
android:textAllCaps="true"
android:textSize="20dp"
android:textStyle="bold" />
<ImageView <!-- Holds the destination text and train info -->
android:id="@+id/yourTrainDestinationColorBar" <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="45dp" android:layout_height="wrap_content"
android:layout_alignParentLeft="true" android:layout_gravity="center_vertical"
android:layout_below="@id/yourTrainHeader" android:orientation="vertical"
android:src="@drawable/basic_rectangle" /> android:padding="5dp"
>
<TextView <TextView
android:id="@+id/yourTrainDestinationText" android:id="@+id/yourTrainDestinationText"
style="@style/DepartureDestinationText" style="@style/DepartureDestinationText"
android:layout_below="@id/yourTrainHeader" android:ellipsize="marquee"
android:layout_toRightOf="@id/yourTrainDestinationColorBar" android:singleLine="true"
android:ellipsize="marquee" tools:text="Richmond"
android:singleLine="true" /> />
<ImageView <TextView
android:id="@+id/yourTrainBikeIcon" android:id="@+id/yourTrainTrainLengthText"
style="@style/BikeIcon" android:layout_width="wrap_content"
android:layout_below="@id/yourTrainHeader" android:layout_height="wrap_content"
android:layout_toRightOf="@id/yourTrainDestinationText" /> tools:text="9 cars, platform 2"
/>
<ImageView </LinearLayout>
android:id="@+id/yourTrainXferIcon"
style="@style/XferIcon"
android:layout_below="@id/yourTrainBikeIcon"
android:layout_toRightOf="@id/yourTrainDestinationText"
android:src="@drawable/xfer" />
<TextView <!-- Holds the bike icon and xfer icon -->
android:id="@+id/yourTrainTrainLengthText" <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/yourTrainDestinationText" android:orientation="vertical"
android:layout_toRightOf="@id/yourTrainDestinationColorBar" >
android:paddingLeft="5dp" />
<ImageView
android:id="@+id/yourTrainBikeIcon"
style="@style/TrainInfoIcon"
tools:src="@drawable/bike"
/>
<ImageView
android:id="@+id/yourTrainXferIcon"
style="@style/TrainInfoIcon"
android:src="@drawable/xfer"
/>
</LinearLayout>
</LinearLayout>
<com.dougkeen.bart.controls.CountdownTextView <com.dougkeen.bart.controls.CountdownTextView
android:id="@+id/yourTrainDepartureCountdown" android:id="@+id/yourTrainDepartureCountdown"
style="@style/DepartureCountdownText" style="@style/DepartureCountdownText"
android:layout_alignLeft="@id/yourTrainSection" bart:tickInterval="1"
android:layout_alignRight="@id/yourTrainSection" tools:text="Leaves in 15 min, 20s (+8s)"
android:layout_below="@id/yourTrainTrainLengthText" />
bart:tickInterval="1" />
<com.dougkeen.bart.controls.CountdownTextView <com.dougkeen.bart.controls.CountdownTextView
android:id="@+id/yourTrainArrivalCountdown" android:id="@+id/yourTrainArrivalCountdown"
style="@style/DepartureCountdownText" style="@style/DepartureCountdownText"
android:layout_alignLeft="@id/yourTrainSection"
android:layout_alignRight="@id/yourTrainSection"
android:layout_below="@id/yourTrainDepartureCountdown"
android:ellipsize="end" android:ellipsize="end"
bart:tickInterval="5" /> bart:tickInterval="5"
tools:text="Arrives ~11:12 AM (20 mins)"
/>
</merge> </LinearLayout>

View File

@ -92,18 +92,10 @@
<item name="android:gravity">center</item> <item name="android:gravity">center</item>
</style> </style>
<style name="BikeIcon"> <style name="TrainInfoIcon">
<item name="android:layout_width">wrap_content</item> <item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item> <item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginTop">5dp</item> <item name="android:layout_margin">5dp</item>
<item name="android:layout_marginLeft">5dp</item>
</style>
<style name="XferIcon">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginRight">87dp</item>
<item name="android:layout_marginBottom">5dp</item>
</style> </style>
</resources> </resources>