From a77b10d3b4f0f7c06b68915aa69ab1a594318929 Mon Sep 17 00:00:00 2001 From: Jeremy Jao Date: Sat, 7 Nov 2015 18:06:52 -0500 Subject: [PATCH 1/6] Update code version and android libs, add retrolambda, strip getVehicles code, refactored busUpdate to be good --- .gitignore | 1 - Realtime-Port-Authority.iml | 2 +- app/app.iml | 45 ++-- app/build.gradle | 54 ++++- .../BusInformationDialog.java | 7 +- .../NavigationDrawerFragment.java | 26 +-- .../SelectTransit.java | 206 ++++-------------- .../handlers/InputSave.java | 2 +- .../handlers/RequestPredictions.java | 2 +- .../patapi/PortAuthorityAPI.java | 149 +++++++++++++ .../jsonpojo/BustimeVehicleResponse.java | 22 ++ 11 files changed, 291 insertions(+), 225 deletions(-) create mode 100644 app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/patapi/PortAuthorityAPI.java diff --git a/.gitignore b/.gitignore index b7c97a57..806cc148 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,6 @@ apks/ /app/apks /app/src/debug/res/values/google_maps_api.xml /app/src/release/res/values/google_maps_api.xml -/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/hidden/ /app/build/ /build/ /local.properties diff --git a/Realtime-Port-Authority.iml b/Realtime-Port-Authority.iml index 23984a95..35276a70 100644 --- a/Realtime-Port-Authority.iml +++ b/Realtime-Port-Authority.iml @@ -8,7 +8,7 @@ - + diff --git a/app/app.iml b/app/app.iml index 05a32ca7..c1f917e1 100644 --- a/app/app.iml +++ b/app/app.iml @@ -26,9 +26,9 @@ - - - + + + @@ -71,13 +71,16 @@ - - - - - - - + + + + + + + + + + @@ -93,26 +96,30 @@ + + + + - - + - + + + + + - - - - - - + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index c895964f..feb84504 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,6 @@ apply plugin: 'com.android.application' + //must define PAT_API_KEY in gradle.properties by adding pat_api= on the last line non-commented def PAT_API_KEY = '"' + pat_api + '"' ?: '"Define Port Authority TrueTime API Key in gradle.properties"'; @@ -10,13 +11,13 @@ android.buildTypes.each { type -> android { compileSdkVersion 23 - buildToolsVersion '23.0.1' + buildToolsVersion '23.0.2' defaultConfig { applicationId "rectangledbmi.com.pittsburghrealtimetracker" minSdkVersion 16 targetSdkVersion 23 - versionCode 54 - versionName "4.8" + versionCode 55 + versionName "5.0" } buildTypes { @@ -25,33 +26,62 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } - productFlavors { + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } } +repositories { + mavenCentral() +} + dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') + repositories { + mavenCentral() + } //google play services maps - compile 'com.google.android.gms:play-services-maps:7.8.0' + compile 'com.google.android.gms:play-services-maps:8.3.0' //google play services location and places - compile 'com.google.android.gms:play-services-location:7.8.0' + compile 'com.google.android.gms:play-services-location:8.3.0' - compile 'com.android.support:support-v4:23.0.1' - compile 'com.android.support:design:23.0.1'; - compile 'com.android.support:appcompat-v7:23.0.1' + compile 'com.android.support:support-v4:23.1.0' + compile 'com.android.support:design:23.1.0'; + compile 'com.android.support:appcompat-v7:23.1.0' - compile 'com.android.support:mediarouter-v7:23.0.1' + compile 'com.android.support:mediarouter-v7:23.1.0' //3rd party android libraries compile 'io.reactivex:rxandroid:1.0.1' - compile 'io.reactivex:rxjava:1.0.14' + compile 'io.reactivex:rxjava:1.0.15' compile 'com.squareup.retrofit:retrofit:1.9.0' compile 'org.glassfish:javax.annotation:10.0-b28' - debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1' releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' } + +buildscript { + repositories { + mavenCentral() + jcenter() + } + + dependencies { + classpath 'me.tatarka:gradle-retrolambda:3.2.3' // use some java 8 syntax in the project + classpath 'com.github.ben-manes:gradle-versions-plugin:0.11.3' // check for updates of 3rd party libs + } +} + +// Required because retrolambda is on maven central +repositories { + jcenter() + mavenCentral() +} + +apply plugin: 'me.tatarka.retrolambda' +apply plugin: 'com.github.ben-manes.versions' diff --git a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/BusInformationDialog.java b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/BusInformationDialog.java index 9644ed11..4aa78b00 100644 --- a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/BusInformationDialog.java +++ b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/BusInformationDialog.java @@ -42,12 +42,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { Button negBut = (Button) view.findViewById(R.id.info_dismiss); negBut.setBackgroundColor(getResources().getColor(R.color.blue_500_trans)); negBut.setTextColor(Color.WHITE); - negBut.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - dismiss(); - } - }); + negBut.setOnClickListener(v -> dismiss()); //Set title builder.setTitle(title); diff --git a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/NavigationDrawerFragment.java b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/NavigationDrawerFragment.java index d474524d..0156e6ef 100644 --- a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/NavigationDrawerFragment.java +++ b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/NavigationDrawerFragment.java @@ -136,34 +136,13 @@ public void onItemClick(AdapterView parent, View view, int position, long id) ); mDrawerListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); // mDrawerListView.setItemChecked(mCurrentSelectedPosition, true); - if(savedInstanceState != null) { + if (savedInstanceState != null) { mDrawerListView.onRestoreInstanceState(savedInstanceState.getParcelable(DRAWER_STATE)); savedInstanceState.putBooleanArray(STATE_SELECTED_POSITIONS, mSelected); } -// restoreListView(); return mDrawerListView; } -/* private void restoreListView() { - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity()); -// System.out.println("In restore view"); -// mSelected = new boolean[getResources().getStringArray(R.array.buses).length]; - int list_size = sp.getInt(BUSLIST_SIZE, -1); - if(getResources().getStringArray(R.array.buses).length == list_size) { - Set listIds = sp.getStringSet(STATE_SELECTED_POSITIONS, Collections.synchronizedSet(new HashSet(0))); - Log.d("restoring listview", listIds.toString()); - for (String selected : listIds) { - setTrue(Integer.parseInt(selected)); -// System.out.println("Restoring: " + selected); - } - } else { - //Something should be here... - if(list_size != -1) - Toast.makeText(getActivity(), "New buses were added. Please re-select your buses", Toast.LENGTH_LONG).show(); - } - - }*/ - /** * Creates an arraylist of routes for the list view. * @@ -433,9 +412,8 @@ protected void clearSelection() { if(lineInfo.exists()) { File[] files = lineInfo.listFiles(); if(files != null) { - for(File file : files) { + for(File file : files) file.delete(); - } } } Toast.makeText(getActivity(), getString(R.string.cleared), Toast.LENGTH_SHORT).show(); diff --git a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/SelectTransit.java b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/SelectTransit.java index 29e7e94a..4301cca2 100644 --- a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/SelectTransit.java +++ b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/SelectTransit.java @@ -41,7 +41,6 @@ import com.google.android.gms.maps.MapFragment; import com.google.android.gms.maps.OnMapReadyCallback; import com.google.android.gms.maps.model.BitmapDescriptorFactory; -import com.google.android.gms.maps.model.CameraPosition; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.Marker; import com.google.android.gms.maps.model.MarkerOptions; @@ -51,12 +50,14 @@ import com.squareup.leakcanary.LeakCanary; import java.io.File; +import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -70,7 +71,6 @@ import rectangledbmi.com.pittsburghrealtimetracker.world.Route; import rectangledbmi.com.pittsburghrealtimetracker.world.TransitStop; import rectangledbmi.com.pittsburghrealtimetracker.world.jsonpojo.BustimeVehicleResponse; -import rectangledbmi.com.pittsburghrealtimetracker.world.jsonpojo.Error; import rectangledbmi.com.pittsburghrealtimetracker.world.jsonpojo.Vehicle; import rectangledbmi.com.pittsburghrealtimetracker.world.jsonpojo.VehicleResponse; import retrofit.RestAdapter; @@ -78,11 +78,10 @@ import retrofit.converter.GsonConverter; import rx.Observable; import rx.Observer; +import rx.Subscriber; import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; -import rx.functions.Action0; import rx.functions.Action1; -import rx.functions.Func1; import rx.schedulers.Schedulers; /** @@ -476,9 +475,9 @@ protected void onResume() { private void restorePreferences() { Log.d("restoring buses", "Attempting to restore buses."); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); - buses = new HashSet(sp.getStringSet(BUS_SELECT_STATE, + buses = new HashSet<>(sp.getStringSet(BUS_SELECT_STATE, Collections.synchronizedSet( - new HashSet(getResources().getInteger(R.integer.max_checked))))); + new HashSet<>(getResources().getInteger(R.integer.max_checked))))); } protected void onPause() { @@ -621,21 +620,13 @@ private boolean isLight(int color) { return 1.0 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255 < .5; } }, - new Action1() { - @Override - public void call(Throwable throwable) { - if (throwable.getMessage() != null) { - Log.e("bus_icon_error", throwable.getMessage()); - } - Log.e("bus_icon_error", Log.getStackTraceString(throwable)); + throwable -> { + if (throwable.getMessage() != null) { + Log.e("bus_icon_error", throwable.getMessage()); } + Log.e("bus_icon_error", Log.getStackTraceString(throwable)); }, - new Action0() { - @Override - public void call() { - clearAndAddToMap(); - } - } + this::clearAndAddToMap ); } @@ -813,33 +804,26 @@ public void restoreBuses() { */ private void createListeners() { if(mMapCameraListener == null) { - mMapCameraListener = new GoogleMap.OnCameraChangeListener() { - - @Override - public void onCameraChange(CameraPosition cameraPosition) { - if (!inSavedState) - inSavedState = true; - latitude = cameraPosition.target.latitude; - longitude = cameraPosition.target.longitude; - if (zoom != cameraPosition.zoom) { - zoom = cameraPosition.zoom; - transitStop.checkAllVisibility(zoom, Float.parseFloat(getString(R.string.zoom_level))); - } + mMapCameraListener = cameraPosition -> { + if (!inSavedState) + inSavedState = true; + latitude = cameraPosition.target.latitude; + longitude = cameraPosition.target.longitude; + if (zoom != cameraPosition.zoom) { + zoom = cameraPosition.zoom; + transitStop.checkAllVisibility(zoom, Float.parseFloat(getString(R.string.zoom_level))); } }; } if(mMapMarkerClickListener == null) { - mMapMarkerClickListener = new GoogleMap.OnMarkerClickListener() { - @Override - public boolean onMarkerClick(Marker marker) { - - if (marker != null) { - mMap.animateCamera(CameraUpdateFactory.newLatLng(marker.getPosition()), 400, null); - new RequestPredictions(getApplicationContext(), marker, buses).execute(marker.getTitle()); - return true; - } - return false; + mMapMarkerClickListener = marker -> { + + if (marker != null) { + mMap.animateCamera(CameraUpdateFactory.newLatLng(marker.getPosition()), 400, null); + new RequestPredictions(getApplicationContext(), marker, buses).execute(marker.getTitle()); + return true; } + return false; }; } } @@ -885,7 +869,7 @@ protected void restorePolylines() { /** * Stops the bus refresh, then adds buses to the map */ - private synchronized void clearAndAddToMap() { + private void clearAndAddToMap() { if (mMap != null) { Log.d("stop_add_buses", buses.toString()); stopTimer(); @@ -909,28 +893,24 @@ private void showToast(String string, int length) { * adds buses to map. or else the map will be clear... */ - private synchronized void addBuses() { + private void addBuses() { Log.d("adding buses", buses.toString()); - vehicleSubscription = Observable.timer(0, 10, TimeUnit.SECONDS) - .flatMap(new Func1>() { - @Override - public Observable call(Long aLong) { - return patApiClient.getVehicles(collectionToString(buses), BuildConfig.PAT_API_KEY); - } - }).subscribeOn(Schedulers.io()) + Observable le = Observable.interval(0, 10, TimeUnit.SECONDS) + .flatMap(aLong -> patApiClient.getVehicles(collectionToString(buses), BuildConfig.PAT_API_KEY)) + .subscribeOn(Schedulers.io()); + Observable poop = le.map(VehicleResponse::getBustimeResponse); + Observable veh = poop.flatMap(bustimeVehicleResponse -> Observable.from(bustimeVehicleResponse.getVehicle())); + + vehicleSubscription = veh + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new Observer() { - + .subscribe(new Observer() { - /** - * Shows whether to displays or not (will only show errors once on the timer) - * @since 47 - */ private boolean showedErrors = false; @Override public void onCompleted() { - Log.d("buses_vehicle", "completed!"); + } @Override @@ -940,9 +920,7 @@ public void onError(Throwable e) { if(e instanceof RetrofitError) { printRetrofitError((RetrofitError)e); } -// makeSnackbar(e.getLocalizedMessage(), Snackbar.LENGTH_LONG); Log.e("bus_vehicle_error", e.getMessage()); -// Toast.makeText(appcontext, e.getMessage(), Toast.LENGTH_LONG).show(); } Log.e("bus_vehicle_error", e.getClass().getName()); @@ -950,52 +928,11 @@ public void onError(Throwable e) { } @Override - public void onNext(VehicleResponse vehicleResponse) { - if (vehicleResponse != null) { - BustimeVehicleResponse response = vehicleResponse.getBustimeResponse(); - List vehicles = response.getVehicle(); - List errors = response.getError(); - //update buses - Log.d("bus_vehicle_err_pres", Boolean.toString(showedErrors)); - if(!errors.isEmpty() && !showedErrors) { - showedErrors = true; - for (Error error : errors) { -// makeSnackbar(printErrors(error).toString(), Snackbar.LENGTH_LONG); - showToast(printErrors(error).toString(), Toast.LENGTH_LONG); - } - } - if (vehicles.size() > 0) { - onHandleVehicles(vehicles); - } else if (vehicles.size() == 0) { - removeBuses(); - stopTimer(); - } - } + public void onNext(Vehicle vehicle) { + addOrUpdateMarkers(vehicle); } -/** - * Handle vehicle updates... either... - *
    - *
  • add marker if not on {@link SelectTransit#busMarkers}
  • - *
  • update marker if in {@link SelectTransit#busMarkers}
  • - *
  • remove marker if in @{@link SelectTransit#busMarkers} but no longer on map
  • - *
- * - * @since 46 - * @param vehicles - list of vehicles from API - */ - - - private void onHandleVehicles(List vehicles) { - // Delete markers in here -// Set routesOnMap = new HashSet(busMarkers.keySet()); - for(Vehicle vehicle : vehicles) { - addOrUpdateMarkers(vehicle); - } -// removeBuses(routesOnMap); - } - -/** + /** * Handle vehicle updates and adds... *
    *
  • add marker if not on {@link SelectTransit#busMarkers} - {@link #addMarker(Vehicle)}
  • @@ -1017,7 +954,7 @@ private void addOrUpdateMarkers(Vehicle vehicle) { } -/** + /** * adds marker not in {@link SelectTransit#busMarkers} * @since 46 * @param vehicle - the vehicle to add @@ -1037,7 +974,7 @@ private void addMarker(Vehicle vehicle) { )); } -/** + /** * Updates marker information on map * @since 46 * @param vehicle - vehicle to update @@ -1050,53 +987,8 @@ private void updateMarker(Vehicle vehicle, Marker marker) { marker.setPosition(new LatLng(vehicle.getLat(), vehicle.getLon())); marker.setRotation(vehicle.getHdg()); } -/** - * Prints the errors - * - * @since 46 - * @param error - the Error Object - * @return a stringBuilder to show errors - */ - - private StringBuilder printErrors(Error error) { - if(error != null) { - StringBuilder st = new StringBuilder(); - if(error.getRt() != null) { - addMsg(st, getString(R.string.no_vehicle_error)); - st.append(" "); - addMsg(st, error.getRt()); -// buses.remove(error.getRt().trim()); TODO: activate this later when we figure out how to handle no route -> next day there's a route - } else if(error.getMsg().contains("specified") && error.getMsg().contains("rt")) { - addMsg(st, getString(R.string.cleared)); - } - else - addMsg(st, error.getMsg()); - Log.d("vehicle_error", st.toString()); - return st; - } - return null; - - } - -/** - * Creates appends to a StringBuilder - * - * @since 46 - * @param st - stringbuilder to make - * @param s - strings to add - */ - - private void addMsg(StringBuilder st, String... s) { - //TODO: maybe do this in another thread - if(s != null) { - for(String i : s) { - if(i != null && i.trim().length() > 0) - st.append(i); - else break; - } - } - } }); + } /** @@ -1250,7 +1142,6 @@ private void requestLocation() { Log.d("location_changed", "current location is null"); LocationServices.FusedLocationApi.requestLocationUpdates(googleAPIClient, gLocationRequest, this); if (currentLocation != null) { - // LocationServices.FusedLocationApi.removeLocationUpdates(googleAPIClient, this); if (mMap != null && isInPittsburgh(currentLocation)) mMap.moveCamera(CameraUpdateFactory.newLatLngZoom( new LatLng(currentLocation.getLatitude(), currentLocation.getLongitude()), 15.0f)); @@ -1284,14 +1175,9 @@ private void requestLocationPermissions() { if(!ActivityCompat.shouldShowRequestPermissionRationale(SelectTransit.this, Manifest.permission.ACCESS_FINE_LOCATION)) { showOkDialog(getString(R.string.location_permission_message), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - ActivityCompat.requestPermissions(SelectTransit.this, - new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, - LOCATION_REQUEST_CODE); - } - }); + (dialog, which) -> ActivityCompat.requestPermissions(SelectTransit.this, + new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, + LOCATION_REQUEST_CODE)); } ActivityCompat.requestPermissions(SelectTransit.this, new String[]{Manifest.permission_group.LOCATION}, diff --git a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/handlers/InputSave.java b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/handlers/InputSave.java index 12949df7..e8bf0689 100644 --- a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/handlers/InputSave.java +++ b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/handlers/InputSave.java @@ -12,7 +12,7 @@ import java.net.URL; import java.net.URLConnection; -import rectangledbmi.com.pittsburghrealtimetracker.hidden.PortAuthorityAPI; +import rectangledbmi.com.pittsburghrealtimetracker.patapi.PortAuthorityAPI; /** * Inspired by http://stackoverflow.com/questions/8986376/how-to-download-xml-file-from-server-and-save-it-in-sd-card diff --git a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/handlers/RequestPredictions.java b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/handlers/RequestPredictions.java index 95312ad6..2d9993b7 100644 --- a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/handlers/RequestPredictions.java +++ b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/handlers/RequestPredictions.java @@ -21,7 +21,7 @@ import rectangledbmi.com.pittsburghrealtimetracker.R; import rectangledbmi.com.pittsburghrealtimetracker.handlers.containers.ETAContainer; -import rectangledbmi.com.pittsburghrealtimetracker.hidden.PortAuthorityAPI; +import rectangledbmi.com.pittsburghrealtimetracker.patapi.PortAuthorityAPI; import rectangledbmi.com.pittsburghrealtimetracker.world.Prediction; /** diff --git a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/patapi/PortAuthorityAPI.java b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/patapi/PortAuthorityAPI.java new file mode 100644 index 00000000..18ec44b7 --- /dev/null +++ b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/patapi/PortAuthorityAPI.java @@ -0,0 +1,149 @@ +package rectangledbmi.com.pittsburghrealtimetracker.patapi; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Set; + +import rectangledbmi.com.pittsburghrealtimetracker.BuildConfig; + +/** + * This is our first way of using the Port Authority API before we actually started using RetroFit. + * This has a lot of static methods to get URLs of the Port Authority API. Eventually, in the future, + * we will only be using RetroFit and will stop using this. + * @author Jeremy Jao + */ +public class PortAuthorityAPI { + + private static final String API_KEY = BuildConfig.PAT_API_KEY; + + private static final String MAIN_LINK = "http://truetime.portauthority.org/bustime/api/v2/"; + private static final String keyEquals = "?key="; + private static final String routeEquals = "&rt="; + private static final String stopEquals = "&stpid="; + private static final String busEquals = "&vid="; + + /** + * Gets the patterns for a bus stop and more importantly the patterns using getpatterns + * + * @param route the selected route + * @return the URL for the bustops and routes + * @throws MalformedURLException + */ + public static URL getPatterns(String route) throws MalformedURLException { + return new URL( + MAIN_LINK + + "getpatterns" + + keyEquals + + API_KEY + + routeEquals + + route + ); + } + + /** + * Gets the vehicles (buses, trains) using getpatterns + * + * @param routes the routes used to + * @return the URL to get the buses + * @throws MalformedURLException + */ + public static URL getVehicles(String routes) throws MalformedURLException { + return new URL( + MAIN_LINK + + "getvehicles" + + keyEquals + + API_KEY + + routeEquals + + routes + ); + } + + /** + * Gets the vehicles (buses, trains) using getpatterns from a list of vehicles + * @param routes array of routes + * @return URL to get the buses + * @throws MalformedURLException + */ + public static URL getVehicles(String[] routes) throws MalformedURLException { + return getVehicles(arrayToString(routes)); + } + + /** + * @param data data to turn an array to a string + * @return a comma-delim string of data + */ + private static String arrayToString(String[] data) { + StringBuilder string = new StringBuilder(); + int oneLess = data.length-1; + for(int i=0;i selectedBuses) throws MalformedURLException { + return new URL( + MAIN_LINK + + "getpredictions" + + keyEquals + + API_KEY + + stopEquals + + Integer.toString(stpid) + + routeEquals + + arrayToString(selectedBuses.toArray(new String[selectedBuses.size()])) + ); + } + +} diff --git a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/world/jsonpojo/BustimeVehicleResponse.java b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/world/jsonpojo/BustimeVehicleResponse.java index 57598f60..406fe993 100644 --- a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/world/jsonpojo/BustimeVehicleResponse.java +++ b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/world/jsonpojo/BustimeVehicleResponse.java @@ -4,6 +4,7 @@ import com.google.gson.annotations.Expose; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import javax.annotation.Generated; @@ -23,6 +24,8 @@ public class BustimeVehicleResponse { @Expose private List error = new ArrayList(); + private HashMap> processedErrors = new HashMap>(); + /** * * @return @@ -48,6 +51,23 @@ public void setVehicle(List vehicle) { */ public void setError(List error) { this.error = error; + processErrors(); + } + + private void processErrors() { + for(Error err : error) { + ArrayList listOfParams = processedErrors.get(err.getMsg()); + if(listOfParams == null) { + listOfParams = new ArrayList(); + processedErrors.put(err.getMsg(), listOfParams); + } + listOfParams.add(err.getRt()); + + } + } + + public HashMap> getProcessedErrors() { + return processedErrors; } /** @@ -59,4 +79,6 @@ public List getError() { return error; } + + } \ No newline at end of file From e0d4133a7b05dd0a0851b6755fbc9a7caee5f97b Mon Sep 17 00:00:00 2001 From: Jeremy Jao Date: Sat, 7 Nov 2015 18:26:00 -0500 Subject: [PATCH 2/6] Change references to observables and strip out some unneeded imports. --- .../SelectTransit.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/SelectTransit.java b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/SelectTransit.java index 4301cca2..2b909dea 100644 --- a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/SelectTransit.java +++ b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/SelectTransit.java @@ -50,14 +50,15 @@ import com.squareup.leakcanary.LeakCanary; import java.io.File; -import java.util.ArrayList; +import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.HashSet; import java.util.List; -import java.util.Map; +import java.util.Locale; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -78,7 +79,6 @@ import retrofit.converter.GsonConverter; import rx.Observable; import rx.Observer; -import rx.Subscriber; import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; import rx.functions.Action1; @@ -895,13 +895,15 @@ private void showToast(String string, int length) { private void addBuses() { Log.d("adding buses", buses.toString()); - Observable le = Observable.interval(0, 10, TimeUnit.SECONDS) + Observable bustimeVehicleResponseObservable = Observable.interval(0, 10, TimeUnit.SECONDS) .flatMap(aLong -> patApiClient.getVehicles(collectionToString(buses), BuildConfig.PAT_API_KEY)) + .subscribeOn(Schedulers.io()) + .map(VehicleResponse::getBustimeResponse) .subscribeOn(Schedulers.io()); - Observable poop = le.map(VehicleResponse::getBustimeResponse); - Observable veh = poop.flatMap(bustimeVehicleResponse -> Observable.from(bustimeVehicleResponse.getVehicle())); - vehicleSubscription = veh + Observable vehicleObservable = bustimeVehicleResponseObservable.flatMap(bustimeVehicleResponse -> Observable.from(bustimeVehicleResponse.getVehicle())); + + vehicleSubscription = vehicleObservable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer() { @@ -910,7 +912,9 @@ private void addBuses() { @Override public void onCompleted() { - + SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ", Locale.ENGLISH); + String cDateTime = dateFormat.format(new Date()); + Log.d("bus_vehicle update", "Bus map updates finished updates at " + cDateTime); } @Override @@ -923,7 +927,6 @@ public void onError(Throwable e) { Log.e("bus_vehicle_error", e.getMessage()); } Log.e("bus_vehicle_error", e.getClass().getName()); - Log.e("bus_vehicle_error", Log.getStackTraceString(e)); } From ecfd28da22abb15224b83f9353cdb2d88145e9e0 Mon Sep 17 00:00:00 2001 From: Jeremy Jao Date: Sun, 8 Nov 2015 20:16:25 -0500 Subject: [PATCH 3/6] Add naturalordercomparator from @paour for error messages and handle transient messages --- .../natorder/NaturalOrderComparator.java | 184 ++++++++++++ .../SelectTransit.java | 263 ++++++++++++------ .../retrofit/{ => patapi}/PATAPI.java | 4 +- .../patapi/errorcontainers/ErrorMessage.java | 75 +++++ .../patapi/errorcontainers/PATAPIErrors.java | 31 +++ .../jsonpojo/BustimeVehicleResponse.java | 16 +- app/src/main/res/values/strings.xml | 2 +- 7 files changed, 475 insertions(+), 100 deletions(-) create mode 100644 app/src/main/java/io/paour/github/natorder/NaturalOrderComparator.java rename app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/retrofit/{ => patapi}/PATAPI.java (92%) create mode 100644 app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/retrofit/patapi/errorcontainers/ErrorMessage.java create mode 100644 app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/retrofit/patapi/errorcontainers/PATAPIErrors.java diff --git a/app/src/main/java/io/paour/github/natorder/NaturalOrderComparator.java b/app/src/main/java/io/paour/github/natorder/NaturalOrderComparator.java new file mode 100644 index 00000000..9e51032f --- /dev/null +++ b/app/src/main/java/io/paour/github/natorder/NaturalOrderComparator.java @@ -0,0 +1,184 @@ +package io.paour.github.natorder; // epicstar - changed to bring natural order comparator to app... + +/* + NaturalOrderComparator.java -- Perform 'natural order' comparisons of strings in Java. + Copyright (C) 2003 by Pierre-Luc Paour + Based on the C version by Martin Pool, of which this is more or less a straight conversion. + Copyright (C) 2000 by Martin Pool + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + */ + +import java.util.*; + +public class NaturalOrderComparator implements Comparator // epicstar: change to abide by Generic Rules +{ + int compareRight(String a, String b) + { + int bias = 0; + int ia = 0; + int ib = 0; + + // The longest run of digits wins. That aside, the greatest + // value wins, but we can't know that it will until we've scanned + // both numbers to know that they have the same magnitude, so we + // remember it in BIAS. + for (;; ia++, ib++) + { + char ca = charAt(a, ia); + char cb = charAt(b, ib); + + if (!Character.isDigit(ca) && !Character.isDigit(cb)) + { + return bias; + } + else if (!Character.isDigit(ca)) + { + return -1; + } + else if (!Character.isDigit(cb)) + { + return +1; + } + else if (ca < cb) + { + if (bias == 0) + { + bias = -1; + } + } + else if (ca > cb) + { + if (bias == 0) + bias = +1; + } + else if (ca == 0 && cb == 0) + { + return bias; + } + } + } + + public int compare(T o1, T o2) + { + String a = o1.toString(); + String b = o2.toString(); + + int ia = 0, ib = 0; + int nza = 0, nzb = 0; + char ca, cb; + int result; + + while (true) + { + // only count the number of zeroes leading the last number compared + nza = nzb = 0; + + ca = charAt(a, ia); + cb = charAt(b, ib); + + // skip over leading spaces or zeros + while (Character.isSpaceChar(ca) || ca == '0') + { + if (ca == '0') + { + nza++; + } + else + { + // only count consecutive zeroes + nza = 0; + } + + ca = charAt(a, ++ia); + } + + while (Character.isSpaceChar(cb) || cb == '0') + { + if (cb == '0') + { + nzb++; + } + else + { + // only count consecutive zeroes + nzb = 0; + } + + cb = charAt(b, ++ib); + } + + // process run of digits + if (Character.isDigit(ca) && Character.isDigit(cb)) + { + if ((result = compareRight(a.substring(ia), b.substring(ib))) != 0) + { + return result; + } + } + + if (ca == 0 && cb == 0) + { + // The strings compare the same. Perhaps the caller + // will want to call strcmp to break the tie. + return nza - nzb; + } + + if (ca < cb) + { + return -1; + } + else if (ca > cb) + { + return +1; + } + + ++ia; + ++ib; + } + } + + static char charAt(String s, int i) + { + if (i >= s.length()) + { + return 0; + } + else + { + return s.charAt(i); + } + } + + public static void main(String[] args) + { + String[] strings = new String[] { "1-2", "1-02", "1-20", "10-20", "fred", "jane", "pic01", + "pic2", "pic02", "pic02a", "pic3", "pic4", "pic 4 else", "pic 5", "pic05", "pic 5", + "pic 5 something", "pic 6", "pic 7", "pic100", "pic100a", "pic120", "pic121", + "pic02000", "tom", "x2-g8", "x2-y7", "x2-y08", "x8-y8" }; + + List orig = Arrays.asList(strings); + + System.out.println("Original: " + orig); + + List scrambled = Arrays.asList(strings); + Collections.shuffle(scrambled); + + System.out.println("Scrambled: " + scrambled); + + Collections.sort(scrambled, new NaturalOrderComparator()); + + System.out.println("Sorted: " + scrambled); + } +} \ No newline at end of file diff --git a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/SelectTransit.java b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/SelectTransit.java index 2b909dea..fd51a9fd 100644 --- a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/SelectTransit.java +++ b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/SelectTransit.java @@ -18,6 +18,7 @@ import android.net.http.HttpResponseCache; import android.os.Bundle; import android.preference.PreferenceManager; +import android.support.annotation.NonNull; import android.support.design.widget.Snackbar; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; @@ -51,11 +52,13 @@ import java.io.File; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -68,7 +71,8 @@ import rectangledbmi.com.pittsburghrealtimetracker.handlers.RequestLine; import rectangledbmi.com.pittsburghrealtimetracker.handlers.RequestPredictions; import rectangledbmi.com.pittsburghrealtimetracker.handlers.extend.ETAWindowAdapter; -import rectangledbmi.com.pittsburghrealtimetracker.retrofit.PATAPI; +import rectangledbmi.com.pittsburghrealtimetracker.retrofit.patapi.PATAPI; +import rectangledbmi.com.pittsburghrealtimetracker.retrofit.patapi.errorcontainers.ErrorMessage; import rectangledbmi.com.pittsburghrealtimetracker.world.Route; import rectangledbmi.com.pittsburghrealtimetracker.world.TransitStop; import rectangledbmi.com.pittsburghrealtimetracker.world.jsonpojo.BustimeVehicleResponse; @@ -204,6 +208,13 @@ public class SelectTransit extends AppCompatActivity implements */ private Subscription vehicleSubscription; + /** + * Subscription for broadcasting vehicle update errors + * + * @since 49 + */ + private Subscription vehicleErrorSubscription; + /** * Bus icons created from drawable/bus_icon.png * @@ -211,12 +222,19 @@ public class SelectTransit extends AppCompatActivity implements */ private ConcurrentHashMap busIcons; + /** + * Subscription for + * + * @since 48 + */ private Subscription busIconGeneration; private GoogleMap.OnCameraChangeListener mMapCameraListener; private GoogleMap.OnMarkerClickListener mMapMarkerClickListener; + private DrawerLayout mainLayout; + /** * Location Request Code @@ -234,6 +252,8 @@ protected void onCreate(Bundle savedInstanceState) { mNavigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager().findFragmentById(R.id.navigation_drawer); + mainLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + // Set up the drawer. mNavigationDrawerFragment.setUp( R.id.navigation_drawer, @@ -377,7 +397,7 @@ protected void restoreInstanceState(Bundle savedInstanceState) { defaultCameraLocation(); } Log.d("savedInstance_restore", "saved? " + inSavedState); - Log.d("savedInstance_restore", "lat="+latitude); + Log.d("savedInstance_restore", "lat=" + latitude); Log.d("savedInstance_restore", "long=" + longitude); if (transitStop == null) { transitStop = new TransitStop(); @@ -712,7 +732,7 @@ public void restoreActionBar() { try { setSupportActionBar(toolbar); } catch (Throwable e) { - Snackbar.make(findViewById(android.R.id.content), + Snackbar.make(mainLayout, "Material Design bugged out on your device. Please report this to the Play Store Email if this pops up.", Snackbar.LENGTH_LONG).show(); // Toast.makeText(this, "Material Design bugged out on your device. Please report this to the Play Store Email if this pops up.", Toast.LENGTH_LONG).show(); } @@ -721,7 +741,7 @@ public void restoreActionBar() { ActionBar t = getSupportActionBar(); if(t != null) t.setDisplayHomeAsUpEnabled(true); } catch(NullPointerException e) { - Snackbar.make(findViewById(android.R.id.content), + Snackbar.make(mainLayout, "Material Design bugged out on your device. Please report this to the Play Store Email if this pops up.", Snackbar.LENGTH_LONG).show(); } @@ -889,6 +909,139 @@ private void showToast(String string, int length) { Toast.makeText(this, string, length).show(); } + private Observer vehicleBusUpdate() { + return new Observer() { + + private boolean showedErrors = false; + + @Override + public void onCompleted() { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ", Locale.ENGLISH); + String cDateTime = dateFormat.format(new Date()); + Log.d("bus_vehicle update", "Bus map updates finished updates at " + cDateTime); + } + + @Override + public void onError(Throwable e) { + if (e.getMessage() != null && e.getLocalizedMessage() != null && !showedErrors) { + showedErrors = true; + if (e instanceof RetrofitError) { + printRetrofitError((RetrofitError) e); + } + Log.e("bus_vehicle_error", e.getMessage()); + } + Log.e("bus_vehicle_error", e.getClass().getName()); + Log.e("bus_vehicle_error", Log.getStackTraceString(e)); + } + + @Override + public void onNext(Vehicle vehicle) { + addOrUpdateMarkers(vehicle); + } + + /** + * Handle vehicle updates and adds... + *
      + *
    • add marker if not on {@link SelectTransit#busMarkers} - {@link #addMarker(Vehicle)}
    • + *
    • update marker if in {@link SelectTransit#busMarkers} - {@link #updateMarker(Vehicle, Marker)}
    • + *
    + * + * @since 46 + * @param vehicle - vehicle to be added + */ + + private void addOrUpdateMarkers(Vehicle vehicle) { + int vid = vehicle.getVid(); + Marker marker = busMarkers.get(vid); + if (marker == null) { + addMarker(vehicle); + } else { + updateMarker(vehicle, marker); + } + + } + + /** + * adds marker not in {@link SelectTransit#busMarkers} + * @since 46 + * @param vehicle - the vehicle to add + */ + + private void addMarker(Vehicle vehicle) { + Log.d("marker_add", "adding_marker " + Integer.toString(vehicle.getVid())); + Log.d("marker_add", busIcons.get(vehicle.getRt()).toString()); + busMarkers.put(vehicle.getVid(), mMap.addMarker(new MarkerOptions() + .position(new LatLng(vehicle.getLat(), vehicle.getLon())) + .title(vehicle.getRt() + "(" + vehicle.getVid() + ") " + vehicle.getDes() + (vehicle.isDly() ? " - Delayed" : "")) + .draggable(false) + .rotation(vehicle.getHdg()) + .icon(BitmapDescriptorFactory.fromBitmap(busIcons.get(vehicle.getRt()))) + .anchor((float) .5, (float) 0.5) + .flat(true) + )); + } + + /** + * Updates marker information on map + * @since 46 + * @param vehicle - vehicle to update + * @param marker - marker to update + */ + + private void updateMarker(Vehicle vehicle, Marker marker) { + Log.d("marker_update", "updating_pointer"); + marker.setTitle(vehicle.getRt() + "(" + vehicle.getVid() + ") " + vehicle.getDes() + (vehicle.isDly() ? " - Delayed" : "")); + marker.setPosition(new LatLng(vehicle.getLat(), vehicle.getLon())); + marker.setRotation(vehicle.getHdg()); + } + }; + } + + private Observer vehicleErrorObserver() { + Log.d("vehicle_error_observer", "restarting the vehicle error observer"); + return new Observer() { + + private String transformMessage(ErrorMessage errorMessage) { + if(errorMessage != null) { + if (errorMessage.getParameters() != null) { + return getString(R.string.no_vehicle_error); + + } else if (errorMessage.getMessage().contains("specified") && errorMessage.getMessage().contains("rt")) { + return getString(R.string.cleared); + } else + return errorMessage.getMessage(); + } + return null; + + } + + @Override + public void onCompleted() { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ", Locale.ENGLISH); + String cDateTime = dateFormat.format(new Date()); + Log.d("vehicle_error_complete", "Bus map updates finished updates at " + cDateTime); + + } + + @Override + public void onError(Throwable e) { + if(e.getLocalizedMessage() != null) + Log.e("vehicle_error_errs", e.getLocalizedMessage()); + Log.e("vehicle_error_errs", Log.getStackTraceString(e)); + } + + @Override + public void onNext(ErrorMessage errorMessage) { + if(errorMessage != null && errorMessage.getMessage() != null) { + showToast(transformMessage(errorMessage) + + (errorMessage.getParameters() != null ? ": " + errorMessage.getParameters() : ""), + Toast.LENGTH_LONG); + } + + } + }; + } + /** * adds buses to map. or else the map will be clear... */ @@ -901,96 +1054,24 @@ private void addBuses() { .map(VehicleResponse::getBustimeResponse) .subscribeOn(Schedulers.io()); - Observable vehicleObservable = bustimeVehicleResponseObservable.flatMap(bustimeVehicleResponse -> Observable.from(bustimeVehicleResponse.getVehicle())); + Observable vehicleObservable = bustimeVehicleResponseObservable.flatMap( + bustimeVehicleResponse -> Observable.from(bustimeVehicleResponse.getVehicle())); vehicleSubscription = vehicleObservable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new Observer() { + .subscribe(vehicleBusUpdate()); - private boolean showedErrors = false; + Observable>> vehicleErrorObservable = bustimeVehicleResponseObservable + .map(BustimeVehicleResponse::getProcessedErrors); - @Override - public void onCompleted() { - SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ", Locale.ENGLISH); - String cDateTime = dateFormat.format(new Date()); - Log.d("bus_vehicle update", "Bus map updates finished updates at " + cDateTime); - } - - @Override - public void onError(Throwable e) { - if(e.getMessage() != null && e.getLocalizedMessage() != null && !showedErrors) { - showedErrors = true; - if(e instanceof RetrofitError) { - printRetrofitError((RetrofitError)e); - } - Log.e("bus_vehicle_error", e.getMessage()); - } - Log.e("bus_vehicle_error", e.getClass().getName()); - Log.e("bus_vehicle_error", Log.getStackTraceString(e)); - } - - @Override - public void onNext(Vehicle vehicle) { - addOrUpdateMarkers(vehicle); - } - - /** - * Handle vehicle updates and adds... - *
      - *
    • add marker if not on {@link SelectTransit#busMarkers} - {@link #addMarker(Vehicle)}
    • - *
    • update marker if in {@link SelectTransit#busMarkers} - {@link #updateMarker(Vehicle, Marker)}
    • - *
    - * - * @since 46 - * @param vehicle - vehicle to be added - */ - - private void addOrUpdateMarkers(Vehicle vehicle) { - int vid = vehicle.getVid(); - Marker marker = busMarkers.get(vid); - if(marker == null) { - addMarker(vehicle); - } else { - updateMarker(vehicle, marker); - } - - } - - /** - * adds marker not in {@link SelectTransit#busMarkers} - * @since 46 - * @param vehicle - the vehicle to add - */ - - private void addMarker(Vehicle vehicle) { - Log.d("marker_add", "adding_marker " + Integer.toString(vehicle.getVid())); - Log.d("marker_add", busIcons.get(vehicle.getRt()).toString()); - busMarkers.put(vehicle.getVid(), mMap.addMarker(new MarkerOptions() - .position(new LatLng(vehicle.getLat(), vehicle.getLon())) - .title(vehicle.getRt() + "(" + vehicle.getVid() + ") " + vehicle.getDes() + (vehicle.isDly() ? " - Delayed" : "")) - .draggable(false) - .rotation(vehicle.getHdg()) - .icon(BitmapDescriptorFactory.fromBitmap(busIcons.get(vehicle.getRt()))) - .anchor((float) .5, (float) 0.5) - .flat(true) - )); - } - - /** - * Updates marker information on map - * @since 46 - * @param vehicle - vehicle to update - * @param marker - marker to update - */ - - private void updateMarker(Vehicle vehicle, Marker marker) { - Log.d("marker_update", "updating_pointer"); - marker.setTitle(vehicle.getRt() + "(" + vehicle.getVid() + ") " + vehicle.getDes() + (vehicle.isDly() ? " - Delayed" : "")); - marker.setPosition(new LatLng(vehicle.getLat(), vehicle.getLon())); - marker.setRotation(vehicle.getHdg()); - } - }); + vehicleErrorSubscription = vehicleErrorObservable + .distinctUntilChanged() + .flatMap(errorMap -> Observable.from(errorMap.entrySet())) + .map(processedError -> new ErrorMessage(processedError.getKey(), processedError.getValue())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(vehicleErrorObserver()); } @@ -1058,7 +1139,7 @@ private String collectionToString(Collection data) { private void makeSnackbar(String string, int showLength) { if(string != null && string.length() > 0) { - Snackbar.make(findViewById(android.R.id.content), + Snackbar.make(mainLayout, string, showLength ).show(); } @@ -1071,6 +1152,8 @@ private void makeSnackbar(String string, int showLength) { private void stopTimer() { if(vehicleSubscription != null) vehicleSubscription.unsubscribe(); + if(vehicleErrorSubscription != null) + vehicleErrorSubscription.unsubscribe(); } /** @@ -1191,7 +1274,7 @@ private void requestLocationPermissions() { } @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == LOCATION_REQUEST_CODE) { if (permissions.length == 1 && permissions[0].equals(Manifest.permission.ACCESS_FINE_LOCATION) && diff --git a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/retrofit/PATAPI.java b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/retrofit/patapi/PATAPI.java similarity index 92% rename from app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/retrofit/PATAPI.java rename to app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/retrofit/patapi/PATAPI.java index e2b47083..a99ce6da 100644 --- a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/retrofit/PATAPI.java +++ b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/retrofit/patapi/PATAPI.java @@ -1,6 +1,5 @@ -package rectangledbmi.com.pittsburghrealtimetracker.retrofit; +package rectangledbmi.com.pittsburghrealtimetracker.retrofit.patapi; -import rectangledbmi.com.pittsburghrealtimetracker.BuildConfig; import rectangledbmi.com.pittsburghrealtimetracker.world.jsonpojo.PatternResponse; import rectangledbmi.com.pittsburghrealtimetracker.world.jsonpojo.PredictionResponse; import rectangledbmi.com.pittsburghrealtimetracker.world.jsonpojo.VehicleResponse; @@ -17,7 +16,6 @@ * @since 46 */ public interface PATAPI { - String key = BuildConfig.PAT_API_KEY; /** * generates a response for patters * @param rt - the route diff --git a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/retrofit/patapi/errorcontainers/ErrorMessage.java b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/retrofit/patapi/errorcontainers/ErrorMessage.java new file mode 100644 index 00000000..599f7c99 --- /dev/null +++ b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/retrofit/patapi/errorcontainers/ErrorMessage.java @@ -0,0 +1,75 @@ +package rectangledbmi.com.pittsburghrealtimetracker.retrofit.patapi.errorcontainers; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import io.paour.github.natorder.NaturalOrderComparator; + +/** + * This is a processed error message object consisting of a message and its parameters. Since Port + * Authority refuses to make errors work as expected, + * + * @author Jeremy Jao + * @since 49 + */ +public class ErrorMessage { + + /** + * The message of the error. + */ + private String message; + + /** + * The combined parameters for the message. + */ + private String parameters; + + + /** + * Creates a processed error message. + * @param message the message for the error + * @param parameterList the parameters that make the error. + */ + public ErrorMessage(String message, List parameterList) { + this.message = message; + this.parameters = commaDelimitParams(parameterList); + } + + /** + * Gets the error message. + * @return the error message + */ + public String getMessage() { + return message; + } + + /** + * Gets the parameters in String form. + * @return a list of parameters for the message. + */ + public String getParameters() { + return parameters; + } + + /** + * Makes a comma-delimited String from a list. + * @param parameters The iterable parameters. + * @return the parameters in string form. + */ + private String commaDelimitParams(List parameters) { + if(parameters == null || parameters.isEmpty()) return null; + parameters = new ArrayList<>(parameters); + Collections.sort(parameters, new NaturalOrderComparator<>()); + StringBuilder paramBuffer = new StringBuilder(); + boolean isFirst = true; + for (String param : parameters) { + if(isFirst) { + isFirst = false; + } else + paramBuffer.append(", "); + paramBuffer.append(param); + } + return paramBuffer.toString(); + } +} diff --git a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/retrofit/patapi/errorcontainers/PATAPIErrors.java b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/retrofit/patapi/errorcontainers/PATAPIErrors.java new file mode 100644 index 00000000..7954d36a --- /dev/null +++ b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/retrofit/patapi/errorcontainers/PATAPIErrors.java @@ -0,0 +1,31 @@ +package rectangledbmi.com.pittsburghrealtimetracker.retrofit.patapi.errorcontainers; + +import java.util.ArrayList; +import java.util.HashMap; + +import rx.Observable; + +/** + * This is a base class for handling all errors in the Port Authority API. + * + * @author Jeremy Jao + * @since 49 + */ +public class PATAPIErrors extends Observable { + + protected HashMap> errors; + + /** + * Creates an Observable with a Function to execute when it is subscribed to. + *

    + * Note: Use {@link #create(OnSubscribe)} to create an Observable, instead of this constructor, + * unless you specifically have a need for inheritance. + * + * @param f {@link OnSubscribe} to be executed when a Subscriber is called is called + */ + protected PATAPIErrors(OnSubscribe f) { + super(f); + } + + +} diff --git a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/world/jsonpojo/BustimeVehicleResponse.java b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/world/jsonpojo/BustimeVehicleResponse.java index 406fe993..02d9cf15 100644 --- a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/world/jsonpojo/BustimeVehicleResponse.java +++ b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/world/jsonpojo/BustimeVehicleResponse.java @@ -1,6 +1,8 @@ package rectangledbmi.com.pittsburghrealtimetracker.world.jsonpojo; +import android.util.Log; + import com.google.gson.annotations.Expose; import java.util.ArrayList; @@ -24,8 +26,6 @@ public class BustimeVehicleResponse { @Expose private List error = new ArrayList(); - private HashMap> processedErrors = new HashMap>(); - /** * * @return @@ -51,10 +51,13 @@ public void setVehicle(List vehicle) { */ public void setError(List error) { this.error = error; - processErrors(); + Log.d("vehicle_error", "set errors...."); + } - private void processErrors() { + private HashMap> processErrors() { + HashMap> processedErrors = + new HashMap>(error.size()); for(Error err : error) { ArrayList listOfParams = processedErrors.get(err.getMsg()); if(listOfParams == null) { @@ -62,12 +65,13 @@ private void processErrors() { processedErrors.put(err.getMsg(), listOfParams); } listOfParams.add(err.getRt()); - } + return processedErrors; } public HashMap> getProcessedErrors() { - return processedErrors; + Log.d("vehicle_error", getError().toString()); + return processErrors(); } /** diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fe0cdfda..55ca422d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -327,7 +327,7 @@ Title of Bus - Port Authority currently not tracking route + Port Authority is currently not tracking Please check your data connection. Cannot translate information from Port Authority. The Port Authority website seems to be down. From 30b299a281154e14f6eb7e52c9a869f9d0454275 Mon Sep 17 00:00:00 2001 From: Jeremy Jao Date: Sun, 8 Nov 2015 22:25:26 -0500 Subject: [PATCH 4/6] refactor error checking to work in threads and add documentation --- .../SelectTransit.java | 118 ++++++++++++------ .../jsonpojo/BustimeVehicleResponse.java | 10 +- 2 files changed, 87 insertions(+), 41 deletions(-) diff --git a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/SelectTransit.java b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/SelectTransit.java index fd51a9fd..fb096765 100644 --- a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/SelectTransit.java +++ b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/SelectTransit.java @@ -51,6 +51,7 @@ import com.squareup.leakcanary.LeakCanary; import java.io.File; +import java.lang.reflect.Array; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; @@ -62,6 +63,7 @@ import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -86,6 +88,7 @@ import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; import rx.functions.Action1; +import rx.functions.Func1; import rx.schedulers.Schedulers; /** @@ -211,7 +214,7 @@ public class SelectTransit extends AppCompatActivity implements /** * Subscription for broadcasting vehicle update errors * - * @since 49 + * @since 55 */ private Subscription vehicleErrorSubscription; @@ -223,16 +226,31 @@ public class SelectTransit extends AppCompatActivity implements private ConcurrentHashMap busIcons; /** - * Subscription for + * Subscription for bus icon generation * * @since 48 */ private Subscription busIconGeneration; + /** + * Camera listener reference for Google Maps + * + * @since 54 + */ private GoogleMap.OnCameraChangeListener mMapCameraListener; + /** + * Click listener reference for Google Maps + * + * @since 54 + */ private GoogleMap.OnMarkerClickListener mMapMarkerClickListener; + /** + * Reference to the main activity's Layout. + * + * @since 55 + */ private DrawerLayout mainLayout; @@ -473,20 +491,9 @@ protected void onStart() { @Override protected void onResume() { super.onResume(); -// if(!isNetworkAvailable()) { -// DataRequiredDialog dialog = new DataRequiredDialog(); -// dialog.show(getSupportFragmentManager(), "data required"); -// } -// if (mMap != null) { -// setUpMap(); -// } else -//// setUpMapIfNeeded(); -// if(buses != null) { if(mMap != null) { restoreBuses(); } - -// } } /** @@ -852,10 +859,6 @@ private void createListeners() { * set the map listeners */ private void setMapListeners() { - // Do a null check to confirm that we have not already instantiated the map. -// if (mMap == null) { -//// mMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap(); -// // Check if we were successful in obtaining the map. if (mMap != null) { createListeners(); mMap.setInfoWindowAdapter(new ETAWindowAdapter(getLayoutInflater())); @@ -909,6 +912,10 @@ private void showToast(String string, int length) { Toast.makeText(this, string, length).show(); } + /** + * This creates an observer to either update or add the buses to the map. + * @return the vehicle update observer + */ private Observer vehicleBusUpdate() { return new Observer() { @@ -966,7 +973,6 @@ private void addOrUpdateMarkers(Vehicle vehicle) { * @since 46 * @param vehicle - the vehicle to add */ - private void addMarker(Vehicle vehicle) { Log.d("marker_add", "adding_marker " + Integer.toString(vehicle.getVid())); Log.d("marker_add", busIcons.get(vehicle.getRt()).toString()); @@ -987,7 +993,6 @@ private void addMarker(Vehicle vehicle) { * @param vehicle - vehicle to update * @param marker - marker to update */ - private void updateMarker(Vehicle vehicle, Marker marker) { Log.d("marker_update", "updating_pointer"); marker.setTitle(vehicle.getRt() + "(" + vehicle.getVid() + ") " + vehicle.getDes() + (vehicle.isDly() ? " - Delayed" : "")); @@ -997,29 +1002,20 @@ private void updateMarker(Vehicle vehicle, Marker marker) { }; } + /** + * This is the vehicle update/add Port Authority API observer that will print each processed error + * into a Toast. + * @return the vehicle update observer + */ private Observer vehicleErrorObserver() { Log.d("vehicle_error_observer", "restarting the vehicle error observer"); return new Observer() { - private String transformMessage(ErrorMessage errorMessage) { - if(errorMessage != null) { - if (errorMessage.getParameters() != null) { - return getString(R.string.no_vehicle_error); - - } else if (errorMessage.getMessage().contains("specified") && errorMessage.getMessage().contains("rt")) { - return getString(R.string.cleared); - } else - return errorMessage.getMessage(); - } - return null; - - } - @Override public void onCompleted() { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ", Locale.ENGLISH); String cDateTime = dateFormat.format(new Date()); - Log.d("vehicle_error_complete", "Bus map updates finished updates at " + cDateTime); + Log.d("vehicle_error_complete", "Bus map error updates finished updates at " + cDateTime); } @@ -1033,7 +1029,7 @@ public void onError(Throwable e) { @Override public void onNext(ErrorMessage errorMessage) { if(errorMessage != null && errorMessage.getMessage() != null) { - showToast(transformMessage(errorMessage) + + showToast(errorMessage.getMessage() + (errorMessage.getParameters() != null ? ": " + errorMessage.getParameters() : ""), Toast.LENGTH_LONG); } @@ -1043,32 +1039,72 @@ public void onNext(ErrorMessage errorMessage) { } /** - * adds buses to map. or else the map will be clear... + * Creates an anonymous class to make messages more human-readable. + * @return a closure to create a human-readable {@link ErrorMessage} */ + private Func1>, ErrorMessage> transformSingleMessage() { + return new Func1>, ErrorMessage>() { + + /** + * Transforms the original message to a user-readable message. + * @param originalMessage The original Port Authority API message + * @return a user-readable message from the original API message + */ + private String transformMessage(String originalMessage) { + if(originalMessage != null) { + if (originalMessage.contains("No data found for parameter")) { + return getString(R.string.no_vehicle_error); + } else if (originalMessage.contains("specified") && originalMessage.contains("rt")) { + return getString(R.string.cleared); + } else + return originalMessage; + } + return null; + } + + @Override + public ErrorMessage call(Map.Entry> processedMessage) { + return new ErrorMessage(transformMessage(processedMessage.getKey()), processedMessage.getValue()); + } + }; + } + + /** + * adds buses to map... or else the map will be clear. + * + * It updates the map every 10 seconds and makes sure that on the other threads, it will + * process the Port Authority API call to Reactive way. + */ private void addBuses() { Log.d("adding buses", buses.toString()); + + // get the JSON object and put it in an observable Observable bustimeVehicleResponseObservable = Observable.interval(0, 10, TimeUnit.SECONDS) .flatMap(aLong -> patApiClient.getVehicles(collectionToString(buses), BuildConfig.PAT_API_KEY)) .subscribeOn(Schedulers.io()) .map(VehicleResponse::getBustimeResponse) .subscribeOn(Schedulers.io()); + // get the vehicles observer Observable vehicleObservable = bustimeVehicleResponseObservable.flatMap( bustimeVehicleResponse -> Observable.from(bustimeVehicleResponse.getVehicle())); + // run the vehicle updater vehicleSubscription = vehicleObservable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(vehicleBusUpdate()); + // get the vehicle errors observer Observable>> vehicleErrorObservable = bustimeVehicleResponseObservable .map(BustimeVehicleResponse::getProcessedErrors); + // run the vehicle update observer and print it when the error message object changes vehicleErrorSubscription = vehicleErrorObservable .distinctUntilChanged() .flatMap(errorMap -> Observable.from(errorMap.entrySet())) - .map(processedError -> new ErrorMessage(processedError.getKey(), processedError.getValue())) + .map(transformSingleMessage()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(vehicleErrorObserver()); @@ -1206,9 +1242,11 @@ private void showOkDialog(String message, DialogInterface.OnClickListener okList * @return whether or not your device is in Pittsburgh */ private boolean isInPittsburgh(Location currentLocation) { - if(currentLocation == null) return false; - return ((currentLocation.getLatitude() > 39.859673 && currentLocation.getLatitude() < 40.992847) && - (currentLocation.getLongitude() > -80.372815 && currentLocation.getLongitude() < -79.414258)); + return currentLocation != null && + ( + (currentLocation.getLatitude() > 39.859673 && currentLocation.getLatitude() < 40.992847) && + (currentLocation.getLongitude() > -80.372815 && currentLocation.getLongitude() < -79.414258) + ); } /** diff --git a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/world/jsonpojo/BustimeVehicleResponse.java b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/world/jsonpojo/BustimeVehicleResponse.java index 02d9cf15..b72934e2 100644 --- a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/world/jsonpojo/BustimeVehicleResponse.java +++ b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/world/jsonpojo/BustimeVehicleResponse.java @@ -55,6 +55,10 @@ public void setError(List error) { } + /** + * Processes the errors into a hashmap since like messages can be transient + * @return the hashmap of processed errors + */ private HashMap> processErrors() { HashMap> processedErrors = new HashMap>(error.size()); @@ -69,6 +73,10 @@ private HashMap> processErrors() { return processedErrors; } + /** + * Gets the processed errors into a HashMap. + * @return a hashmap of messages as keys and the parameters for the messages as the value. + */ public HashMap> getProcessedErrors() { Log.d("vehicle_error", getError().toString()); return processErrors(); @@ -77,7 +85,7 @@ public HashMap> getProcessedErrors() { /** * * @return - * the error + * the raw error messages */ public List getError() { return error; From e00210409cb0082e7dd101eab5ee613a823ee9b3 Mon Sep 17 00:00:00 2001 From: Jeremy Jao Date: Sun, 8 Nov 2015 22:52:13 -0500 Subject: [PATCH 5/6] Add more documentation and put listview into full alphabetic order. --- .../SelectTransit.java | 3 ++ .../jsonpojo/BustimeVehicleResponse.java | 2 + app/src/main/res/values/strings.xml | 44 +++++++++---------- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/SelectTransit.java b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/SelectTransit.java index fb096765..1cdae7e4 100644 --- a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/SelectTransit.java +++ b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/SelectTransit.java @@ -915,6 +915,7 @@ private void showToast(String string, int length) { /** * This creates an observer to either update or add the buses to the map. * @return the vehicle update observer + * @since 55 */ private Observer vehicleBusUpdate() { return new Observer() { @@ -1006,6 +1007,7 @@ private void updateMarker(Vehicle vehicle, Marker marker) { * This is the vehicle update/add Port Authority API observer that will print each processed error * into a Toast. * @return the vehicle update observer + * @since 55 */ private Observer vehicleErrorObserver() { Log.d("vehicle_error_observer", "restarting the vehicle error observer"); @@ -1041,6 +1043,7 @@ public void onNext(ErrorMessage errorMessage) { /** * Creates an anonymous class to make messages more human-readable. * @return a closure to create a human-readable {@link ErrorMessage} + * @since 55 */ private Func1>, ErrorMessage> transformSingleMessage() { return new Func1>, ErrorMessage>() { diff --git a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/world/jsonpojo/BustimeVehicleResponse.java b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/world/jsonpojo/BustimeVehicleResponse.java index b72934e2..67886e83 100644 --- a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/world/jsonpojo/BustimeVehicleResponse.java +++ b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/world/jsonpojo/BustimeVehicleResponse.java @@ -58,6 +58,7 @@ public void setError(List error) { /** * Processes the errors into a hashmap since like messages can be transient * @return the hashmap of processed errors + * @since 55 */ private HashMap> processErrors() { HashMap> processedErrors = @@ -76,6 +77,7 @@ private HashMap> processErrors() { /** * Gets the processed errors into a HashMap. * @return a hashmap of messages as keys and the parameters for the messages as the value. + * @since 55 */ public HashMap> getProcessedErrors() { Log.d("vehicle_error", getError().toString()); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 55ca422d..8d4ec0c4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,6 +5,9 @@ 1 + 2 + 6 + 8 12 13 14 @@ -13,7 +16,6 @@ 17 18 19L - 2 20 21 22 @@ -39,7 +41,6 @@ 57 58 59 - 6 60 61A 61B @@ -60,7 +61,6 @@ 77 78 79 - 8 81 82 83 @@ -74,20 +74,20 @@ G3 G31 O1 - O12 O5 + O12 P1 + P2 + P3 + P7 P10 P12 P13 P16 P17 - P2 - P3 P67 P68 P69 - P7 P71 P76 P78 @@ -99,6 +99,9 @@ #3300cc + #ff33cc + #ffcc33 + #98fb98 #cc00cc #ff6666 #f4a460 @@ -107,7 +110,6 @@ #ff3300 #bc8f8f #00ff66 - #ff33cc #00cc00 #99cc00 #00ff00 @@ -133,7 +135,6 @@ #cc0066 #00ff99 #99ff00 - #ffcc33 #999900 #e0ffff #afeeee @@ -154,7 +155,6 @@ #0099ff #993366 #ff9933 - #98fb98 #ff66cc #ffd700 #00ff33 @@ -168,20 +168,20 @@ #7cfc00 #32cd32 #ff6600 - #ff8c00 #ff7f50 + #ff8c00 #9900ff + #9933cc + #cc00ff + #9933ff #9932cc #ff00ff #cc33cc #cc0000 #990033 - #9933cc - #cc00ff #993399 #ff0099 #ff66ff - #9933ff #6666ff #6600ff #6633ff @@ -193,6 +193,9 @@ Freeport Road + Mount Royal + Spring Hill + Perrysville McKnight Bellevue Ohio Valley @@ -201,7 +204,6 @@ Shadeland Manchester Emsworth Limited - Mount Royal Kennedy Coraopolis McCoy @@ -227,7 +229,6 @@ Hazelwood Greenfield Mon Valley - Spring Hill Walnut-Crawford Village North Braddock Braddock-Swissvale @@ -248,7 +249,6 @@ Penn Hills Oakmont East Hills - Perrysville Oak Hill Lincoln Bedford Hill @@ -262,20 +262,20 @@ Moon Flyer Bridgeville Flyer Ross Flyer - McKnight Flyer Thompson Run Flyer + McKnight Flyer East Busway-All Stops + East Busway Short + East Busway-Oakland + McKeesport Flyer Allegheny Valley Flyer Holiday Park Flyer Mount Royal Flyer Penn Hills Flyer Lincoln Park Flyer - East Busway Short - East Busway-Oakland Monroeville Flyer Braddock Hills Flyer Trafford Flyer - McKeesport Flyer Swissvale Flyer Lincoln Highway Flyer Oakmont Flyer @@ -327,7 +327,7 @@ Title of Bus - Port Authority is currently not tracking + No tracking data available for routes Please check your data connection. Cannot translate information from Port Authority. The Port Authority website seems to be down. From c0e2b2feacf24221e731db5a44cdbeec3c0a0d04 Mon Sep 17 00:00:00 2001 From: Jeremy Jao Date: Sun, 8 Nov 2015 23:23:57 -0500 Subject: [PATCH 6/6] Fix no selection error message --- .../retrofit/patapi/errorcontainers/ErrorMessage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/retrofit/patapi/errorcontainers/ErrorMessage.java b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/retrofit/patapi/errorcontainers/ErrorMessage.java index 599f7c99..5e3576d2 100644 --- a/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/retrofit/patapi/errorcontainers/ErrorMessage.java +++ b/app/src/main/java/rectangledbmi/com/pittsburghrealtimetracker/retrofit/patapi/errorcontainers/ErrorMessage.java @@ -58,7 +58,7 @@ public String getParameters() { * @return the parameters in string form. */ private String commaDelimitParams(List parameters) { - if(parameters == null || parameters.isEmpty()) return null; + if(parameters == null || parameters.isEmpty() || parameters.get(0) == null) return null; parameters = new ArrayList<>(parameters); Collections.sort(parameters, new NaturalOrderComparator<>()); StringBuilder paramBuffer = new StringBuilder();