diff --git a/src/it/reyboz/bustorino/adapters/ArrivalsStopAdapter.java b/src/it/reyboz/bustorino/adapters/ArrivalsStopAdapter.java
index 9d65e71..58eccf9 100644
--- a/src/it/reyboz/bustorino/adapters/ArrivalsStopAdapter.java
+++ b/src/it/reyboz/bustorino/adapters/ArrivalsStopAdapter.java
@@ -1,185 +1,273 @@
/*
BusTO - UI components
Copyright (C) 2017 Fabio Mazza
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
package it.reyboz.bustorino.adapters;
import android.content.Context;
import android.location.Location;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.util.Pair;
import androidx.recyclerview.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
+import com.android.volley.VolleyError;
import it.reyboz.bustorino.R;
import it.reyboz.bustorino.backend.*;
import it.reyboz.bustorino.fragments.FragmentListener;
import it.reyboz.bustorino.util.RoutePositionSorter;
import it.reyboz.bustorino.util.StopSorterByDistance;
+import java.io.NotSerializableException;
import java.util.*;
public class ArrivalsStopAdapter extends RecyclerView.Adapter {
private final static int layoutRes = R.layout.arrivals_nearby_card;
//private List stops;
private @Nullable Location userPosition;
private FragmentListener listener;
private List< Pair > routesPairList = new ArrayList<>();
private Context context;
//Maximum number of stops to keep
private final int MAX_STOPS = 20; //TODO: make it programmable
public ArrivalsStopAdapter(@Nullable List< Pair > routesPairList, FragmentListener fragmentListener, Context con, @Nullable Location pos) {
listener = fragmentListener;
userPosition = pos;
this.routesPairList = routesPairList;
context = con.getApplicationContext();
resetListAndPosition();
// if(paline!=null)
//resetRoutesPairList(paline);
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(parent.getContext()).inflate(layoutRes, parent, false);
return new ViewHolder(view);
}
@Override
- public void onBindViewHolder(ViewHolder holder, int position) {
+ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
//DO THE ACTUAL WORK TO PUT THE DATA
if(routesPairList==null || routesPairList.size() == 0) return; //NO STOPS
final Pair stopRoutePair = routesPairList.get(position);
- if(stopRoutePair!=null){
+ if(stopRoutePair!=null && stopRoutePair.first!=null){
final Stop stop = stopRoutePair.first;
final Route r = stopRoutePair.second;
final Double distance = stop.getDistanceFromLocation(userPosition);
if(distance!=Double.POSITIVE_INFINITY){
holder.distancetextView.setText(distance.intValue()+" m");
} else {
holder.distancetextView.setVisibility(View.GONE);
}
final String stopText = String.format(context.getResources().getString(R.string.two_strings_format),stop.getStopDisplayName(),stop.ID);
holder.stopNameView.setText(stopText);
//final String routeName = String.format(context.getResources().getString(R.string.two_strings_format),r.getNameForDisplay(),r.destinazione);
- holder.lineNameTextView.setText(r.getNameForDisplay());
+ if (r!=null) {
+ holder.lineNameTextView.setText(r.getNameForDisplay());
+ holder.lineDirectionTextView.setText(r.destinazione);
+ holder.arrivalsTextView.setText(r.getPassaggiToString(0,2,true));
+ } else {
+ holder.lineNameTextView.setVisibility(View.INVISIBLE);
+ holder.lineDirectionTextView.setVisibility(View.INVISIBLE);
+ //holder.arrivalsTextView.setVisibility(View.INVISIBLE);
+ }
/* EXPERIMENTS
if(r.destinazione==null || r.destinazione.trim().isEmpty()){
holder.lineDirectionTextView.setVisibility(View.GONE);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) holder.arrivalsDescriptionTextView.getLayoutParams();
params.addRule(RelativeLayout.RIGHT_OF,holder.lineNameTextView.getId());
holder.arrivalsDescriptionTextView.setLayoutParams(params);
} else {
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) holder.arrivalsDescriptionTextView.getLayoutParams();
params.removeRule(RelativeLayout.RIGHT_OF);
holder.arrivalsDescriptionTextView.setLayoutParams(params);
holder.lineDirectionTextView.setVisibility(View.VISIBLE);
}
*/
- holder.lineDirectionTextView.setText(r.destinazione);
- holder.arrivalsTextView.setText(r.getPassaggiToString(0,2,true));
holder.stopID =stop.ID;
} else {
Log.w("SquareStopAdapter","!! The selected stop is null !!");
}
}
@Override
public int getItemCount() {
return routesPairList.size();
}
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView lineNameTextView;
TextView lineDirectionTextView;
TextView stopNameView;
TextView arrivalsDescriptionTextView;
TextView arrivalsTextView;
TextView distancetextView;
String stopID;
ViewHolder(View holdView){
super(holdView);
holdView.setOnClickListener(this);
lineNameTextView = (TextView) holdView.findViewById(R.id.lineNameTextView);
lineDirectionTextView = (TextView) holdView.findViewById(R.id.lineDirectionTextView);
stopNameView = (TextView) holdView.findViewById(R.id.arrivalStopName);
arrivalsTextView = (TextView) holdView.findViewById(R.id.arrivalsTimeTextView);
arrivalsDescriptionTextView = (TextView) holdView.findViewById(R.id.arrivalsDescriptionTextView);
distancetextView = (TextView) holdView.findViewById(R.id.arrivalsDistanceTextView);
}
@Override
public void onClick(View v) {
listener.createFragmentForStop(stopID);
}
}
public void resetRoutesPairList(List stopList){
Collections.sort(stopList,new StopSorterByDistance(userPosition));
this.routesPairList = new ArrayList<>(stopList.size());
int maxNum = Math.min(MAX_STOPS, stopList.size());
for(Palina p: stopList.subList(0,maxNum)){
//if there are no routes available, skip stop
if(p.queryAllRoutes().size() == 0) continue;
for(Route r: p.queryAllRoutes()){
//if there are no routes, should not do anything
routesPairList.add(new Pair<>(p,r));
}
}
}
public void setUserPosition(@Nullable Location userPosition) {
this.userPosition = userPosition;
}
- public void setRoutesPairListAndPosition(List> routesPairList, @Nullable Location pos) {
- if(routesPairList!=null)
- this.routesPairList = routesPairList;
+ public void setRoutesPairListAndPosition(List> mRoutesPairList, @Nullable Location pos) {
if(pos!=null){
this.userPosition = pos;
}
- resetListAndPosition();
+ if(mRoutesPairList!=null){
+ //this.routesPairList = routesPairList;
+ //remove duplicates
+ sortAndRemoveDuplicates(mRoutesPairList, this.userPosition);
+ //routesPairList = mRoutesPairList;
+ //STUPID CODE
+ if (this.routesPairList == null || routesPairList.size() == 0){
+ routesPairList = mRoutesPairList;
+ notifyDataSetChanged();
+ } else{
+
+ final HashMap, Integer> indexMapIn = getRouteIndexMap(mRoutesPairList);
+ final HashMap, Integer> indexMapExisting = getRouteIndexMap(routesPairList);
+ List> oldList = routesPairList;
+ routesPairList = mRoutesPairList;
+
+ for (Pair pair: indexMapIn.keySet()){
+ final Integer posIn = indexMapIn.get(pair);
+ if (posIn == null) continue;
+ if (indexMapExisting.containsKey(pair)){
+ final Integer posExisting = indexMapExisting.get(pair);
+ //THERE IS ALREADY
+ //routesPairList.remove(posExisting.intValue());
+ //routesPairList.add(posIn,mRoutesPairList.get(posIn));
+
+ notifyItemMoved(posExisting, posIn);
+ indexMapExisting.remove(pair);
+ } else{
+ //INSERT IT
+ //routesPairList.add(posIn,mRoutesPairList.get(posIn));
+ notifyItemInserted(posIn);
+ }
+ }//
+ //REMOVE OLD STOPS
+ for (Pair pair: indexMapExisting.keySet()) {
+ final Integer posExisting = indexMapExisting.get(pair);
+ if (posExisting == null) continue;
+ //routesPairList.remove(posExisting.intValue());
+ notifyItemRemoved(posExisting);
+ }
+ //notifyDataSetChanged();
+
+ }
+ //remove and join the
+ }
}
+
+ /**
+ * Sort and remove the repetitions for the routesPairList
+ */
private void resetListAndPosition(){
Collections.sort(this.routesPairList,new RoutePositionSorter(userPosition));
//All of this to get only the first occurrences of a line (name & direction)
ListIterator> iterator = routesPairList.listIterator();
Set> allRoutesDirections = new HashSet<>();
while(iterator.hasNext()){
final Pair stopRoutePair = iterator.next();
- final Pair routeNameDirection = new Pair<>(stopRoutePair.second.getName(),stopRoutePair.second.destinazione);
- if(allRoutesDirections.contains(routeNameDirection)){
- iterator.remove();
- } else {
- allRoutesDirections.add(routeNameDirection);
+ if (stopRoutePair.second != null) {
+ final Pair routeNameDirection = new Pair<>(stopRoutePair.second.getName(), stopRoutePair.second.destinazione);
+ if (allRoutesDirections.contains(routeNameDirection)) {
+ iterator.remove();
+ } else {
+ allRoutesDirections.add(routeNameDirection);
+ }
+ }
+ }
+ }
+ /**
+ * Sort and remove the repetitions in the list
+ */
+ private static void sortAndRemoveDuplicates(List< Pair > routesPairList, Location positionToSort ){
+ Collections.sort(routesPairList,new RoutePositionSorter(positionToSort));
+ //All of this to get only the first occurrences of a line (name & direction)
+ ListIterator> iterator = routesPairList.listIterator();
+ Set> allRoutesDirections = new HashSet<>();
+ while(iterator.hasNext()){
+ final Pair stopRoutePair = iterator.next();
+ if (stopRoutePair.second != null) {
+ final Pair routeNameDirection = new Pair<>(stopRoutePair.second.getName(), stopRoutePair.second.destinazione);
+ if (allRoutesDirections.contains(routeNameDirection)) {
+ iterator.remove();
+ } else {
+ allRoutesDirections.add(routeNameDirection);
+ }
}
}
}
-
+ private static HashMap, Integer> getRouteIndexMap(List> routesPairList){
+ final HashMap, Integer> myMap = new HashMap<>();
+ for (int i=0; i(name.toLowerCase(Locale.ROOT).trim(),destination.toLowerCase(Locale.ROOT).trim()), i);
+ }
+ return myMap;
+ }
}
diff --git a/src/it/reyboz/bustorino/fragments/NearbyStopsFragment.java b/src/it/reyboz/bustorino/fragments/NearbyStopsFragment.java
index 9d55b31..4420473 100644
--- a/src/it/reyboz/bustorino/fragments/NearbyStopsFragment.java
+++ b/src/it/reyboz/bustorino/fragments/NearbyStopsFragment.java
@@ -1,648 +1,648 @@
/*
BusTO - Fragments components
Copyright (C) 2018 Fabio Mazza
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
package it.reyboz.bustorino.fragments;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.location.Location;
import android.net.Uri;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.CursorLoader;
import androidx.loader.content.Loader;
import androidx.core.util.Pair;
import androidx.preference.PreferenceManager;
import androidx.appcompat.widget.AppCompatButton;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.android.volley.*;
import it.reyboz.bustorino.R;
import it.reyboz.bustorino.adapters.ArrivalsStopAdapter;
import it.reyboz.bustorino.backend.*;
import it.reyboz.bustorino.backend.FiveTAPIFetcher.QueryType;
import it.reyboz.bustorino.middleware.AppLocationManager;
import it.reyboz.bustorino.middleware.AppDataProvider;
import it.reyboz.bustorino.middleware.NextGenDB.Contract.*;
import it.reyboz.bustorino.adapters.SquareStopAdapter;
import it.reyboz.bustorino.util.LocationCriteria;
import it.reyboz.bustorino.util.StopSorterByDistance;
import java.util.*;
public class NearbyStopsFragment extends Fragment implements LoaderManager.LoaderCallbacks {
private FragmentListener mListener;
private FragmentLocationListener fragmentLocationListener;
private final String[] PROJECTION = {StopsTable.COL_ID,StopsTable.COL_LAT,StopsTable.COL_LONG,
StopsTable.COL_NAME,StopsTable.COL_TYPE,StopsTable.COL_LINES_STOPPING};
private final static String DEBUG_TAG = "NearbyStopsFragment";
private final static String FRAGMENT_TYPE_KEY = "FragmentType";
public final static int TYPE_STOPS = 19, TYPE_ARRIVALS = 20;
private int fragment_type;
//data Bundle
private final String BUNDLE_LOCATION = "location";
private final int LOADER_ID = 0;
private RecyclerView gridRecyclerView;
private SquareStopAdapter dataAdapter;
private AutoFitGridLayoutManager gridLayoutManager;
boolean canStartDBQuery = true;
private Location lastReceivedLocation = null;
private ProgressBar circlingProgressBar,flatProgressBar;
private int distance;
protected SharedPreferences globalSharedPref;
private SharedPreferences.OnSharedPreferenceChangeListener preferenceChangeListener;
private TextView messageTextView,titleTextView;
private CommonScrollListener scrollListener;
private AppCompatButton switchButton;
private boolean firstLocForStops = true,firstLocForArrivals = true;
public static final int COLUMN_WIDTH_DP = 250;
private Integer MAX_DISTANCE = -3;
private int MIN_NUM_STOPS = -1;
private int TIME_INTERVAL_REQUESTS = -1;
private AppLocationManager locManager;
//These are useful for the case of nearby arrivals
private ArrivalsManager arrivalsManager = null;
private ArrivalsStopAdapter arrivalsStopAdapter = null;
public NearbyStopsFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
* @return A new instance of fragment NearbyStopsFragment.
*/
public static NearbyStopsFragment newInstance(int fragmentType) {
if(fragmentType != TYPE_STOPS && fragmentType != TYPE_ARRIVALS )
throw new IllegalArgumentException("WRONG KIND OF FRAGMENT USED");
NearbyStopsFragment fragment = new NearbyStopsFragment();
final Bundle args = new Bundle(1);
args.putInt(FRAGMENT_TYPE_KEY,fragmentType);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
setFragmentType(getArguments().getInt(FRAGMENT_TYPE_KEY));
}
locManager = AppLocationManager.getInstance(getContext());
fragmentLocationListener = new FragmentLocationListener(this);
globalSharedPref = getContext().getSharedPreferences(getString(R.string.mainSharedPreferences),Context.MODE_PRIVATE);
globalSharedPref.registerOnSharedPreferenceChangeListener(preferenceChangeListener);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View root = inflater.inflate(R.layout.fragment_nearby_stops, container, false);
gridRecyclerView = (RecyclerView) root.findViewById(R.id.stopGridRecyclerView);
gridLayoutManager = new AutoFitGridLayoutManager(getContext().getApplicationContext(), utils.convertDipToPixels(getContext(),COLUMN_WIDTH_DP));
gridRecyclerView.setLayoutManager(gridLayoutManager);
gridRecyclerView.setHasFixedSize(false);
circlingProgressBar = (ProgressBar) root.findViewById(R.id.loadingBar);
flatProgressBar = (ProgressBar) root.findViewById(R.id.horizontalProgressBar);
messageTextView = (TextView) root.findViewById(R.id.messageTextView);
titleTextView = (TextView) root.findViewById(R.id.titleTextView);
switchButton = (AppCompatButton) root.findViewById(R.id.switchButton);
preferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Log.d(DEBUG_TAG,"Key "+key+" was changed");
if(key.equals(getString(R.string.databaseUpdatingPref))){
if(!sharedPreferences.getBoolean(getString(R.string.databaseUpdatingPref),true)){
canStartDBQuery = true;
Log.d(DEBUG_TAG,"The database has finished updating, can start update now");
}
}
}
};
scrollListener = new CommonScrollListener(mListener,false);
switchButton.setOnClickListener(v -> {
switchFragmentType();
});
return root;
}
protected ArrayList createStopListFromCursor(Cursor data){
ArrayList stopList = new ArrayList<>();
final int col_id = data.getColumnIndex(StopsTable.COL_ID);
final int latInd = data.getColumnIndex(StopsTable.COL_LAT);
final int lonInd = data.getColumnIndex(StopsTable.COL_LONG);
final int nameindex = data.getColumnIndex(StopsTable.COL_NAME);
final int typeIndex = data.getColumnIndex(StopsTable.COL_TYPE);
final int linesIndex = data.getColumnIndex(StopsTable.COL_LINES_STOPPING);
data.moveToFirst();
for(int i=0; i onCreateLoader(int id, Bundle args) {
//BUILD URI
lastReceivedLocation = args.getParcelable(BUNDLE_LOCATION);
Uri.Builder builder = new Uri.Builder();
builder.scheme("content").authority(AppDataProvider.AUTHORITY)
.appendPath("stops").appendPath("location")
.appendPath(String.valueOf(lastReceivedLocation.getLatitude()))
.appendPath(String.valueOf(lastReceivedLocation.getLongitude()))
.appendPath(String.valueOf(distance)); //distance
CursorLoader cl = new CursorLoader(getContext(),builder.build(),PROJECTION,null,null,null);
cl.setUpdateThrottle(2000);
return cl;
}
@Override
public void onLoadFinished(Loader loader, Cursor data) {
if (0 > MAX_DISTANCE) throw new AssertionError();
//Cursor might be null
if(data==null){
Log.e(DEBUG_TAG,"Null cursor, something really wrong happened");
return;
}
if(!isDBUpdating() && (data.getCount() stopList = createStopListFromCursor(data);
if(data.getCount()>0) {
//quick trial to hopefully always get the stops in the correct order
Collections.sort(stopList,new StopSorterByDistance(lastReceivedLocation));
switch (fragment_type){
case TYPE_STOPS:
showStopsInRecycler(stopList);
break;
case TYPE_ARRIVALS:
arrivalsManager = new ArrivalsManager(stopList);
flatProgressBar.setVisibility(View.VISIBLE);
flatProgressBar.setProgress(0);
flatProgressBar.setIndeterminate(false);
//for the moment, be satisfied with only one location
//AppLocationManager.getInstance(getContext()).removeLocationRequestFor(fragmentLocationListener);
break;
default:
}
} else {
setNoStopsLayout();
}
}
@Override
public void onLoaderReset(Loader loader) {
}
/**
* To enable targeting from the Button
*/
public void switchFragmentType(View v){
switchFragmentType();
}
/**
* Call when you need to switch the type of fragment
*/
private void switchFragmentType(){
if(fragment_type==TYPE_ARRIVALS){
setFragmentType(TYPE_STOPS);
switchButton.setText(getString(R.string.show_arrivals));
titleTextView.setText(getString(R.string.nearby_stops_message));
if(arrivalsManager!=null)
arrivalsManager.cancelAllRequests();
if(dataAdapter!=null)
gridRecyclerView.setAdapter(dataAdapter);
} else if (fragment_type==TYPE_STOPS){
setFragmentType(TYPE_ARRIVALS);
titleTextView.setText(getString(R.string.nearby_arrivals_message));
switchButton.setText(getString(R.string.show_stops));
if(arrivalsStopAdapter!=null)
gridRecyclerView.setAdapter(arrivalsStopAdapter);
}
fragmentLocationListener.lastUpdateTime = -1;
locManager.removeLocationRequestFor(fragmentLocationListener);
locManager.addLocationRequestFor(fragmentLocationListener);
}
//useful methods
protected boolean isDBUpdating(){
return globalSharedPref.getBoolean(getString(R.string.databaseUpdatingPref),false);
}
/////// GUI METHODS ////////
private void showStopsInRecycler(List stops){
if(firstLocForStops) {
dataAdapter = new SquareStopAdapter(stops, mListener, lastReceivedLocation);
gridRecyclerView.setAdapter(dataAdapter);
firstLocForStops = false;
}else {
dataAdapter.setStops(stops);
dataAdapter.setUserPosition(lastReceivedLocation);
}
dataAdapter.notifyDataSetChanged();
//showRecyclerHidingLoadMessage();
if (gridRecyclerView.getVisibility() != View.VISIBLE) {
circlingProgressBar.setVisibility(View.GONE);
gridRecyclerView.setVisibility(View.VISIBLE);
}
messageTextView.setVisibility(View.GONE);
}
private void showArrivalsInRecycler(List palinas){
Collections.sort(palinas,new StopSorterByDistance(lastReceivedLocation));
final ArrayList> routesPairList = new ArrayList<>(10);
//int maxNum = Math.min(MAX_STOPS, stopList.size());
for(Palina p: palinas){
//if there are no routes available, skip stop
if(p.queryAllRoutes().size() == 0) continue;
for(Route r: p.queryAllRoutes()){
//if there are no routes, should not do anything
routesPairList.add(new Pair<>(p,r));
}
}
if(firstLocForArrivals){
arrivalsStopAdapter = new ArrivalsStopAdapter(routesPairList,mListener,getContext(),lastReceivedLocation);
gridRecyclerView.setAdapter(arrivalsStopAdapter);
firstLocForArrivals = false;
} else {
arrivalsStopAdapter.setRoutesPairListAndPosition(routesPairList,lastReceivedLocation);
}
- arrivalsStopAdapter.notifyDataSetChanged();
+ //arrivalsStopAdapter.notifyDataSetChanged();
showRecyclerHidingLoadMessage();
}
private void setNoStopsLayout(){
messageTextView.setVisibility(View.VISIBLE);
messageTextView.setText(R.string.no_stops_nearby);
circlingProgressBar.setVisibility(View.GONE);
}
/**
* Does exactly what is says on the tin
*/
private void showRecyclerHidingLoadMessage(){
if (gridRecyclerView.getVisibility() != View.VISIBLE) {
circlingProgressBar.setVisibility(View.GONE);
gridRecyclerView.setVisibility(View.VISIBLE);
}
messageTextView.setVisibility(View.GONE);
}
class ArrivalsManager implements FiveTAPIVolleyRequest.ResponseListener, Response.ErrorListener{
final HashMap mStops;
final Map> routesToAdd = new HashMap<>();
final static String REQUEST_TAG = "NearbyArrivals";
private final QueryType[] types = {QueryType.ARRIVALS,QueryType.DETAILS};
final NetworkVolleyManager volleyManager;
private final int MAX_ARRIVAL_STOPS =35;
int activeRequestCount = 0,reqErrorCount = 0, reqSuccessCount=0;
ArrivalsManager(List stops){
mStops = new HashMap<>();
volleyManager = NetworkVolleyManager.getInstance(getContext());
for(Stop s: stops.subList(0,Math.min(stops.size(), MAX_ARRIVAL_STOPS))){
mStops.put(s.ID,new Palina(s));
for(QueryType t: types) {
final FiveTAPIVolleyRequest req = FiveTAPIVolleyRequest.getNewRequest(t, s.ID, this, this);
if (req != null) {
req.setTag(REQUEST_TAG);
volleyManager.addToRequestQueue(req);
activeRequestCount++;
}
}
}
flatProgressBar.setMax(activeRequestCount);
}
@Override
public void onErrorResponse(VolleyError error) {
if(error instanceof ParseError){
//TODO
Log.w(DEBUG_TAG,"Parsing error for stop request");
} else if (error instanceof NetworkError){
String s;
if(error.networkResponse!=null)
s = new String(error.networkResponse.data);
else s="";
Log.w(DEBUG_TAG,"Network error: "+s);
}else {
Log.w(DEBUG_TAG,"Volley Error: "+error.getMessage());
}
if(error.networkResponse!=null){
Log.w(DEBUG_TAG, "Error status code: "+error.networkResponse.statusCode);
}
//counters
activeRequestCount--;
reqErrorCount++;
flatProgressBar.setProgress(reqErrorCount+reqSuccessCount);
}
@Override
public void onResponse(Palina result, QueryType type) {
//counter for requests
activeRequestCount--;
reqSuccessCount++;
final Palina palinaInMap = mStops.get(result.ID);
//palina cannot be null here
//sorry for the brutal crash when it happens
if(palinaInMap == null) throw new IllegalStateException("Cannot get the palina from the map");
//necessary to split the Arrivals and Details cases
switch (type){
case ARRIVALS:
palinaInMap.addInfoFromRoutes(result.queryAllRoutes());
final List possibleRoutes = routesToAdd.get(result.ID);
if(possibleRoutes!=null) {
palinaInMap.addInfoFromRoutes(possibleRoutes);
routesToAdd.remove(result.ID);
}
break;
case DETAILS:
if(palinaInMap.queryAllRoutes().size()>0){
//merge the branches
palinaInMap.addInfoFromRoutes(result.queryAllRoutes());
} else {
routesToAdd.put(result.ID,result.queryAllRoutes());
}
break;
default:
throw new IllegalArgumentException("Wrong QueryType in onResponse");
}
final ArrayList outList = new ArrayList<>();
for(Palina p: mStops.values()){
final List routes = p.queryAllRoutes();
if(routes!=null && routes.size()>0) outList.add(p);
}
showArrivalsInRecycler(outList);
flatProgressBar.setProgress(reqErrorCount+reqSuccessCount);
if(activeRequestCount==0) {
flatProgressBar.setIndeterminate(true);
flatProgressBar.setVisibility(View.GONE);
}
}
void cancelAllRequests(){
volleyManager.getRequestQueue().cancelAll(REQUEST_TAG);
flatProgressBar.setVisibility(View.GONE);
}
}
/**
* Local locationListener, to use for the GPS
*/
class FragmentLocationListener implements AppLocationManager.LocationRequester{
LoaderManager.LoaderCallbacks callbacks;
private int oldLocStatus = -2;
private LocationCriteria cr;
private long lastUpdateTime = -1;
public FragmentLocationListener(LoaderManager.LoaderCallbacks callbacks) {
this.callbacks = callbacks;
}
@Override
public void onLocationChanged(Location location) {
//set adapter
float accuracy = location.getAccuracy();
if(accuracy<60 && canStartDBQuery) {
distance = 20;
final Bundle msgBundle = new Bundle();
msgBundle.putParcelable(BUNDLE_LOCATION,location);
getLoaderManager().restartLoader(LOADER_ID,msgBundle,callbacks);
}
lastUpdateTime = System.currentTimeMillis();
Log.d("BusTO:NearPositListen","can start loader "+ canStartDBQuery);
}
@Override
public void onLocationStatusChanged(int status) {
switch(status){
case AppLocationManager.LOCATION_GPS_AVAILABLE:
messageTextView.setVisibility(View.GONE);
break;
case AppLocationManager.LOCATION_UNAVAILABLE:
messageTextView.setText(R.string.enableGpsText);
messageTextView.setVisibility(View.VISIBLE);
break;
default:
Log.e(DEBUG_TAG,"Location status not recognized");
}
}
@Override
public LocationCriteria getLocationCriteria() {
return new LocationCriteria(60,TIME_INTERVAL_REQUESTS);
}
@Override
public long getLastUpdateTimeMillis() {
return lastUpdateTime;
}
void resetUpdateTime(){
lastUpdateTime = -1;
}
}
/**
* Simple trick to get an automatic number of columns (from https://www.journaldev.com/13792/android-gridlayoutmanager-example)
*
*/
class AutoFitGridLayoutManager extends GridLayoutManager {
private int columnWidth;
private boolean columnWidthChanged = true;
public AutoFitGridLayoutManager(Context context, int columnWidth) {
super(context, 1);
setColumnWidth(columnWidth);
}
public void setColumnWidth(int newColumnWidth) {
if (newColumnWidth > 0 && newColumnWidth != columnWidth) {
columnWidth = newColumnWidth;
columnWidthChanged = true;
}
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (columnWidthChanged && columnWidth > 0) {
int totalSpace;
if (getOrientation() == VERTICAL) {
totalSpace = getWidth() - getPaddingRight() - getPaddingLeft();
} else {
totalSpace = getHeight() - getPaddingTop() - getPaddingBottom();
}
int spanCount = Math.max(1, totalSpace / columnWidth);
setSpanCount(spanCount);
columnWidthChanged = false;
}
super.onLayoutChildren(recycler, state);
}
}
}