* Drop support for Android versions < 4.0

* Convert all http client to use OkHttp
* Use Android number picker instead of third party lib
This commit is contained in:
Doug Keen 2015-10-18 17:26:30 -07:00
parent 4d98f0df42
commit 958541e2af
10 changed files with 116 additions and 133 deletions

View File

@ -7,7 +7,7 @@ dependencies {
compile 'org.apache.commons:commons-lang3:3.4' compile 'org.apache.commons:commons-lang3:3.4'
compile 'com.fasterxml.jackson.core:jackson-databind:2.6.1' compile 'com.fasterxml.jackson.core:jackson-databind:2.6.1'
compile 'org.springframework.android:spring-android-rest-template:1.0.1.RELEASE' compile 'org.springframework.android:spring-android-rest-template:1.0.1.RELEASE'
compile 'com.code-troopers.betterpickers:library:2.0.0' compile 'com.squareup.okhttp:okhttp:2.5.0'
compile 'com.android.support:appcompat-v7:22.2.1' compile 'com.android.support:appcompat-v7:22.2.1'
@ -16,7 +16,7 @@ dependencies {
} }
android { android {
compileSdkVersion 22 compileSdkVersion 23
buildToolsVersion "22.0.1" buildToolsVersion "22.0.1"
compileOptions { compileOptions {
@ -25,12 +25,11 @@ android {
} }
defaultConfig { defaultConfig {
versionName "2.2.4" versionName "2.2.5"
versionCode 31 versionCode 32
// TODO(fuegofro) - bring this back down to 8 if it's easy by using a different number picker library minSdkVersion 14
minSdkVersion 9 targetSdkVersion 23
targetSdkVersion 22
} }
buildTypes { buildTypes {

View File

@ -13,8 +13,8 @@ import android.support.v4.app.FragmentActivity;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.NumberPicker;
import com.codetroopers.betterpickers.numberpicker.NumberPicker;
import com.dougkeen.bart.BartRunnerApplication; 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;
@ -54,8 +54,8 @@ public class TrainAlarmDialogFragment extends DialogFragment {
final Departure boardedDeparture = application.getBoardedDeparture(); final Departure boardedDeparture = application.getBoardedDeparture();
final int maxValue = boardedDeparture.getMeanSecondsLeft() / 60; final int maxValue = boardedDeparture.getMeanSecondsLeft() / 60;
numberPicker.setMin(1); numberPicker.setMinValue(1);
numberPicker.setMax(maxValue); numberPicker.setMaxValue(maxValue);
if (boardedDeparture.isAlarmPending()) { if (boardedDeparture.isAlarmPending()) {
setNumber(numberPicker, boardedDeparture.getAlarmLeadTimeMinutes()); setNumber(numberPicker, boardedDeparture.getAlarmLeadTimeMinutes());
@ -71,8 +71,7 @@ public class TrainAlarmDialogFragment extends DialogFragment {
} }
private void setNumber(NumberPicker numberPicker, int value) { private void setNumber(NumberPicker numberPicker, int value) {
// Passing in null for the decimalPart and sign doesn't change them. numberPicker.setValue(value);
numberPicker.setNumber(value, null /* decimalPart */, null /* sign */);
} }
@NonNull @NonNull
@ -95,7 +94,7 @@ public class TrainAlarmDialogFragment extends DialogFragment {
int which) { int which) {
NumberPicker numberPicker = (NumberPicker) getDialog() NumberPicker numberPicker = (NumberPicker) getDialog()
.findViewById(R.id.numberPicker); .findViewById(R.id.numberPicker);
final int alarmLeadTime = numberPicker.getNumber(); final int alarmLeadTime = numberPicker.getValue();
// Save most recent selection // Save most recent selection
Editor editor = getActivity().getPreferences( Editor editor = getActivity().getPreferences(

View File

@ -19,10 +19,9 @@
package com.dougkeen.bart.controls; package com.dougkeen.bart.controls;
import static com.nineoldandroids.view.ViewHelper.setAlpha; import android.animation.Animator;
import static com.nineoldandroids.view.ViewHelper.setTranslationX; import android.animation.AnimatorListenerAdapter;
import static com.nineoldandroids.view.ViewPropertyAnimator.animate; import android.animation.ValueAnimator;
import android.util.Log; import android.util.Log;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.VelocityTracker; import android.view.VelocityTracker;
@ -32,9 +31,6 @@ import android.view.ViewConfiguration;
import android.view.ViewGroup; import android.view.ViewGroup;
import com.dougkeen.bart.model.Constants; import com.dougkeen.bart.model.Constants;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorListenerAdapter;
import com.nineoldandroids.animation.ValueAnimator;
/** /**
* A utility class for animating the dismissal of a view (with 'onDismiss' * A utility class for animating the dismissal of a view (with 'onDismiss'
@ -167,7 +163,7 @@ public class SwipeHelper implements View.OnTouchListener {
dismissWithAnimation(dismissRight); dismissWithAnimation(dismissRight);
} else { } else {
// cancel // cancel
animate(mView).translationX(0).alpha(1) mView.animate().translationX(0).alpha(1)
.setDuration(mAnimationTime).setListener(null); .setDuration(mAnimationTime).setListener(null);
} }
mVelocityTracker = null; mVelocityTracker = null;
@ -198,10 +194,9 @@ public class SwipeHelper implements View.OnTouchListener {
if (mSwiping) { if (mSwiping) {
mTranslationX = deltaX; mTranslationX = deltaX;
setTranslationX(mView, deltaX); mView.setTranslationX(deltaX);
// TODO: use an ease-out interpolator or such // TODO: use an ease-out interpolator or such
setAlpha( mView.setAlpha(
mView,
Math.max( Math.max(
0f, 0f,
Math.min(1f, 1f - 2f * Math.abs(deltaX) Math.min(1f, 1f - 2f * Math.abs(deltaX)
@ -215,7 +210,7 @@ public class SwipeHelper implements View.OnTouchListener {
} }
public void dismissWithAnimation(boolean dismissRight) { public void dismissWithAnimation(boolean dismissRight) {
animate(mView).translationX(dismissRight ? mViewWidth : -mViewWidth) mView.animate().translationX(dismissRight ? mViewWidth : -mViewWidth)
.alpha(0).setDuration(mAnimationTime) .alpha(0).setDuration(mAnimationTime)
.setListener(new AnimatorListenerAdapter() { .setListener(new AnimatorListenerAdapter() {
@Override @Override
@ -250,7 +245,7 @@ public class SwipeHelper implements View.OnTouchListener {
*/ */
// setAlpha(mView, 1f); // setAlpha(mView, 1f);
setTranslationX(mView, 0); mView.setTranslationX(0);
lp.height = originalHeight; lp.height = originalHeight;
mView.setLayoutParams(lp); mView.setLayoutParams(lp);
} }
@ -273,12 +268,12 @@ public class SwipeHelper implements View.OnTouchListener {
mView.measure(measureSpec, measureSpec); mView.measure(measureSpec, measureSpec);
mViewWidth = mView.getMeasuredWidth(); mViewWidth = mView.getMeasuredWidth();
final int viewHeight = mView.getMeasuredHeight(); final int viewHeight = mView.getMeasuredHeight();
setAlpha(mView, 0f); mView.setAlpha(0f);
final ViewGroup.LayoutParams lp = mView.getLayoutParams(); final ViewGroup.LayoutParams lp = mView.getLayoutParams();
final int originalHeight = lp.height; final int originalHeight = lp.height;
setTranslationX(mView, mViewWidth); mView.setTranslationX(mViewWidth);
// Grow space // Grow space
ValueAnimator animator = ValueAnimator.ofInt(1, viewHeight) ValueAnimator animator = ValueAnimator.ofInt(1, viewHeight)
@ -292,7 +287,7 @@ public class SwipeHelper implements View.OnTouchListener {
mView.setLayoutParams(lp); mView.setLayoutParams(lp);
// Swipe view into space that opened up // Swipe view into space that opened up
animate(mView).translationX(0).alpha(1) mView.animate().translationX(0).alpha(1)
.setDuration(mAnimationTime) .setDuration(mAnimationTime)
// Dummy listener so the default doesn't run // Dummy listener so the default doesn't run
.setListener(new AnimatorListenerAdapter() { .setListener(new AnimatorListenerAdapter() {

View File

@ -1,17 +1,10 @@
package com.dougkeen.bart.networktasks; package com.dougkeen.bart.networktasks;
import java.io.IOException; import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.util.List; import java.util.List;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.util.Log; import android.util.Log;
import android.util.Xml; import android.util.Xml;
@ -20,6 +13,9 @@ import com.dougkeen.bart.model.Constants;
import com.dougkeen.bart.model.RealTimeDepartures; import com.dougkeen.bart.model.RealTimeDepartures;
import com.dougkeen.bart.model.Route; import com.dougkeen.bart.model.Route;
import com.dougkeen.bart.model.StationPair; import com.dougkeen.bart.model.StationPair;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
public abstract class GetRealTimeDeparturesTask extends public abstract class GetRealTimeDeparturesTask extends
AsyncTask<StationPair, Integer, RealTimeDepartures> { AsyncTask<StationPair, Integer, RealTimeDepartures> {
@ -30,6 +26,8 @@ public abstract class GetRealTimeDeparturesTask extends
+ Constants.API_KEY + "&orig=%1$s"; + Constants.API_KEY + "&orig=%1$s";
private final static int MAX_ATTEMPTS = 5; private final static int MAX_ATTEMPTS = 5;
private final OkHttpClient mClient = NetworkUtils.makeHttpClient();
private Exception mException; private Exception mException;
private List<Route> mRoutes; private List<Route> mRoutes;
@ -83,7 +81,9 @@ public abstract class GetRealTimeDeparturesTask extends
mRoutes.get(0).getDirection()); mRoutes.get(0).getDirection());
} }
HttpUriRequest request = new HttpGet(url);
Request request = new Request.Builder()
.url(url).build();
EtdContentHandler handler = new EtdContentHandler( EtdContentHandler handler = new EtdContentHandler(
params.getOrigin(), params.getDestination(), mRoutes); params.getOrigin(), params.getDestination(), mRoutes);
@ -91,17 +91,13 @@ public abstract class GetRealTimeDeparturesTask extends
return null; return null;
} }
HttpResponse response = NetworkUtils.executeWithRecovery(request); Response response = mClient.newCall(request).execute();
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { if (!response.isSuccessful()) {
throw new IOException("Server returned " throw new IOException("Server returned " + response.code());
+ response.getStatusLine().toString());
} }
StringWriter writer = new StringWriter(); xml = response.body().string();
IOUtils.copy(response.getEntity().getContent(), writer, "UTF-8");
xml = writer.toString();
if (xml.length() == 0) { if (xml.length() == 0) {
throw new IOException("Server returned blank xml document"); throw new IOException("Server returned blank xml document");
} }

View File

@ -1,23 +1,20 @@
package com.dougkeen.bart.networktasks; package com.dougkeen.bart.networktasks;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.xml.sax.SAXException;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.util.Log; import android.util.Log;
import android.util.Xml; import android.util.Xml;
import com.dougkeen.bart.model.Constants; import com.dougkeen.bart.model.Constants;
import com.dougkeen.bart.model.Station; import com.dougkeen.bart.model.Station;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
public abstract class GetRouteFareTask extends public abstract class GetRouteFareTask extends
AsyncTask<GetRouteFareTask.Params, Integer, String> { AsyncTask<GetRouteFareTask.Params, Integer, String> {
@ -26,6 +23,8 @@ public abstract class GetRouteFareTask extends
private final static String FARE_URL = "http://api.bart.gov/api/sched.aspx?cmd=fare&date=today&key=" private final static String FARE_URL = "http://api.bart.gov/api/sched.aspx?cmd=fare&date=today&key="
+ Constants.API_KEY + "&orig=%1$s&dest=%2$s"; + Constants.API_KEY + "&orig=%1$s&dest=%2$s";
private final static OkHttpClient client = NetworkUtils.makeHttpClient();
private Exception mException; private Exception mException;
private String fare; private String fare;
@ -45,26 +44,21 @@ public abstract class GetRouteFareTask extends
String xml = null; String xml = null;
try { try {
HttpUriRequest request = new HttpGet( Request request = new Request.Builder()
String.format(FARE_URL, params.origin.abbreviation, .url(String.format(FARE_URL, params.origin.abbreviation, params.destination.abbreviation)).build();
params.destination.abbreviation));
FareContentHandler handler = new FareContentHandler(); FareContentHandler handler = new FareContentHandler();
if (isCancelled()) { if (isCancelled()) {
return null; return null;
} }
HttpResponse response = NetworkUtils.executeWithRecovery(request); Response response = client.newCall(request).execute();
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { if (!response.isSuccessful()) {
throw new IOException("Server returned " throw new IOException("Server returned " + response.code());
+ response.getStatusLine().toString());
} }
StringWriter writer = new StringWriter(); xml = response.body().string();
IOUtils.copy(response.getEntity().getContent(), writer, "UTF-8");
xml = writer.toString();
if (xml.length() == 0) { if (xml.length() == 0) {
throw new IOException("Server returned blank xml document"); throw new IOException("Server returned blank xml document");
} }

View File

@ -1,17 +1,5 @@
package com.dougkeen.bart.networktasks; package com.dougkeen.bart.networktasks;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.xml.sax.SAXException;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.util.Log; import android.util.Log;
import android.util.Xml; import android.util.Xml;
@ -19,6 +7,15 @@ import android.util.Xml;
import com.dougkeen.bart.model.Constants; import com.dougkeen.bart.model.Constants;
import com.dougkeen.bart.model.ScheduleInformation; import com.dougkeen.bart.model.ScheduleInformation;
import com.dougkeen.bart.model.StationPair; import com.dougkeen.bart.model.StationPair;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
public abstract class GetScheduleInformationTask extends public abstract class GetScheduleInformationTask extends
AsyncTask<StationPair, Integer, ScheduleInformation> { AsyncTask<StationPair, Integer, ScheduleInformation> {
@ -28,6 +25,8 @@ public abstract class GetScheduleInformationTask extends
private final static int MAX_ATTEMPTS = 5; private final static int MAX_ATTEMPTS = 5;
private final static OkHttpClient client = NetworkUtils.makeHttpClient();
private Exception mException; private Exception mException;
@Override @Override
@ -50,7 +49,7 @@ public abstract class GetScheduleInformationTask extends
params.getOrigin().abbreviation, params.getOrigin().abbreviation,
params.getDestination().abbreviation); params.getDestination().abbreviation);
HttpUriRequest request = new HttpGet(url); Request request = new Request.Builder().url(url).build();
if (isCancelled()) { if (isCancelled()) {
return null; return null;
@ -59,17 +58,13 @@ public abstract class GetScheduleInformationTask extends
ScheduleContentHandler handler = new ScheduleContentHandler( ScheduleContentHandler handler = new ScheduleContentHandler(
params.getOrigin(), params.getDestination()); params.getOrigin(), params.getDestination());
HttpResponse response = NetworkUtils.executeWithRecovery(request); Response response = client.newCall(request).execute();
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { if (!response.isSuccessful()) {
throw new IOException("Server returned " throw new IOException("Server returned " + response.code());
+ response.getStatusLine().toString());
} }
StringWriter writer = new StringWriter(); xml = response.body().string();
IOUtils.copy(response.getEntity().getContent(), writer, "UTF-8");
xml = writer.toString();
if (xml.length() == 0) { if (xml.length() == 0) {
throw new IOException("Server returned blank xml document"); throw new IOException("Server returned blank xml document");
} }

View File

@ -1,40 +1,47 @@
package com.dougkeen.bart.networktasks; package com.dougkeen.bart.networktasks;
import java.io.IOException; import android.util.Log;
import org.apache.http.HttpResponse; import com.squareup.okhttp.Interceptor;
import org.apache.http.client.ClientProtocolException; import com.squareup.okhttp.OkHttpClient;
import org.apache.http.client.HttpClient; import com.squareup.okhttp.Request;
import org.apache.http.client.methods.HttpUriRequest; import com.squareup.okhttp.Response;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.impl.client.DefaultHttpClient; import java.io.IOException;
import org.apache.http.params.HttpConnectionParams; import java.util.concurrent.TimeUnit;
import org.apache.http.params.HttpParams;
public class NetworkUtils { public class NetworkUtils {
public static HttpClient getHttpClient() { private static class RetryInterceptor implements Interceptor {
HttpClient client = new DefaultHttpClient(); private static final String TAG = "RetryInterceptor";
final HttpParams params = client.getParams();
HttpConnectionParams.setConnectionTimeout(params,
NetworkUtils.CONNECTION_TIMEOUT_MILLIS);
HttpConnectionParams.setSoTimeout(params,
NetworkUtils.CONNECTION_TIMEOUT_MILLIS);
ConnManagerParams.setTimeout(params,
NetworkUtils.CONNECTION_TIMEOUT_MILLIS);
return client;
}
public static HttpResponse executeWithRecovery(final HttpUriRequest request) @Override
throws IOException { public Response intercept(Chain chain) throws IOException {
try { Request request = chain.request();
return getHttpClient().execute(request);
} catch (IllegalStateException e) { // try the request
// try again... this is a rare error Response response;
return getHttpClient().execute(request); int attempt = 0;
do {
attempt++;
try {
response = chain.proceed(request);
} catch (IOException e) {
Log.w(TAG, "Request failed: ", e);
response = null;
}
} while ((response == null || !response.isSuccessful()) && attempt < 2);
return response;
} }
} }
static final int CONNECTION_TIMEOUT_MILLIS = 10000; public static OkHttpClient makeHttpClient() {
OkHttpClient client = new OkHttpClient();
client.setConnectTimeout(CONNECTION_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
client.interceptors().add(new RetryInterceptor());
return client;
}
static final int CONNECTION_TIMEOUT_MILLIS = 10000;
} }

View File

@ -1,10 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="horizontal">
>
<NumberPicker
android:id="@+id/numberPicker"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="2" />
<TextView <TextView
android:id="@+id/textView1" android:id="@+id/textView1"
@ -14,15 +18,8 @@
android:layout_weight="1" android:layout_weight="1"
android:gravity="center_vertical" android:gravity="center_vertical"
android:maxLines="3" android:maxLines="3"
android:paddingLeft="10sp"
android:singleLine="false" android:singleLine="false"
android:text="minutes before departure" android:text="@string/minutes_before_departure" />
/>
<com.codetroopers.betterpickers.numberpicker.NumberPicker
android:id="@+id/numberPicker"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
</LinearLayout> </LinearLayout>

View File

@ -37,7 +37,7 @@
<string name="getting_on_this_train">I will board this train</string> <string name="getting_on_this_train">I will board this train</string>
<string name="departure_options">Departure options</string> <string name="departure_options">Departure options</string>
<string name="your_train">Your train</string> <string name="your_train">Your train</string>
<string name="set_up_departure_alarm">Set up departure alarm</string> <string name="set_up_departure_alarm">Set alarm</string>
<string name="train_alarm_text">Your train is leaving soon!</string> <string name="train_alarm_text">Your train is leaving soon!</string>
<string name="silence_alarm">Silence alarm</string> <string name="silence_alarm">Silence alarm</string>
<string name="cancel_alarm">Cancel alarm</string> <string name="cancel_alarm">Cancel alarm</string>
@ -47,5 +47,6 @@
<string name="arrives_at_destination">Arrive @ dest. ~</string> <string name="arrives_at_destination">Arrive @ dest. ~</string>
<string name="arrival_message">I\'ll be arriving at %1$s around %2$s</string> <string name="arrival_message">I\'ll be arriving at %1$s around %2$s</string>
<string name="share_arrival_time">Share arrival time</string> <string name="share_arrival_time">Share arrival time</string>
<string name="minutes_before_departure">minutes before departure</string>
</resources> </resources>

View File

@ -3,7 +3,7 @@
<!-- Base application theme is the default theme. --> <!-- Base application theme is the default theme. -->
<style name="AppTheme" parent="Theme.AppCompat"></style> <style name="AppTheme" parent="Theme.AppCompat"/>
<style name="ButtonBar"> <style name="ButtonBar">
<item name="android:layout_width">fill_parent</item> <item name="android:layout_width">fill_parent</item>