Page Menu
Home
GitPull.it
Search
Configure Global Search
Log In
Files
F13558886
D246.1783826766.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Authored By
Unknown
Size
114 KB
Referenced Files
None
Subscribers
None
D246.1783826766.diff
View Options
diff --git a/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java b/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java
--- a/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java
+++ b/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java
@@ -49,6 +49,8 @@
import java.util.Arrays;
import java.util.Map;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.function.Consumer;
import it.reyboz.bustorino.backend.Stop;
import it.reyboz.bustorino.data.DBUpdateCheckWorker;
@@ -70,9 +72,6 @@
private final static String TAG_FAVORITES="favorites_frag";
private Snackbar snackbar;
- private boolean showingMainFragmentFromOther = false;
- private boolean onCreateComplete = false;
-
private ServiceAlertsViewModel serviceAlertsViewModel;
private FragmentKind showingFragmentKind;
@@ -85,8 +84,7 @@
R.id.nav_favorites_item, () ->
checkAndShowFavoritesFragment(getSupportFragmentManager(), true),
- R.id.nav_home, () ->
- showHomeMainFragmentFromClick(true),
+ R.id.nav_home, this::showHomeMainFragmentFromClick,
R.id.nav_map_item, () ->
requestMapFragment(true),
@@ -99,6 +97,7 @@
R.id.nav_nearby, this::openNearbyStopsFragment
);
+
private long lastClosingAttempt = -1L;
private final OnBackPressedCallback backPressedCallback = new OnBackPressedCallback(false) {
@Override
@@ -117,7 +116,6 @@
}
};
-
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -279,10 +277,10 @@
case "favorites" -> checkAndShowFavoritesFragment(framan, false);
case "lines" -> showLinesFragment(framan, false, null);
case "nearby" -> createShowMainFragment(framan, MainScreenFragment.makeArgsNearby(), false);
- default -> showHomeMainFragmentFromClick(false);
+ default -> createShowMainFragment(framan, MainScreenFragment.makeArgsButtonsScreen(), false);
}
}
- onCreateComplete = true;
+ //boolean onCreateComplete = true;
//last but not least, set the good default values
checkApplyDefaultSettingsValues();
@@ -475,20 +473,30 @@
*/
private boolean activityCustomBackPressed(){
- boolean resolved = true;
var mainFragManager = getSupportFragmentManager();
+ boolean resolved = mainFragManager.getBackStackEntryCount() > 0;
+
Fragment shownFrag = mainFragManager.findFragmentById(R.id.mainActContentFrame);
- if (mDrawer.isDrawerOpen(GravityCompat.START))
+ if (mDrawer.isDrawerOpen(GravityCompat.START)) {
mDrawer.closeDrawer(GravityCompat.START);
- else if(shownFrag != null && shownFrag.isVisible() && shownFrag.getChildFragmentManager().getBackStackEntryCount() > 0){
- if(shownFrag instanceof MainScreenFragment){
- //we have to stop the arrivals reload
- ((MainScreenFragment) shownFrag).cancelReloadArrivalsIfNeeded();
+ resolved = true;
+ }
+ else if (shownFrag instanceof MainScreenFragment mainFrag) { //the only case when we have to pop both stacks
+ mainFrag.cancelReloadArrivalsIfNeeded();
+
+ var chManager = mainFrag.getChildFragmentManager();
+
+ if(chManager.getBackStackEntryCount() > 0){
+ chManager.popBackStack();
+ Log.d(DEBUG_TAG, "Child back stack popped");
+ resolved = true;
+ } else{
+ Log.d(DEBUG_TAG, "No back stack on child fragment manager");
}
- shownFrag.getChildFragmentManager().popBackStack();
- if(showingMainFragmentFromOther && mainFragManager.getBackStackEntryCount() > 0){
- getSupportFragmentManager().popBackStack();
- Log.d(DEBUG_TAG, "Popping main back stack also");
+ boolean haveToPopMainFromFragment = mainFrag.needToPopMainStackOnBack();
+ //mainFrag.getChildFragmentManager().popBackStack();
+ if(haveToPopMainFromFragment){ // pops the stack
+ mainFragManager.popBackStack();
}
}
else if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
@@ -536,6 +544,7 @@
private void updateShowingFragmentKindInternal(@NonNull FragmentKind newKind){
if(BuildConfig.DEBUG)
Log.d(DEBUG_TAG, "Updating fragment kind, new: "+newKind+", current: "+showingFragmentKind);
+ boolean showingMainFragmentFromOther = false;
if(showingFragmentKind == null){
showingFragmentKind = newKind;
showingMainFragmentFromOther = false;
@@ -562,7 +571,9 @@
R.anim.slide_out // popExit
)*/
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
- if (addToBackStack) ft.addToBackStack(null);
+ if (addToBackStack) {
+ ft.addToBackStack(null);
+ }
ft.commit();
}
/**
@@ -571,7 +582,6 @@
* @param arguments args for the fragment
*/
private static void createShowMainFragment(FragmentManager fraMan,@Nullable Bundle arguments, boolean addToBackStack){
- //var frag = MainScreenFragment.newInstance();
FragmentTransaction ft = fraMan.beginTransaction()
.replace(R.id.mainActContentFrame, MainScreenFragment.class, arguments, MainScreenFragment.FRAGMENT_TAG)
.setReorderingAllowed(false)
@@ -585,34 +595,18 @@
if (addToBackStack) ft.addToBackStack(null);
ft.commit();
}
- private void showMainFragmentFromClick(@Nullable Bundle argsToCreate, boolean addToBackStack){
- FragmentManager fraMan = getSupportFragmentManager();
- Fragment fragment = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG);
- final MainScreenFragment mainScreenFragment;
- if (fragment==null | !(fragment instanceof MainScreenFragment)){
- createShowMainFragment(fraMan, argsToCreate, addToBackStack);
- }
- else if(!fragment.isVisible()){
-
- mainScreenFragment = (MainScreenFragment) fragment;
- showMainFragment(fraMan, mainScreenFragment, addToBackStack);
- Log.d(DEBUG_TAG, "Found the main fragment");
- } else{
- mainScreenFragment = (MainScreenFragment) fragment;
- }
- }
-
- private void showHomeMainFragmentFromClick(boolean addToBackStack){
+ private void showHomeMainFragmentFromClick(){
FragmentManager fraMan = getSupportFragmentManager();
var fragment = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG);
if(fragment instanceof MainScreenFragment mainFrag){
+ var visible = mainFrag.isVisible();
if(!mainFrag.isVisible()){
- showMainFragment(fraMan, mainFrag, addToBackStack);
+ showMainFragment(fraMan, mainFrag, true);
}
- mainFrag.showButtonsFragmentIfNotNearby(addToBackStack);
+ mainFrag.showButtonsFragmentIfNotNearby(true);
} else{
- createShowMainFragment(fraMan, MainScreenFragment.makeArgsButtonsScreen(), addToBackStack);
+ createShowMainFragment(fraMan, MainScreenFragment.makeArgsButtonsScreen(), true);
}
}
@@ -714,10 +708,7 @@
@Override
public void readyGUIfor(FragmentKind fragmentType) {
- MainScreenFragment mainFragmentIfVisible = getMainFragmentIfVisible();
- if (mainFragmentIfVisible!=null){
- mainFragmentIfVisible.readyGUIfor(fragmentType);
- }
+
updateShowingFragmentKindInternal(fragmentType);
Integer titleResId = null;
@@ -758,38 +749,24 @@
}
if(getSupportActionBar()!=null && titleResId!=null)
getSupportActionBar().setTitle(titleResId);
+ MainScreenFragment mainFragmentIfVisible = getMainFragmentIfVisible();
+ if (mainFragmentIfVisible!=null){
+ mainFragmentIfVisible.readyGUIfor(fragmentType);
+ }
}
@Override
public void requestArrivalsForStopID(String ID) {
- //register if the request came from the main fragment or not
- MainScreenFragment probableFragment = getMainFragmentIfVisible();
-
- // this has some contorted logic, but it works
- if (probableFragment == null){
- FragmentManager fraMan = getSupportFragmentManager();
- Fragment fragment = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG);
- Log.d(DEBUG_TAG, "Requested main fragment, not visible. Search by TAG returned: "+fragment);
- if(fragment!=null){
- //the fragment is there but not shown
- probableFragment = (MainScreenFragment) fragment;
- // set the flag
- probableFragment.setSuppressArrivalsReload(true);
- showMainFragment(fraMan, probableFragment, true);
- probableFragment.requestArrivalsForStopID(ID);
- } else {
- // we have no fragment
- //if onCreate is complete, then we are not asking for the first showing fragment
- final Bundle args = MainScreenFragment.makeArgsArrivals(ID);
- boolean addtobackstack = onCreateComplete;
- createShowMainFragment(fraMan, args ,addtobackstack);
- }
- } else {
- //the MainScreeFragment is shown, nothing to do
- probableFragment.requestArrivalsForStopID(ID);
+ Consumer<MainScreenFragment> consumer = fragment -> {
+ fragment.setSuppressArrivalsReload(true);
+ fragment.requestArrivalsForStopID(ID);
+ };
+ boolean done = getMainFragmentAndDoStuff(consumer);
+ if(!done){
+ //create the fragment
+ final Bundle args = MainScreenFragment.makeArgsArrivals(ID);
+ createShowMainFragment(getSupportFragmentManager(), args ,true);
}
-
- mNavView.setCheckedItem(R.id.nav_home);
}
@Override
public void openLineFromStop(String routeGtfsId, @Nullable String stopIDFrom){
@@ -802,6 +779,23 @@
}
+ private boolean getMainFragmentAndDoStuff(Consumer<MainScreenFragment> consumer){
+ FragmentManager fraMan = getSupportFragmentManager();
+ var frag = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG);
+ if( frag instanceof MainScreenFragment mainFrag){
+ if(!mainFrag.isVisible()) {
+ showMainFragment(fraMan, mainFrag, true);
+ mainFrag.setMainFragmentManagerTransition(true);
+ } else{
+ mainFrag.setMainFragmentManagerTransition(false);
+ }
+ consumer.accept(mainFrag);
+ return true;
+ } else{
+ return false;
+ }
+ }
+
@Override
public void openLineFromVehicle(String routeGtfsId, @Nullable String optionalPatternId, @Nullable Bundle args) {
@@ -814,17 +808,9 @@
@Override
public void openNearbyStopsFragment() {
- FragmentManager fraMan = getSupportFragmentManager();
- var fragment = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG);
- if(fragment instanceof MainScreenFragment mainFrag){
- if(!mainFrag.isVisible()){
- showMainFragment(fraMan, mainFrag, true);
- }
- mainFrag.openNearbyStopsFragment();
- } else{
- // there is no fragment and it is not visible
- // add to back stack the main fragment, as the NearbyStopsFragment will not be added
- createShowMainFragment(fraMan, MainScreenFragment.makeArgsNearby(), true);
+ boolean done = getMainFragmentAndDoStuff(MainScreenFragment::openNearbyStopsFragment);
+ if(!done){
+ createShowMainFragment(getSupportFragmentManager(), MainScreenFragment.makeArgsNearby(), true);
}
}
diff --git a/app/src/main/java/it/reyboz/bustorino/adapters/ArrivalsStopAdapter.java b/app/src/main/java/it/reyboz/bustorino/adapters/ArrivalsStopAdapter.java
--- a/app/src/main/java/it/reyboz/bustorino/adapters/ArrivalsStopAdapter.java
+++ b/app/src/main/java/it/reyboz/bustorino/adapters/ArrivalsStopAdapter.java
@@ -24,6 +24,7 @@
import androidx.annotation.Nullable;
import androidx.core.util.Pair;
import androidx.preference.PreferenceManager;
+import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
@@ -34,8 +35,6 @@
import it.reyboz.bustorino.R;
import it.reyboz.bustorino.backend.*;
import it.reyboz.bustorino.fragments.FragmentListenerMain;
-import it.reyboz.bustorino.util.RoutePositionSorter;
-import it.reyboz.bustorino.util.StopSorterByDistance;
import java.util.*;
@@ -44,15 +43,15 @@
//private List<Stop> stops;
private @NonNull GPSPoint userPosition;
private FragmentListenerMain listener;
- private List< Pair<Stop, Route> > routesPairList;
+ private List< RouteWithStop > routesPairList;
private final Context context;
//Maximum number of stops to keep
private final int MAX_STOPS = 20; //TODO: make it programmable
- private String KEY_CAPITALIZE;
+ private final String KEY_CAPITALIZE;
private NameCapitalize capit;
- public ArrivalsStopAdapter(@Nullable List< Pair<Stop, Route> > routesPairList, FragmentListenerMain fragmentListener, Context con, @NonNull GPSPoint pos) {
+ public ArrivalsStopAdapter(@Nullable List< RouteWithStop > routesPairList, FragmentListenerMain fragmentListener, Context con, @NonNull GPSPoint pos) {
listener = fragmentListener;
userPosition = pos;
this.routesPairList = routesPairList;
@@ -81,10 +80,10 @@
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<Stop,Route> stopRoutePair = routesPairList.get(position);
- if(stopRoutePair!=null && stopRoutePair.first!=null){
- final Stop stop = stopRoutePair.first;
- final Route r = stopRoutePair.second;
+ final var stopRoutePair = routesPairList.get(position);
+ if(stopRoutePair != null){
+ final Stop stop = stopRoutePair.getStop();
+ final Route r = stopRoutePair.getRoute();
final Double distance = stop.getDistanceFromLocation(userPosition.getLatitude(), userPosition.longitude);
if(distance!=Double.POSITIVE_INFINITY){
holder.distancetextView.setText(distance.intValue()+" m");
@@ -165,7 +164,7 @@
}
}
-
+ /*
public void resetRoutesPairList(List<Palina> stopList){
Collections.sort(stopList,new StopSorterByDistance(userPosition));
@@ -181,59 +180,96 @@
}
}
- public void setUserPosition(@Nullable GPSPoint userPosition) {
- this.userPosition = userPosition;
+ */
+
+ public void updatePosition(@NonNull GPSPoint pos){
+ userPosition = pos;
}
- public void setRoutesPairListAndPosition(List<Pair<Stop, Route>> mRoutesPairList, @Nullable GPSPoint pos) {
- if(pos!=null){
- this.userPosition = pos;
+ public void setRoutesPairListAndPosition(@NonNull List<RouteWithStop> newList, @Nullable GPSPoint pos) {
+ if(pos!=null) updatePosition(pos);
+
+ if (routesPairList == null) {
+ routesPairList = new ArrayList<>(newList);
+ notifyItemRangeInserted(0, newList.size());
+ return;
}
- 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<Pair<String,String>, Integer> indexMapIn = getRouteIndexMap(mRoutesPairList);
- final HashMap<Pair<String,String>, Integer> indexMapExisting = getRouteIndexMap(routesPairList);
- //List<Pair<Stop,Route>> oldList = routesPairList;
- routesPairList = mRoutesPairList;
- /*
- for (Pair<String,String> 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);
+
+ DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffUtil.Callback() {
+ @Override
+ public int getOldListSize() {
+ return routesPairList.size();
+ }
+
+ @Override
+ public int getNewListSize() {
+ return newList.size();
}
- }//
- //REMOVE OLD STOPS
- for (Pair<String,String> pair: indexMapExisting.keySet()) {
+
+ @Override
+ public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
+
+ RouteWithStop oldItem = routesPairList.get(oldItemPosition);
+ RouteWithStop newItem = newList.get(newItemPosition);
+
+ // usa un ID univoco
+ return oldItem.getId().equals(newItem.getId());
+ }
+
+ @Override
+ public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
+
+ RouteWithStop oldItem = routesPairList.get(oldItemPosition);
+ RouteWithStop newItem = newList.get(newItemPosition);
+
+ // confronta il contenuto
+ return oldItem.equals(newItem);
+ }
+ }
+ );
+
+ routesPairList.clear();
+ routesPairList.addAll(newList);
+
+ diffResult.dispatchUpdatesTo(this);
+
+
+ /*if (this.routesPairList == null || routesPairList.size() == 0){
+ routesPairList = mRoutesPairList;
+ notifyDataSetChanged();
+ } else{
+
+ final var indexMapIn = getRouteIndexMap(mRoutesPairList);
+ final var indexMapExisting = getRouteIndexMap(routesPairList);
+ //List<Pair<Stop,Route>> oldList = routesPairList;
+ routesPairList = mRoutesPairList;
+ /*
+ for (Pair<String,String> pair: indexMapIn.keySet()){
+ final Integer posIn = indexMapIn.get(pair);
+ if (posIn == null) continue;
+ if (indexMapExisting.containsKey(pair)){
final Integer posExisting = indexMapExisting.get(pair);
- if (posExisting == null) continue;
+ //THERE IS ALREADY
//routesPairList.remove(posExisting.intValue());
- notifyItemRemoved(posExisting);
+ //routesPairList.add(posIn,mRoutesPairList.get(posIn));
+
+ notifyItemMoved(posExisting, posIn);
+ indexMapExisting.remove(pair);
+ } else{
+ //INSERT IT
+ //routesPairList.add(posIn,mRoutesPairList.get(posIn));
+ notifyItemInserted(posIn);
}
- //*/notifyDataSetChanged();
-
+ }//
+ //REMOVE OLD STOPS
+ for (Pair<String,String> pair: indexMapExisting.keySet()) {
+ final Integer posExisting = indexMapExisting.get(pair);
+ if (posExisting == null) continue;
+ //routesPairList.remove(posExisting.intValue());
+ notifyItemRemoved(posExisting);
}
- //remove and join the
- }
+
+ */
}
@@ -241,52 +277,27 @@
* 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<Pair<Stop,Route>> iterator = routesPairList.listIterator();
- Set<Pair<String,String>> allRoutesDirections = new HashSet<>();
- while(iterator.hasNext()){
- final Pair<Stop,Route> stopRoutePair = iterator.next();
- if (stopRoutePair.second != null) {
- final Pair<String, String> 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<Stop, Route> > routesPairList, GPSPoint positionToSort ){
- Collections.sort(routesPairList,new RoutePositionSorter(positionToSort));
+ //Collections.sort(this.routesPairList,new RoutePositionSorter(userPosition));
//All of this to get only the first occurrences of a line (name & direction)
- ListIterator<Pair<Stop,Route>> iterator = routesPairList.listIterator();
+ var iterator = routesPairList.listIterator();
Set<Pair<String,String>> allRoutesDirections = new HashSet<>();
while(iterator.hasNext()){
- final Pair<Stop,Route> stopRoutePair = iterator.next();
- if (stopRoutePair.second != null) {
- final Pair<String, String> routeNameDirection = new Pair<>(stopRoutePair.second.getName(), stopRoutePair.second.destinazione);
- if (allRoutesDirections.contains(routeNameDirection)) {
- iterator.remove();
- } else {
- allRoutesDirections.add(routeNameDirection);
- }
+ final var stopRoutePair = iterator.next();
+ stopRoutePair.getRoute();
+ final Pair<String, String> routeNameDirection = new Pair<>(stopRoutePair.getRoute().getName(), stopRoutePair.getRoute().destinazione);
+ if (allRoutesDirections.contains(routeNameDirection)) {
+ iterator.remove();
+ } else {
+ allRoutesDirections.add(routeNameDirection);
}
}
}
- private static HashMap<Pair<String, String>, Integer> getRouteIndexMap(List<Pair<Stop, Route>> routesPairList){
- final HashMap<Pair<String, String>, Integer> myMap = new HashMap<>();
+
+ private static HashMap<RouteWithStop, Integer> getRouteIndexMap(List<RouteWithStop> routesPairList){
+ final HashMap<RouteWithStop, Integer> myMap = new HashMap<>();
for (int i=0; i<routesPairList.size(); i++){
- final Route r = routesPairList.get(i).second;
- if (r==null) continue;
- final String name = r.getName();
- final String destination = r.destinazione;
- if (name!= null && destination!=null)
- myMap.put(new Pair<>(name.toLowerCase(Locale.ROOT).trim(),destination.toLowerCase(Locale.ROOT).trim()), i);
+ myMap.put(routesPairList.get(i), i);
}
return myMap;
}
diff --git a/app/src/main/java/it/reyboz/bustorino/adapters/SquareStopAdapter.java b/app/src/main/java/it/reyboz/bustorino/adapters/SquareStopAdapter.java
--- a/app/src/main/java/it/reyboz/bustorino/adapters/SquareStopAdapter.java
+++ b/app/src/main/java/it/reyboz/bustorino/adapters/SquareStopAdapter.java
@@ -19,6 +19,7 @@
import android.location.Location;
import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
@@ -27,6 +28,7 @@
import android.widget.TextView;
import it.reyboz.bustorino.R;
import it.reyboz.bustorino.backend.GPSPoint;
+import it.reyboz.bustorino.backend.RouteWithStop;
import it.reyboz.bustorino.backend.Stop;
import it.reyboz.bustorino.util.StopSorterByDistance;
import it.reyboz.bustorino.fragments.FragmentListenerMain;
@@ -113,8 +115,36 @@
}
- public void setStops(List<Stop> stops) {
- this.stops = stops;
+ public void setStops(List<Stop> newStops) {
+ // this.stops = stops;
+ DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffUtil.Callback() {
+ @Override
+ public int getOldListSize() {
+ return stops.size();
+ }
+ @Override
+ public int getNewListSize() {
+ return newStops.size();
+ }
+ @Override
+ public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
+ var oldItem = stops.get(oldItemPosition);
+ var newItem = newStops.get(newItemPosition);
+
+ // usa un ID univoco
+ return oldItem.ID.equals(newItem.ID);
+ }
+
+ @Override
+ public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
+ var oldItem = stops.get(oldItemPosition);
+ var newItem = newStops.get(newItemPosition);
+ return oldItem.equals(newItem);
+ }
+ });
+ this.stops.clear();
+ this.stops.addAll(newStops);
+ diffResult.dispatchUpdatesTo(this);
}
public void setUserPosition(@Nullable GPSPoint userPosition) {
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/FiveTNormalizer.java b/app/src/main/java/it/reyboz/bustorino/backend/FiveTNormalizer.java
--- a/app/src/main/java/it/reyboz/bustorino/backend/FiveTNormalizer.java
+++ b/app/src/main/java/it/reyboz/bustorino/backend/FiveTNormalizer.java
@@ -19,6 +19,7 @@
package it.reyboz.bustorino.backend;
import android.util.Log;
+import androidx.annotation.NonNull;
/**
* Converts some weird stop IDs found on the 5T website to the form used everywhere else (including GTT website).
@@ -254,6 +255,7 @@
return null;
}
}
+ @NonNull
public static String fixShortNameForDisplay(String routeID, boolean withBarratoSpace) {
/*if (routeID.length() == 3 && routeID.charAt(2) == 'B') {
return routeID.substring(0, 2).concat("/");
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/RouteWithStop.kt b/app/src/main/java/it/reyboz/bustorino/backend/RouteWithStop.kt
new file mode 100644
--- /dev/null
+++ b/app/src/main/java/it/reyboz/bustorino/backend/RouteWithStop.kt
@@ -0,0 +1,10 @@
+package it.reyboz.bustorino.backend
+
+data class RouteWithStop(
+ val stop: Stop,
+ val route: Route,
+) {
+ val id by lazy {
+ "stop${stop.ID}route${route.displayCode}"
+ }
+}
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/FragmentHelper.java b/app/src/main/java/it/reyboz/bustorino/fragments/FragmentHelper.java
--- a/app/src/main/java/it/reyboz/bustorino/fragments/FragmentHelper.java
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/FragmentHelper.java
@@ -35,13 +35,14 @@
import java.lang.ref.WeakReference;
import java.util.List;
+import java.util.concurrent.LinkedBlockingDeque;
/**
* Helper class to manage the fragments and their needs
*/
public class FragmentHelper {
//GeneralActivity act;
- private final FragmentListenerMain listenerMain;
+ private final FragmentListenerMain mainFragment;
private final WeakReference<FragmentManager> managerWeakRef;
private Stop lastSuccessfullySearchedBusStop;
//support for multiple frames
@@ -52,13 +53,16 @@
private static final String DEBUG_TAG = "BusTO FragmHelper";
private final StopSearcher stopSearcher;
+ //this is for deciding whether to popMain Fragment stack with children
+ private final LinkedBlockingDeque<Boolean> popMainQueueOnMainFragStack = new LinkedBlockingDeque<>();
+
public FragmentHelper(FragmentListenerMain listener, FragmentManager framan, Context context, int mainFrame) {
this(listener,framan, context,mainFrame,NO_FRAME);
}
public FragmentHelper(FragmentListenerMain listener, FragmentManager fraMan, Context context, int primaryFrameLayout, int secondaryFrameLayout) {
- this.listenerMain = listener;
+ this.mainFragment = listener;
this.managerWeakRef = new WeakReference<>(fraMan);
this.primaryFrameLayout = primaryFrameLayout;
this.secondaryFrameLayout = secondaryFrameLayout;
@@ -79,7 +83,16 @@
this.lastSuccessfullySearchedBusStop = stop;
}
-
+ public boolean needToPopMainStackOnBack(){
+ if(popMainQueueOnMainFragStack.isEmpty()){
+ return true;
+ }
+ return popMainQueueOnMainFragStack.pop();
+ }
+ public void setMainFragmentManagerTransition(boolean popMain){
+ Log.d(DEBUG_TAG, "Adding child fragment pop for main screen: " + popMain);
+ popMainQueueOnMainFragStack.addFirst(popMain);
+ }
/**
* Called when you need to create a fragment for a specified Palina
* @param p the Stop that needs to be displayed
@@ -124,7 +137,7 @@
// enable fragment auto refresh
arrivalsFragment.setReloadOnResume(true);
- listenerMain.hideKeyboard();
+ mainFragment.hideKeyboard();
toggleSpinner(false);
}
@@ -134,7 +147,7 @@
* @param query String queried
*/
public void createStopListFragment(List<Stop> resultList, String query, boolean addToBackStack){
- listenerMain.hideKeyboard();
+ mainFragment.hideKeyboard();
StopListFragment listfragment = StopListFragment.newInstance(query);
if(managerWeakRef.get()==null) {
//SOMETHING WENT VERY WRONG
@@ -143,6 +156,8 @@
}
attachFragmentToContainer(managerWeakRef.get(),
listfragment, "search_"+query, false, addToBackStack);
+ //DO NOT DO THE SAME ON THE ARRIVALS (the call goes through MainActivity)
+ setMainFragmentManagerTransition(false);
listfragment.setStopList(resultList);
//listenerMain.readyGUIfor(FragmentKind.STOPS);
toggleSpinner(false);
@@ -154,7 +169,7 @@
* @param on new status of spinner system
*/
public void toggleSpinner(boolean on){
- listenerMain.toggleSpinner(on);
+ mainFragment.toggleSpinner(on);
}
/**
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java
--- a/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java
@@ -49,8 +49,10 @@
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.Map;
+import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;
+import it.reyboz.bustorino.BuildConfig;
import it.reyboz.bustorino.R;
import it.reyboz.bustorino.backend.*;
import it.reyboz.bustorino.util.Permissions;
@@ -65,7 +67,7 @@
* Use the {@link MainScreenFragment#newInstance} factory method to
* create an instance of this fragment.
*/
-public class MainScreenFragment extends BarcodeFragment implements FragmentListenerMain{
+public class MainScreenFragment extends BarcodeFragment implements FragmentListenerMain, ParentFragmentManagerFromChild{
private static final String SAVED_FRAGMENT="saved_fragment";
@@ -140,7 +142,8 @@
private CoordinatorLayout coordLayout;
//this is really a hackish thing, but it works
- private LinkedBlockingQueue<Runnable> thingsToDoOnStart = new LinkedBlockingQueue<>();
+ private final LinkedBlockingQueue<Runnable> thingsToDoOnStart = new LinkedBlockingQueue<>();
+
private void refreshStop() {
if(getContext() == null){
@@ -243,7 +246,6 @@
}
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -261,6 +263,18 @@
pendingSearchQuery = args.getString(ARG_SEARCH_QUERY);
}
+ fragmentHelper = new FragmentHelper(this, getChildFragmentManager(), getContext(), R.id.resultFrame);
+
+ }
+
+ @Override
+ public boolean needToPopMainStackOnBack() {
+ return fragmentHelper.needToPopMainStackOnBack();
+ }
+
+ @Override
+ public void setMainFragmentManagerTransition(boolean yes) {
+ fragmentHelper.setMainFragmentManagerTransition(yes);
}
@Override
@@ -325,8 +339,6 @@
childFragMan = getChildFragmentManager();
childFragMan.addOnBackStackChangedListener(() -> Log.d("BusTO Main Fragment", "BACK STACK CHANGED"));
- fragmentHelper = new FragmentHelper(this, getChildFragmentManager(), getContext(), R.id.resultFrame);
-
/*
cr.setAccuracy(Criteria.ACCURACY_FINE);
cr.setAltitudeRequired(false);
@@ -793,8 +805,7 @@
*/
@Override
public void readyGUIfor(FragmentKind fragmentType) {
-
-
+ if(BuildConfig.DEBUG) Log.d(DEBUG_TAG, "Readying main fragment for type "+fragmentType);
//if we are getting results, already, stop waiting for nearbyStops
if (fragmentType == FragmentKind.ARRIVALS || fragmentType == FragmentKind.STOPS) {
hideKeyboard();
@@ -815,7 +826,7 @@
prepareGUIForBusStops();
break;
default:
- Log.d(DEBUG_TAG, "Fragment type is unknown");
+ //Log.d(DEBUG_TAG, "Fragment type is unknown");
return;
}
// Shows hints
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/NearbyArrivalsDownloader.java b/app/src/main/java/it/reyboz/bustorino/fragments/NearbyArrivalsDownloader.java
deleted file mode 100644
--- a/app/src/main/java/it/reyboz/bustorino/fragments/NearbyArrivalsDownloader.java
+++ /dev/null
@@ -1,122 +0,0 @@
-package it.reyboz.bustorino.fragments;
-
-import android.content.Context;
-import android.util.Log;
-import com.android.volley.NetworkError;
-import com.android.volley.ParseError;
-import com.android.volley.Response;
-import com.android.volley.VolleyError;
-import it.reyboz.bustorino.backend.NetworkVolleyManager;
-import it.reyboz.bustorino.backend.Palina;
-import it.reyboz.bustorino.backend.Route;
-import it.reyboz.bustorino.backend.Stop;
-import it.reyboz.bustorino.backend.mato.MapiArrivalRequest;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-
-class NearbyArrivalsDownloader implements Response.Listener<Palina>, Response.ErrorListener {
- final static String DEBUG_TAG = "BusTO-NearbyArrivDowns";
- //final Map<String,List<Route>> routesToAdd = new HashMap<>();
- final ArrayList<Palina> nonEmptyPalinas = new ArrayList<>();
- final HashMap<String, Boolean> completedRequests = new HashMap<>();
- final static String REQUEST_TAG = "NearbyArrivals";
- final NetworkVolleyManager volleyManager;
- int activeRequestCount = 0, reqErrorCount = 0, reqSuccessCount = 0;
-
- final ArrivalsListener listener;
-
- NearbyArrivalsDownloader(Context context, ArrivalsListener arrivalsListener) {
- volleyManager = NetworkVolleyManager.getInstance(context);
-
- listener = arrivalsListener;
- //flatProgressBar.setMax(numreq);
- }
-
- public int requestArrivalsForStops(List<Stop> stops){
- int MAX_ARRIVAL_STOPS = 35;
- Date currentDate = new Date();
- int timeRange = 3600;
- int departures = 10;
- int numreq = 0;
- activeRequestCount = 0;
- reqErrorCount = 0;
- reqSuccessCount = 0;
- nonEmptyPalinas.clear();
- completedRequests.clear();
-
- for (Stop s : stops.subList(0, Math.min(stops.size(), MAX_ARRIVAL_STOPS))) {
-
- final MapiArrivalRequest req = new MapiArrivalRequest(s.ID, currentDate, timeRange, departures, this, this);
- req.setTag(REQUEST_TAG);
- volleyManager.addToRequestQueue(req);
- activeRequestCount++;
- numreq++;
- completedRequests.put(s.ID, false);
- }
- listener.setProgress(reqErrorCount+reqSuccessCount, activeRequestCount);
- return numreq;
- }
-
- private int totalRequests(){
- return activeRequestCount + reqSuccessCount + reqErrorCount;
- }
-
-
- @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);
- listener.setProgress(reqErrorCount + reqSuccessCount, activeRequestCount);
- }
-
- @Override
- public void onResponse(Palina palinaResult) {
- //counter for requests
- activeRequestCount--;
- reqSuccessCount++;
- listener.setProgress(reqErrorCount + reqSuccessCount, activeRequestCount);
-
- //add the palina to the successful one
- if(palinaResult!=null) {
- final List<Route> routes = palinaResult.queryAllRoutes();
- if (routes != null && !routes.isEmpty()) {
- nonEmptyPalinas.add(palinaResult);
- listener.showCompletedArrivals(nonEmptyPalinas);
- }
- }
- }
-
- void cancelAllRequests() {
- volleyManager.getRequestQueue().cancelAll(REQUEST_TAG);
- //flatProgressBar.setVisibility(View.GONE);
- listener.onAllRequestsCancelled();
- }
-
- public interface ArrivalsListener{
- void setProgress(int completedRequests, int pendingRequests);
-
- void onAllRequestsCancelled();
-
- void showCompletedArrivals(ArrayList<Palina> completedPalinas);
- }
-}
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/NearbyArrivalsDownloader.kt b/app/src/main/java/it/reyboz/bustorino/fragments/NearbyArrivalsDownloader.kt
new file mode 100644
--- /dev/null
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/NearbyArrivalsDownloader.kt
@@ -0,0 +1,113 @@
+package it.reyboz.bustorino.fragments
+
+import android.content.Context
+import android.util.Log
+import com.android.volley.NetworkError
+import com.android.volley.ParseError
+import com.android.volley.Response
+import com.android.volley.VolleyError
+import it.reyboz.bustorino.backend.NetworkVolleyManager
+import it.reyboz.bustorino.backend.Palina
+import it.reyboz.bustorino.backend.Stop
+import it.reyboz.bustorino.backend.mato.MapiArrivalRequest
+import java.util.*
+import kotlin.math.min
+
+internal class NearbyArrivalsDownloader(context: Context, val listener: ArrivalsListener) : Response.Listener<Palina>,
+ Response.ErrorListener {
+ //final Map<String,List<Route>> routesToAdd = new HashMap<>();
+ val nonEmptyPalinas = ArrayList<Palina>()
+ val completedRequests: HashMap<String?, Boolean?> = HashMap<String?, Boolean?>()
+ val volleyManager: NetworkVolleyManager = NetworkVolleyManager.getInstance(context)
+ var activeRequestCount: Int = 0
+ var reqErrorCount: Int = 0
+ var reqSuccessCount: Int = 0
+
+
+ fun requestArrivalsForStops(stops: List<Stop>): Int {
+ val currentDate = Date()
+ val timeRange = 3600
+ val departures = 10
+ var numreq = 0
+ activeRequestCount = 0
+ reqErrorCount = 0
+ reqSuccessCount = 0
+ nonEmptyPalinas.clear()
+ completedRequests.clear()
+
+ for (s in stops.subList(0, min(stops.size, MAX_ARRIVAL_STOPS))) {
+ val req = MapiArrivalRequest(s.ID, currentDate, timeRange, departures, this, this)
+ req.setTag(REQUEST_TAG)
+ volleyManager.addToRequestQueue(req)
+ activeRequestCount++
+ numreq++
+ completedRequests[s.ID] = false
+ }
+ listener.setProgress(reqErrorCount + reqSuccessCount, activeRequestCount)
+ return numreq
+ }
+
+ private fun totalRequests(): Int {
+ return activeRequestCount + reqSuccessCount + reqErrorCount
+ }
+
+
+ override fun onErrorResponse(error: VolleyError) {
+ if (error is ParseError) {
+ //TODO
+ Log.w(DEBUG_TAG, "Parsing error for stop request")
+ } else if (error is NetworkError) {
+ val s: String?
+ if (error.networkResponse != null) s = String(error.networkResponse.data)
+ else s = ""
+ Log.w(DEBUG_TAG, "Network error: " + s)
+ } else {
+ Log.w(DEBUG_TAG, "Volley Error: " + error.message)
+ }
+ if (error.networkResponse != null) {
+ Log.w(DEBUG_TAG, "Error status code: " + error.networkResponse.statusCode)
+ }
+ //counters
+ activeRequestCount--
+ reqErrorCount++
+ //flatProgressBar.setProgress(reqErrorCount + reqSuccessCount);
+ listener.setProgress(reqErrorCount + reqSuccessCount, activeRequestCount)
+ }
+
+ override fun onResponse(palinaResult: Palina?) {
+ //counter for requests
+ activeRequestCount--
+ reqSuccessCount++
+ listener.setProgress(reqErrorCount + reqSuccessCount, activeRequestCount)
+
+ //add the palina to the successful one
+ if (palinaResult != null) {
+ val routes = palinaResult.queryAllRoutes()
+ if (routes != null && !routes.isEmpty()) {
+ nonEmptyPalinas.add(palinaResult)
+ listener.showCompletedArrivals(nonEmptyPalinas)
+ }
+ }
+ }
+
+ fun cancelAllRequests() {
+ volleyManager.getRequestQueue().cancelAll(REQUEST_TAG)
+ //flatProgressBar.setVisibility(View.GONE);
+ listener.onAllRequestsCancelled()
+ }
+
+ interface ArrivalsListener {
+ fun setProgress(completedRequests: Int, pendingRequests: Int)
+
+ fun onAllRequestsCancelled()
+
+ fun showCompletedArrivals(completedPalinas: ArrayList<Palina>)
+ }
+
+ companion object {
+ private const val DEBUG_TAG: String = "BusTO-NearbyArrivDowns"
+ const val REQUEST_TAG: String = "NearbyArrivals"
+ private const val MAX_ARRIVAL_STOPS = 35
+
+ }
+}
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.java
deleted file mode 100644
--- a/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.java
+++ /dev/null
@@ -1,716 +0,0 @@
-/*
- 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 <http://www.gnu.org/licenses/>.
- */
-package it.reyboz.bustorino.fragments;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-
-import android.content.SharedPreferences;
-import android.location.Location;
-import android.os.Bundle;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.lifecycle.Observer;
-import androidx.lifecycle.ViewModelProvider;
-import androidx.core.util.Pair;
-import androidx.preference.PreferenceManager;
-import androidx.appcompat.widget.AppCompatButton;
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.work.WorkInfo;
-
-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 it.reyboz.bustorino.BuildConfig;
-import it.reyboz.bustorino.R;
-import it.reyboz.bustorino.adapters.ArrivalsStopAdapter;
-import it.reyboz.bustorino.backend.*;
-import it.reyboz.bustorino.data.DatabaseUpdate;
-import it.reyboz.bustorino.adapters.SquareStopAdapter;
-import it.reyboz.bustorino.middleware.AutoFitGridLayoutManager;
-import it.reyboz.bustorino.middleware.FusedNativeLocationProvider;
-import it.reyboz.bustorino.util.Permissions;
-import it.reyboz.bustorino.util.StopSorterByDistance;
-import it.reyboz.bustorino.viewmodels.NearbyStopsViewModel;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.*;
-
-public class NearbyStopsFragment extends ScreenBaseFragment {
-
- @Nullable
- @Override
- public View getBaseViewForSnackBar() {
- return null;
- }
-
- public enum FragType{
- STOPS(1), ARRIVALS(2);
- private final int num;
- FragType(int num){
- this.num = num;
- }
- public static FragType fromNum(int i){
- switch (i){
- case 1: return STOPS;
- case 2: return ARRIVALS;
- default:
- throw new IllegalArgumentException("type not recognized");
- }
- }
- }
- private enum LocationShowingStatus {SEARCHING, FIRST_FIX, DISABLED, NO_PERMISSION}
-
- private FragmentListenerMain mListener;
-
- 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 FragType fragment_type = FragType.STOPS;
-
- public final static String FRAGMENT_TAG="NearbyStopsFrag";
-
- private RecyclerView gridRecyclerView;
-
- private SquareStopAdapter dataAdapter;
- private AutoFitGridLayoutManager gridLayoutManager;
- private GPSPoint lastPosition = null;
- private ProgressBar circlingProgressBar,flatProgressBar;
- //protected SharedPreferences globalSharedPref;
- //private SharedPreferences.OnSharedPreferenceChangeListener preferenceChangeListener;
- private TextView messageTextView,titleTextView, loadingTextView;
- 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;
-
- //These are useful for the case of nearby arrivals
- private NearbyArrivalsDownloader arrivalsManager = null;
- private ArrivalsStopAdapter arrivalsStopAdapter = null;
-
- private ArrayList<Stop> currentNearbyStops = new ArrayList<>();
-
- private LocationShowingStatus showingStatus = LocationShowingStatus.NO_PERMISSION;
- private boolean isLocationEnabled = false;
-
- private final FusedNativeLocationProvider.LocationUpdateListener locationUpdateListener = new FusedNativeLocationProvider.LocationUpdateListener() {
- @Override
- public void onLocationUpdate(@NotNull Location location) {
- updateLocationViewModel(location);
- }
-
- @Override
- public void onFusedStatusChanged(boolean isEnabled) {
- Log.d(DEBUG_TAG, "Location provider is enabled: " + isEnabled);
- isLocationEnabled = isEnabled;
- if(isEnabled){
- setShowingStatus(LocationShowingStatus.SEARCHING);
- } else{
- setShowingStatus(LocationShowingStatus.DISABLED);
- }
- }
- };
- private final FusedNativeLocationProvider.Options locationOptionsArrivals = new FusedNativeLocationProvider.Options(5*1000L, 50f),
- locationOptionsStops = new FusedNativeLocationProvider.Options(1000L, 5f);;
-
-
-
- /*
- TODO: we do not request the permission in this fragment, only showing it when we have the location. Request position if this changes.
- private final ActivityResultLauncher<String[]> permissionsResultLauncher = getPositionRequestLauncher(
- granted ->{
-
- }
- );
- */
- private FusedNativeLocationProvider locationProvider = null;
-
-
- private final NearbyArrivalsDownloader.ArrivalsListener arrivalsListener = new NearbyArrivalsDownloader.ArrivalsListener() {
- @Override
- public void setProgress(int completedRequests, int pendingRequests) {
- if(flatProgressBar!=null) {
- if (pendingRequests == 0) {
- flatProgressBar.setIndeterminate(true);
- flatProgressBar.setVisibility(View.GONE);
- } else {
- flatProgressBar.setIndeterminate(false);
- flatProgressBar.setProgress(completedRequests);
- }
- }
- }
-
- @Override
- public void onAllRequestsCancelled() {
- if(flatProgressBar!=null) flatProgressBar.setVisibility(View.GONE);
- }
-
- @Override
- public void showCompletedArrivals(ArrayList<Palina> completedPalinas) {
- showArrivalsInRecycler(completedPalinas);
- }
- };
-
- //ViewModel
- private NearbyStopsViewModel viewModel;
-
- 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(FragType type) {
- //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,type.num);
- fragment.setArguments(args);
- return fragment;
- }
-
-
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (getArguments() != null) {
- setFragmentType(FragType.fromNum(getArguments().getInt(FRAGMENT_TYPE_KEY)));
- }
- //locManager = (LocationManager) requireContext().getSystemService(Context.LOCATION_SERVICE);
- //fragmentLocationListener = new FragmentLocationListener();
- if (getContext()!=null) {
- //globalSharedPref = getContext().getSharedPreferences(getString(R.string.mainSharedPreferences), Context.MODE_PRIVATE);
- //globalSharedPref.registerOnSharedPreferenceChangeListener(preferenceChangeListener);
- }
-
- //NearbyArrivalsDownloader nearbyArrivalsDownloader = new NearbyArrivalsDownloader(getContext().getApplicationContext(), arrivalsListener);
- locationProvider = new FusedNativeLocationProvider(requireContext());
- }
-
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- // Inflate the layout for this fragment
- if (getContext() == null) throw new RuntimeException();
- View root = inflater.inflate(R.layout.fragment_nearby_stops, container, false);
- gridRecyclerView = root.findViewById(R.id.stopGridRecyclerView);
- gridLayoutManager = new AutoFitGridLayoutManager(getContext().getApplicationContext(), Float.valueOf(utils.convertDipToPixels(getContext(),COLUMN_WIDTH_DP)).intValue());
- gridRecyclerView.setLayoutManager(gridLayoutManager);
- gridRecyclerView.setHasFixedSize(false);
- circlingProgressBar = root.findViewById(R.id.circularProgressBar);
- flatProgressBar = root.findViewById(R.id.horizontalProgressBar);
- messageTextView = root.findViewById(R.id.messageTextView);
- titleTextView = root.findViewById(R.id.titleTextView);
- loadingTextView = root.findViewById(R.id.positionLoadingTextView);
- switchButton = root.findViewById(R.id.switchButton);
-
- scrollListener = new CommonScrollListener(mListener,false);
- switchButton.setOnClickListener(v -> switchFragmentType());
- if(BuildConfig.DEBUG)
- Log.d(DEBUG_TAG, "onCreateView");
-
- final Context appContext =requireContext().getApplicationContext();
- DatabaseUpdate.watchUpdateWorkStatus(getContext(), this, new Observer<List<WorkInfo>>() {
- @SuppressLint("MissingPermission")
- @Override
- public void onChanged(List<WorkInfo> workInfos) {
- if(workInfos.isEmpty()) {
- viewModel.setDBUpdateRunning(false);
- return;
- }
-
- WorkInfo wi = workInfos.get(0);
- if (wi.getState() == WorkInfo.State.RUNNING && locationProvider.isRunning()) {
- locationProvider.stopUpdates();
- viewModel.setDBUpdateRunning(true);
- } else{
- //start the request
- if(Permissions.bothLocationPermissionsGranted(requireContext())) {
- if(!locationProvider.isRunning()){
- startLocationUpdatesByType();
- }
- } else{
- setShowingStatus(LocationShowingStatus.NO_PERMISSION);
- }
-
- viewModel.setDBUpdateRunning(false);
- //actually restart request
- }
- }
- });
-
- //observe the livedata
- viewModel.getStopsAtDistance().observe(getViewLifecycleOwner(), stops -> {
- Log.d(DEBUG_TAG, "Received "+stops.size()+" stops nearby");
- Integer distance = viewModel.getDistanceMtLiveData().getValue();
- if(distance == null){
- distance = 40;
- }
- if ((stops.size() < MIN_NUM_STOPS && distance <= MAX_DISTANCE)) {
- viewModel.setDistance(distance + 40);
- //viewModel.requestStopsAtDistance(distance, true);
- //Log.d(DEBUG_TAG, "Doubling distance now!");
- return; // THIS WORKS AS AN `else`
- }
- if(!stops.isEmpty()) {
- currentNearbyStops =stops;
- showStopsInViews(currentNearbyStops, lastPosition);
- }
- });
- if(Permissions.anyLocationPermissionsGranted(appContext)){
- setShowingStatus(LocationShowingStatus.SEARCHING);
- } else {
- setShowingStatus(LocationShowingStatus.NO_PERMISSION);
-
- }
- //add location listener
- locationProvider.addListener(locationUpdateListener);
-
- return root;
- }
-
- //because linter is stupid and cannot look inside *anyLocationPermissionGranted*
- @SuppressLint("MissingPermission")
- private boolean requestLocationUpdates(){
- if(Permissions.anyLocationPermissionsGranted(requireContext())) {
- startLocationUpdatesByType();
- return true;
- } else return false;
- }
-
- /**
- * Internal bit used to start location updates
- */
- private void startLocationUpdatesByType(){
- switch (fragment_type) {
- case STOPS: locationProvider.startUpdates(locationOptionsStops); break;
- case ARRIVALS: locationProvider.startUpdates(locationOptionsArrivals); break;
- }
- }
-
-
-
- /**
- * Use this method to set the fragment type
- * @param type the type, TYPE_ARRIVALS or TYPE_STOPS
- */
- private void setFragmentType(FragType type){
- boolean isChanged = fragment_type != type;
- this.fragment_type = type;
- /*switch(type){
- case ARRIVALS:
- TIME_INTERVAL_REQUESTS = 5*1000;
- break;
- case STOPS:
- TIME_INTERVAL_REQUESTS = 1000;
-
- }
-
- */
- if(isChanged){
- startLocationUpdatesByType();
- setShowingStatus(LocationShowingStatus.SEARCHING);
- }
- }
- /**
- * Set the location in the view model if it is good
- * @param location new location
- */
- private void updateLocationViewModel(@NonNull Location location, float accuracy){
- if(viewModel==null) {
- return;
- }
- if(location.getAccuracy()<accuracy) {
- lastPosition = new GPSPoint(location.getLatitude(), location.getLongitude());
- //viewModel.requestStopsAtDistance(location.getLatitude(), location.getLongitude(), distance, true);
- viewModel.setLastLocation(location);
- }
- }
- private void updateLocationViewModel(@NonNull Location location){
- updateLocationViewModel(location, 150);
- }
-
- private void setShowingStatus(@NonNull LocationShowingStatus newStatus){
- if(BuildConfig.DEBUG)
- Log.d(DEBUG_TAG, "Asked to set showing status : " + newStatus);
- if(newStatus == showingStatus){
- return;
- }
- if(!isLocationEnabled && newStatus!=LocationShowingStatus.NO_PERMISSION){
- Log.d(DEBUG_TAG, "asked to show status: "+newStatus+" but the position is disabled");
- newStatus = LocationShowingStatus.DISABLED;
- }
-
- switch (newStatus){
- case FIRST_FIX:
- circlingProgressBar.setVisibility(View.GONE);
- loadingTextView.setVisibility(View.GONE);
- gridRecyclerView.setVisibility(View.VISIBLE);
- messageTextView.setVisibility(View.GONE);
- break;
- case NO_PERMISSION:
- circlingProgressBar.setVisibility(View.GONE);
- loadingTextView.setVisibility(View.GONE);
- messageTextView.setText(R.string.enable_position_message_nearby);
- messageTextView.setVisibility(View.VISIBLE);
- break;
- case DISABLED:
- //if (showingStatus== LocationShowingStatus.SEARCHING){
- circlingProgressBar.setVisibility(View.GONE);
- loadingTextView.setVisibility(View.GONE);
- //}
- messageTextView.setText(R.string.enable_location_message);
- messageTextView.setVisibility(View.VISIBLE);
- break;
- case SEARCHING:
- circlingProgressBar.setVisibility(View.VISIBLE);
- loadingTextView.setVisibility(View.VISIBLE);
- gridRecyclerView.setVisibility(View.GONE);
- messageTextView.setVisibility(View.GONE);
- }
- showingStatus = newStatus;
- }
-
-
- @Override
- public void onAttach(@NonNull Context context) {
- super.onAttach(context);
- if (context instanceof FragmentListenerMain) {
- mListener = (FragmentListenerMain) context;
- } else {
- throw new RuntimeException(context
- + " must implement OnFragmentInteractionListener");
- }
- Log.d(DEBUG_TAG, "OnAttach called");
- viewModel = new ViewModelProvider(this).get(NearbyStopsViewModel.class);
-
-
- }
-
- @Override
- public void onPause() {
- super.onPause();
-
- gridRecyclerView.setAdapter(null);
- Log.d(DEBUG_TAG,"On paused called");
-
- locationProvider.stopUpdates();
- }
-
- @Override
- public void onResume() {
- super.onResume();
- //fix view if we were showing the stops or the arrivals
- prepareForFragmentType();
- switch(fragment_type){
- case STOPS:
- if(dataAdapter!=null){
- //gridRecyclerView.setAdapter(dataAdapter);
- circlingProgressBar.setVisibility(View.GONE);
- loadingTextView.setVisibility(View.GONE);
- }
- break;
- case ARRIVALS:
- if(arrivalsStopAdapter!=null){
- //gridRecyclerView.setAdapter(arrivalsStopAdapter);
- circlingProgressBar.setVisibility(View.GONE);
- loadingTextView.setVisibility(View.GONE);
- }
- }
-
- mListener.enableRefreshLayout(false);
- Log.d(DEBUG_TAG,"OnResume called");
- if(getContext()==null){
- Log.e(DEBUG_TAG, "NULL CONTEXT, everything is going to crash now");
- MIN_NUM_STOPS = 5;
- MAX_DISTANCE = 600;
- return;
- }
- //Re-read preferences
- SharedPreferences shpr = PreferenceManager.getDefaultSharedPreferences(getContext().getApplicationContext());
- //For some reason, they are all saved as strings
- MAX_DISTANCE = shpr.getInt(getString(R.string.pref_key_radius_recents),600);
- boolean isMinStopInt = true;
- try{
- MIN_NUM_STOPS = shpr.getInt(getString(R.string.pref_key_num_recents), 5);
- } catch (ClassCastException ex){
- isMinStopInt = false;
- }
- if(!isMinStopInt)
- try {
- MIN_NUM_STOPS = Integer.parseInt(shpr.getString(getString(R.string.pref_key_num_recents), "5"));
- } catch (NumberFormatException ex){
- MIN_NUM_STOPS = 5;
- }
- if(BuildConfig.DEBUG)
- Log.d(DEBUG_TAG, "Max distance for stops: "+MAX_DISTANCE+ ", Min number of stops: "+MIN_NUM_STOPS);
-
- if(!locationProvider.isRunning()){
- startLocationUpdatesByType();
- }
-
- }
-
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- gridRecyclerView.setVisibility(View.INVISIBLE);
- gridRecyclerView.addOnScrollListener(scrollListener);
- if(mListener!=null) mListener.readyGUIfor(FragmentKind.NEARBY_STOPS);
- }
-
- @Override
- public void onDetach() {
- super.onDetach();
- mListener = null;
- if(arrivalsManager!=null) arrivalsManager.cancelAllRequests();
- }
-
- /**
- * Display the stops, or run new set of requests for arrivals
- */
- private void showStopsInViews(ArrayList<Stop> stops, GPSPoint location){
- if (stops.isEmpty()) {
- setNoStopsLayout();
- return;
- }
- if (location == null){
- // we could do something better, but it's better to do this for now
- return;
- }
-
- double minDistance = Double.POSITIVE_INFINITY;
- for(Stop s: stops){
- minDistance = Math.min(minDistance, s.getDistanceFromLocation(location.getLatitude(), location.getLongitude()));
- }
-
-
- //quick trial to hopefully always get the stops in the correct order
- Collections.sort(stops,new StopSorterByDistance(location));
- switch (fragment_type){
- case STOPS:
- showStopsInRecycler(stops);
- break;
- case ARRIVALS:
- if(getContext()==null) break; //don't do anything if we're not attached
- if(arrivalsManager==null)
- arrivalsManager = new NearbyArrivalsDownloader(getContext().getApplicationContext(), arrivalsListener);
- arrivalsManager.requestArrivalsForStops(stops);
- /*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:
- }
-
- }
-
- /**
- * 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(){
- switch (fragment_type){
- case ARRIVALS:
- setFragmentType(FragType.STOPS);
- break;
- case STOPS:
- setFragmentType(FragType.ARRIVALS);
- break;
- default:
- }
- prepareForFragmentType();
- //locManager.removeLocationRequestFor(fragmentLocationListener);
- //locManager.addLocationRequestFor(fragmentLocationListener);
- if(lastPosition!=null) {
- // we have at least one fix on the position
- showStopsInViews(currentNearbyStops, lastPosition);
- }
- }
-
- /**
- * Prepare the views for the set fragment type
- */
- private void prepareForFragmentType(){
- if(fragment_type==FragType.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==FragType.ARRIVALS){
- titleTextView.setText(getString(R.string.nearby_arrivals_message));
- switchButton.setText(getString(R.string.show_stops));
- if(arrivalsStopAdapter!=null)
- gridRecyclerView.setAdapter(arrivalsStopAdapter);
- }
- }
-
- //useful methods
-
- /////// GUI METHODS ////////
- private void showStopsInRecycler(List<Stop> stops){
-
- if(firstLocForStops) {
- dataAdapter = new SquareStopAdapter(stops, mListener, lastPosition);
- gridRecyclerView.setAdapter(dataAdapter);
- firstLocForStops = false;
- }else {
- dataAdapter.setStops(stops);
- dataAdapter.setUserPosition(lastPosition);
- }
- dataAdapter.notifyDataSetChanged();
-
- //showRecyclerHidingLoadMessage();
- if (gridRecyclerView.getVisibility() != View.VISIBLE) {
- circlingProgressBar.setVisibility(View.GONE);
- loadingTextView.setVisibility(View.GONE);
- gridRecyclerView.setVisibility(View.VISIBLE);
- }
- messageTextView.setVisibility(View.GONE);
-
- if(mListener!=null) mListener.readyGUIfor(FragmentKind.NEARBY_STOPS);
- }
-
- private void showArrivalsInRecycler(List<Palina> palinas){
- Collections.sort(palinas,new StopSorterByDistance(lastPosition));
-
- final ArrayList<Pair<Stop,Route>> 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().isEmpty()) continue;
- for(Route r: p.queryAllRoutes()){
- //if there are no routes, should not do anything
- if (r.passaggi != null && !r.passaggi.isEmpty())
- routesPairList.add(new Pair<>(p,r));
- }
- }
- if (getContext()==null){
- Log.e(DEBUG_TAG, "Trying to show arrivals in Recycler but we're not attached");
- return;
- }
- if(firstLocForArrivals){
- arrivalsStopAdapter = new ArrivalsStopAdapter(routesPairList,mListener,getContext(),lastPosition);
- gridRecyclerView.setAdapter(arrivalsStopAdapter);
- firstLocForArrivals = false;
- } else {
- arrivalsStopAdapter.setRoutesPairListAndPosition(routesPairList,lastPosition);
- }
-
- //arrivalsStopAdapter.notifyDataSetChanged();
-
- showRecyclerHidingLoadMessage();
- if(mListener!=null) mListener.readyGUIfor(FragmentKind.NEARBY_ARRIVALS);
-
- }
-
- private void setNoStopsLayout(){
- messageTextView.setVisibility(View.VISIBLE);
- messageTextView.setText(R.string.no_stops_nearby);
- circlingProgressBar.setVisibility(View.GONE);
- loadingTextView.setVisibility(View.GONE);
- }
-
- /**
- * Does exactly what is says on the tin
- */
- private void showRecyclerHidingLoadMessage(){
- if (gridRecyclerView.getVisibility() != View.VISIBLE) {
- circlingProgressBar.setVisibility(View.GONE);
- loadingTextView.setVisibility(View.GONE);
- gridRecyclerView.setVisibility(View.VISIBLE);
- }
- messageTextView.setVisibility(View.GONE);
- }
-
- /*
- * Local locationListener, to use for the GPS
- */
- /*
- class FragmentLocationListener implements LocationListenerCompat {
-
- private long lastUpdateTime = -1;
- public boolean isRegistered = false;
-
- @Override
- public void onLocationChanged(@NonNull Location location) {
- if(viewModel==null){
- return;
- }
- if(location.getAccuracy()<200) {
-
- lastPosition = new GPSPoint(location.getLatitude(), location.getLongitude());
- //viewModel.requestStopsAtDistance(location.getLatitude(), location.getLongitude(), distance, true);
- viewModel.setLastLocation(location);
- }
- lastUpdateTime = System.currentTimeMillis();
- //Log.d("BusTO:NearPositListen","can start request for stops: "+ !dbUpdateRunning);
- }
-
- @Override
- public void onProviderEnabled(@NonNull String provider) {
- Log.d(DEBUG_TAG, "Location provider "+provider+" enabled");
- if(provider.equals(LocationManager.GPS_PROVIDER)){
- setShowingStatus(LocationShowingStatus.SEARCHING);
- }
- }
-
- @Override
- public void onProviderDisabled(@NonNull String provider) {
- Log.d(DEBUG_TAG, "Location provider "+provider+" disabled");
- if(provider.equals(LocationManager.GPS_PROVIDER)) {
- setShowingStatus(LocationShowingStatus.DISABLED);
- }
- }
-
- @Override
- public void onStatusChanged(@NonNull String provider, int status, @Nullable Bundle extras) {
- LocationListenerCompat.super.onStatusChanged(provider, status, extras);
- }
- }
-
- */
-
-}
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.kt
new file mode 100644
--- /dev/null
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.kt
@@ -0,0 +1,682 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ */
+package it.reyboz.bustorino.fragments
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.location.Location
+import android.os.Bundle
+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 androidx.appcompat.widget.AppCompatButton
+import androidx.core.util.Pair
+import androidx.fragment.app.viewModels
+import androidx.preference.PreferenceManager
+import androidx.recyclerview.widget.RecyclerView
+import androidx.work.WorkInfo
+import it.reyboz.bustorino.BuildConfig
+import it.reyboz.bustorino.R
+import it.reyboz.bustorino.adapters.ArrivalsStopAdapter
+import it.reyboz.bustorino.adapters.SquareStopAdapter
+import it.reyboz.bustorino.backend.*
+import it.reyboz.bustorino.data.DatabaseUpdate
+import it.reyboz.bustorino.middleware.AutoFitGridLayoutManager
+import it.reyboz.bustorino.middleware.FusedNativeLocationProvider
+import it.reyboz.bustorino.middleware.FusedNativeLocationProvider.LocationUpdateListener
+import it.reyboz.bustorino.util.Permissions.Companion.anyLocationPermissionsGranted
+import it.reyboz.bustorino.util.Permissions.Companion.bothLocationPermissionsGranted
+import it.reyboz.bustorino.util.StopSorterByDistance
+import it.reyboz.bustorino.viewmodels.NearbyStopsViewModel
+import java.util.*
+import kotlin.math.min
+
+class NearbyStopsFragment : ScreenBaseFragment() {
+ override fun getBaseViewForSnackBar(): View? {
+ return null
+ }
+
+ enum class FragType(val num: Int) {
+ STOPS(1), ARRIVALS(2);
+
+ companion object {
+ @JvmStatic
+ fun fromNum(i: Int): FragType {
+ when (i) {
+ 1 -> return STOPS
+ 2 -> return ARRIVALS
+ else -> throw IllegalArgumentException("type not recognized")
+ }
+ }
+ }
+ }
+
+ private enum class LocationShowingStatus {
+ SEARCHING, FIRST_FIX, DISABLED, NO_PERMISSION
+ }
+
+ private var mListener: FragmentListenerMain? = null
+
+ private var fragment_type = FragType.STOPS
+
+ private lateinit var gridRecyclerView: RecyclerView
+
+ private var dataAdapter: SquareStopAdapter? = null
+ private var gridLayoutManager: AutoFitGridLayoutManager? = null
+ private var lastPosition: GPSPoint? = null
+ private var circlingProgressBar: ProgressBar? = null
+ private lateinit var flatProgressBar: ProgressBar
+
+ //protected SharedPreferences globalSharedPref;
+ //private SharedPreferences.OnSharedPreferenceChangeListener preferenceChangeListener;
+ private var messageTextView: TextView? = null
+ private var titleTextView: TextView? = null
+ private var loadingTextView: TextView? = null
+ private var scrollListener: CommonScrollListener? = null
+ private var switchButton: AppCompatButton? = null
+ private var firstLocForStops = true
+ private var firstLocForArrivals = true
+ private var stopsMaxDistance = -3
+ private var stopsMinNumber = -1
+
+ //These are useful for the case of nearby arrivals
+ private var arrivalsStopAdapter: ArrivalsStopAdapter? = null
+
+ private var currentNearbyStops = ArrayList<Stop>()
+
+ private var showingStatus = LocationShowingStatus.NO_PERMISSION
+ private var isLocationEnabled = false
+
+ private val locationUpdateListener: LocationUpdateListener = object : LocationUpdateListener {
+ override fun onLocationUpdate(location: Location) {
+ updateLocationViewModel(location)
+ }
+
+ override fun onFusedStatusChanged(isEnabled: Boolean) {
+ Log.d(DEBUG_TAG, "Location provider is enabled: " + isEnabled)
+ isLocationEnabled = isEnabled
+ if (isEnabled) {
+ setShowingStatus(LocationShowingStatus.SEARCHING)
+ } else {
+ setShowingStatus(LocationShowingStatus.DISABLED)
+ }
+ }
+ }
+ // Two different settings for the location provider
+ private val locationOptionsArrivals = FusedNativeLocationProvider.Options(5 * 1000L, 25f)
+ private val locationOptionsStops = FusedNativeLocationProvider.Options(1000L, 5f)
+
+ private var locationProvider: FusedNativeLocationProvider? = null
+
+
+ /*private val arrivalsListener: ArrivalsListener = object : ArrivalsListener {
+ override fun setProgress(completedRequests: Int, pendingRequests: Int) {
+ if (pendingRequests == 0) {
+ flatProgressBar.setIndeterminate(true)
+ flatProgressBar.setVisibility(View.GONE)
+ } else {
+ flatProgressBar.setIndeterminate(false)
+ flatProgressBar.progress = completedRequests
+ }
+ }
+
+ /*override fun onAllRequestsCancelled() {
+ if (flatProgressBar != null) flatProgressBar!!.setVisibility(View.GONE)
+ }
+
+ */
+
+ override fun showCompletedArrivals(completedPalinas: ArrayList<Palina>) {
+ showArrivalsInRecycler(completedPalinas)
+ }
+ }
+
+ */
+
+ //ViewModel
+ private val viewModel : NearbyStopsViewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ arguments?.let{
+
+ setFragmentType(FragType.fromNum(it.getInt(FRAGMENT_TYPE_KEY)))
+ }
+ //locManager = (LocationManager) requireContext().getSystemService(Context.LOCATION_SERVICE);
+ //fragmentLocationListener = new FragmentLocationListener();
+ if (getContext() != null) {
+ //globalSharedPref = getContext().getSharedPreferences(getString(R.string.mainSharedPreferences), Context.MODE_PRIVATE);
+ //globalSharedPref.registerOnSharedPreferenceChangeListener(preferenceChangeListener);
+ }
+
+ //NearbyArrivalsDownloader nearbyArrivalsDownloader = new NearbyArrivalsDownloader(getContext().getApplicationContext(), arrivalsListener);
+ locationProvider = FusedNativeLocationProvider(requireContext())
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ // Inflate the layout for this fragment
+ if (getContext() == null) throw RuntimeException()
+ val root = inflater.inflate(R.layout.fragment_nearby_stops, container, false)
+ gridRecyclerView = root.findViewById<RecyclerView>(R.id.stopGridRecyclerView)
+ gridLayoutManager = AutoFitGridLayoutManager(
+ requireContext().getApplicationContext(),
+ utils.convertDipToPixels(getContext(), COLUMN_WIDTH_DP.toFloat()).toInt()
+ )
+ gridRecyclerView.setLayoutManager(gridLayoutManager)
+ gridRecyclerView.setHasFixedSize(false)
+ circlingProgressBar = root.findViewById<ProgressBar>(R.id.circularProgressBar)
+ flatProgressBar = root.findViewById(R.id.horizontalProgressBar)
+ messageTextView = root.findViewById<TextView>(R.id.messageTextView)
+ titleTextView = root.findViewById<TextView>(R.id.titleTextView)
+ loadingTextView = root.findViewById<TextView>(R.id.positionLoadingTextView)
+ switchButton = root.findViewById<AppCompatButton>(R.id.switchButton)
+
+ scrollListener = CommonScrollListener(mListener, false)
+ switchButton!!.setOnClickListener(View.OnClickListener { v: View? -> switchFragmentType() })
+ if (BuildConfig.DEBUG) Log.d(DEBUG_TAG, "onCreateView")
+
+ val appContext = requireContext().applicationContext
+ DatabaseUpdate.watchUpdateWorkStatus(context, this){ workInfos ->
+ if (workInfos.isEmpty()) {
+ viewModel.setDBUpdateRunning(false)
+ return@watchUpdateWorkStatus
+ }
+
+ val wi = workInfos.get(0)
+ if (wi.state == WorkInfo.State.RUNNING && locationProvider!!.isRunning()) {
+ locationProvider!!.stopUpdates()
+ viewModel.setDBUpdateRunning(true)
+ } else {
+ //start the request
+ if (bothLocationPermissionsGranted(requireContext())) {
+ if (!locationProvider!!.isRunning()) {
+ startLocationUpdatesByType()
+ }
+ } else {
+ setShowingStatus(LocationShowingStatus.NO_PERMISSION)
+ }
+
+ viewModel.setDBUpdateRunning(false)
+ //actually restart request
+ }
+ }
+
+
+ if (anyLocationPermissionsGranted(appContext)) {
+ setShowingStatus(LocationShowingStatus.SEARCHING)
+ } else {
+ setShowingStatus(LocationShowingStatus.NO_PERMISSION)
+ }
+ //add location listener
+ locationProvider!!.addListener(locationUpdateListener)
+
+ return root
+ }
+
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ gridRecyclerView.setVisibility(View.INVISIBLE)
+ gridRecyclerView.addOnScrollListener(scrollListener!!)
+ //mListener?.readyGUIfor(FragmentKind.NEARBY_STOPS)
+
+ //observe the livedata
+ viewModel.stopsAtDistance.observe(getViewLifecycleOwner()) {stops ->
+ Log.d(DEBUG_TAG, "Received " + stops.size + " stops nearby")
+ var distance = viewModel.distanceMtLiveData.getValue()
+ if (distance == null) {
+ distance = 40
+ }
+ if ((stops.size < stopsMinNumber && distance <= stopsMaxDistance)) {
+ viewModel.setDistance(distance + 40)
+ //viewModel.requestStopsAtDistance(distance, true);
+ //Log.d(DEBUG_TAG, "Doubling distance now!");
+ return@observe // THIS WORKS AS AN `else`
+ }
+ if (!stops.isEmpty()) {
+ currentNearbyStops = stops
+ showStopsInViews(currentNearbyStops, lastPosition)
+ }
+ }
+
+ viewModel.downloadingArrivals.observe(viewLifecycleOwner){ running ->
+ if(!running) flatProgressBar.visibility = View.GONE
+ else flatProgressBar.visibility = View.VISIBLE
+ }
+ viewModel.progressPerc.observe(viewLifecycleOwner){ progress ->
+ flatProgressBar.isIndeterminate = false
+ flatProgressBar.progress = progress
+ flatProgressBar.max = 100
+
+ if (progress<100){
+ flatProgressBar.visibility = View.VISIBLE
+ }
+
+ }
+
+ viewModel.arrivalsDecoupled.observe(viewLifecycleOwner){ stoprouteList ->
+ if (getContext() == null) {
+ Log.e(DEBUG_TAG, "Trying to show arrivals in Recycler but we're not attached")
+ return@observe
+ }
+ if (firstLocForArrivals) {
+ arrivalsStopAdapter = ArrivalsStopAdapter(stoprouteList, mListener, context, lastPosition!!)
+ gridRecyclerView.setAdapter(arrivalsStopAdapter)
+ firstLocForArrivals = false
+ } else {
+ arrivalsStopAdapter!!.setRoutesPairListAndPosition(stoprouteList, lastPosition)
+ }
+
+ //arrivalsStopAdapter.notifyDataSetChanged();
+ showRecyclerHidingLoadMessage()
+ //if (mListener != null) mListener!!.readyGUIfor(FragmentKind.NEARBY_ARRIVALS)
+ }
+ }
+
+
+ /**
+ * Internal bit used to start location updates
+ */
+ private fun startLocationUpdatesByType() {
+ when (fragment_type) {
+ FragType.STOPS -> locationProvider!!.startUpdates(locationOptionsStops)
+ FragType.ARRIVALS -> locationProvider!!.startUpdates(locationOptionsArrivals)
+ }
+ }
+
+
+ /**
+ * Use this method to set the fragment type
+ * @param type the type, TYPE_ARRIVALS or TYPE_STOPS
+ */
+ private fun setFragmentType(type: FragType) {
+ val isChanged = fragment_type != type
+ this.fragment_type = type
+ /*switch(type){
+ case ARRIVALS:
+ TIME_INTERVAL_REQUESTS = 5*1000;
+ break;
+ case STOPS:
+ TIME_INTERVAL_REQUESTS = 1000;
+
+ }
+
+ */
+ if (isChanged) {
+ startLocationUpdatesByType()
+ setShowingStatus(LocationShowingStatus.SEARCHING)
+ }
+ }
+
+ /**
+ * Set the location in the view model if it is good
+ * @param location new location
+ */
+ private fun updateLocationViewModel(location: Location, accuracy: Float = 150f) {
+ if (location.getAccuracy() < accuracy) {
+ lastPosition = GPSPoint(location.getLatitude(), location.getLongitude())
+ //viewModel.requestStopsAtDistance(location.getLatitude(), location.getLongitude(), distance, true);
+ viewModel.setLastLocation(location)
+ }
+ }
+
+ private fun setShowingStatus(newStatus: LocationShowingStatus) {
+ var newStatus = newStatus
+ if (BuildConfig.DEBUG) Log.d(DEBUG_TAG, "Asked to set showing status : $newStatus")
+ if (newStatus == showingStatus) {
+ return
+ }
+ if (!isLocationEnabled && newStatus != LocationShowingStatus.NO_PERMISSION) {
+ Log.d(DEBUG_TAG, "asked to show status: $newStatus but the position is disabled")
+ newStatus = LocationShowingStatus.DISABLED
+ }
+
+ when (newStatus) {
+ LocationShowingStatus.FIRST_FIX -> {
+ circlingProgressBar!!.setVisibility(View.GONE)
+ loadingTextView!!.setVisibility(View.GONE)
+ gridRecyclerView.setVisibility(View.VISIBLE)
+ messageTextView!!.setVisibility(View.GONE)
+ }
+
+ LocationShowingStatus.NO_PERMISSION -> {
+ circlingProgressBar!!.setVisibility(View.GONE)
+ loadingTextView!!.setVisibility(View.GONE)
+ messageTextView!!.setText(R.string.enable_position_message_nearby)
+ messageTextView!!.setVisibility(View.VISIBLE)
+ }
+
+ LocationShowingStatus.DISABLED -> {
+ //if (showingStatus== LocationShowingStatus.SEARCHING){
+ circlingProgressBar!!.setVisibility(View.GONE)
+ loadingTextView!!.setVisibility(View.GONE)
+ //}
+ messageTextView!!.setText(R.string.enable_location_message)
+ messageTextView!!.setVisibility(View.VISIBLE)
+ }
+
+ LocationShowingStatus.SEARCHING -> {
+ circlingProgressBar!!.setVisibility(View.VISIBLE)
+ loadingTextView!!.setVisibility(View.VISIBLE)
+ gridRecyclerView.setVisibility(View.GONE)
+ messageTextView!!.setVisibility(View.GONE)
+ }
+ }
+ showingStatus = newStatus
+ }
+
+
+ override fun onAttach(context: Context) {
+ super.onAttach(context)
+ if (context is FragmentListenerMain) {
+ mListener = context as FragmentListenerMain
+ } else {
+ throw RuntimeException(
+ context
+ .toString() + " must implement OnFragmentInteractionListener"
+ )
+ }
+ Log.d(DEBUG_TAG, "OnAttach called")
+ //viewModel = ViewModelProvider(this).get<NearbyStopsViewModel?>(NearbyStopsViewModel::class.java)
+ }
+
+ override fun onPause() {
+ super.onPause()
+
+ //gridRecyclerView.setAdapter(null)
+ Log.d(DEBUG_TAG, "On paused called")
+
+ locationProvider!!.stopUpdates()
+ }
+
+ override fun onResume() {
+ super.onResume()
+ //fix view if we were showing the stops or the arrivals
+ prepareForFragmentType()
+ when (fragment_type) {
+ FragType.STOPS -> if (dataAdapter != null) {
+ //gridRecyclerView.setAdapter(dataAdapter);
+ circlingProgressBar!!.setVisibility(View.GONE)
+ loadingTextView!!.setVisibility(View.GONE)
+ }
+
+ FragType.ARRIVALS -> if (arrivalsStopAdapter != null) {
+ //gridRecyclerView.setAdapter(arrivalsStopAdapter);
+ circlingProgressBar!!.setVisibility(View.GONE)
+ loadingTextView!!.setVisibility(View.GONE)
+ }
+ }
+
+ mListener!!.enableRefreshLayout(false)
+ Log.d(DEBUG_TAG, "OnResume called")
+ if (getContext() == null) {
+ Log.e(DEBUG_TAG, "NULL CONTEXT, everything is going to crash now")
+ stopsMinNumber = 5
+ stopsMaxDistance = 600
+ return
+ }
+ //Re-read preferences
+ val shpr = PreferenceManager.getDefaultSharedPreferences(requireContext().getApplicationContext())
+ //For some reason, they are all saved as strings
+ stopsMaxDistance = shpr.getInt(getString(R.string.pref_key_radius_recents), 600)
+ var isMinStopInt = true
+ try {
+ stopsMinNumber = shpr.getInt(getString(R.string.pref_key_num_recents), 5)
+ } catch (ex: ClassCastException) {
+ isMinStopInt = false
+ }
+ if (!isMinStopInt) try {
+ stopsMinNumber = shpr.getString(getString(R.string.pref_key_num_recents), "5")!!.toInt()
+ } catch (ex: NumberFormatException) {
+ stopsMinNumber = 5
+ }
+ if (BuildConfig.DEBUG) Log.d(
+ DEBUG_TAG,
+ "Max distance for stops: $stopsMaxDistance, Min number of stops: $stopsMinNumber"
+ )
+
+ if (!locationProvider!!.isRunning()) {
+ startLocationUpdatesByType()
+ }
+ }
+
+
+
+
+ override fun onDetach() {
+ super.onDetach()
+ mListener = null
+ //if (arrivalsManager != null) arrivalsManager!!.cancelAllRequests()
+ }
+
+ /**
+ * Display the stops, or run new set of requests for arrivals
+ */
+ private fun showStopsInViews(stops: ArrayList<Stop>, location: GPSPoint?) {
+ if (stops.isEmpty()) {
+ setNoStopsLayout()
+ return
+ }
+ if (location == null) {
+ // we could do something better, but it's better to do this for now
+ return
+ }
+
+ /*var minDistance = Double.POSITIVE_INFINITY
+ for (s in stops) {
+ minDistance = min(minDistance, s.getDistanceFromLocation(location.getLatitude(), location.getLongitude()))
+ }
+
+ */
+
+
+ //quick trial to hopefully always get the stops in the correct order
+ Collections.sort<Stop?>(stops, StopSorterByDistance(location))
+ when (fragment_type) {
+ FragType.STOPS -> showStopsInRecycler(stops)
+ FragType.ARRIVALS -> {
+ //don't do anything if we're not attached
+ /*context?.let{
+ if (arrivalsManager == null) arrivalsManager =
+ NearbyArrivalsDownloader(it.applicationContext, arrivalsListener)
+ arrivalsManager!!.requestArrivalsForStops(stops)
+ }
+
+ */
+ viewModel.requestArrivalsForStops(stops)
+ }
+ }
+ }
+
+ /**
+ * To enable targeting from the Button
+ */
+ fun switchFragmentType(v: View?) {
+ switchFragmentType()
+ }
+
+ /**
+ * Call when you need to switch the type of fragment
+ */
+ private fun switchFragmentType() {
+ when (fragment_type) {
+ FragType.ARRIVALS -> setFragmentType(FragType.STOPS)
+ FragType.STOPS -> setFragmentType(FragType.ARRIVALS)
+ else -> {}
+ }
+ prepareForFragmentType()
+ //locManager.removeLocationRequestFor(fragmentLocationListener);
+ //locManager.addLocationRequestFor(fragmentLocationListener);
+ if (lastPosition != null) {
+ // we have at least one fix on the position
+ showStopsInViews(currentNearbyStops, lastPosition)
+ }
+ }
+
+ /**
+ * Prepare the views for the set fragment type
+ */
+ private fun prepareForFragmentType() {
+ if (fragment_type == FragType.STOPS) {
+ switchButton!!.text = getString(R.string.show_arrivals)
+ titleTextView!!.text = getString(R.string.nearby_stops_message)
+
+ dataAdapter?.let{ gridRecyclerView.adapter = it}
+
+ mListener?.readyGUIfor(FragmentKind.NEARBY_STOPS)
+
+ } else if (fragment_type == FragType.ARRIVALS) {
+ titleTextView!!.text = getString(R.string.nearby_arrivals_message)
+ switchButton!!.text = getString(R.string.show_stops)
+ val arrivalsSorted = viewModel.arrivalsDecoupled.value
+ arrivalsSorted?.let{
+ lastPosition?.let{pos ->
+ arrivalsStopAdapter = ArrivalsStopAdapter(it,mListener,requireContext(), pos )
+ }
+ }
+ arrivalsStopAdapter?.let{
+ gridRecyclerView.setAdapter(it)
+ }
+
+ mListener?.readyGUIfor(FragmentKind.NEARBY_ARRIVALS)
+ }
+ }
+
+ //useful methods
+ /**//// GUI METHODS //////// */
+ private fun showStopsInRecycler(stops: MutableList<Stop>?) {
+ if (dataAdapter == null) {
+ dataAdapter = SquareStopAdapter(stops, mListener, lastPosition)
+ gridRecyclerView!!.setAdapter(dataAdapter)
+ firstLocForStops = false
+ } else {
+ dataAdapter!!.setUserPosition(lastPosition)
+ dataAdapter!!.setStops(stops)
+ }
+
+ //showRecyclerHidingLoadMessage();
+ if (gridRecyclerView!!.getVisibility() != View.VISIBLE) {
+ circlingProgressBar!!.setVisibility(View.GONE)
+ loadingTextView!!.setVisibility(View.GONE)
+ gridRecyclerView!!.setVisibility(View.VISIBLE)
+ }
+ messageTextView!!.setVisibility(View.GONE)
+
+ }
+
+ private fun showArrivalsInRecycler(routesPairList: List<Pair<Stop, Route>>) {
+
+
+ }
+
+ private fun setNoStopsLayout() {
+ messageTextView!!.setVisibility(View.VISIBLE)
+ messageTextView!!.setText(R.string.no_stops_nearby)
+ circlingProgressBar!!.setVisibility(View.GONE)
+ loadingTextView!!.setVisibility(View.GONE)
+ }
+
+ /**
+ * Does exactly what is says on the tin
+ */
+ private fun showRecyclerHidingLoadMessage() {
+ if (gridRecyclerView.getVisibility() != View.VISIBLE) {
+ circlingProgressBar!!.setVisibility(View.GONE)
+ loadingTextView!!.setVisibility(View.GONE)
+ gridRecyclerView.setVisibility(View.VISIBLE)
+ }
+ messageTextView!!.setVisibility(View.GONE)
+ } /*
+ * Local locationListener, to use for the GPS
+ */
+ /*
+ class FragmentLocationListener implements LocationListenerCompat {
+
+ private long lastUpdateTime = -1;
+ public boolean isRegistered = false;
+
+ @Override
+ public void onLocationChanged(@NonNull Location location) {
+ if(viewModel==null){
+ return;
+ }
+ if(location.getAccuracy()<200) {
+
+ lastPosition = new GPSPoint(location.getLatitude(), location.getLongitude());
+ //viewModel.requestStopsAtDistance(location.getLatitude(), location.getLongitude(), distance, true);
+ viewModel.setLastLocation(location);
+ }
+ lastUpdateTime = System.currentTimeMillis();
+ //Log.d("BusTO:NearPositListen","can start request for stops: "+ !dbUpdateRunning);
+ }
+
+ @Override
+ public void onProviderEnabled(@NonNull String provider) {
+ Log.d(DEBUG_TAG, "Location provider "+provider+" enabled");
+ if(provider.equals(LocationManager.GPS_PROVIDER)){
+ setShowingStatus(LocationShowingStatus.SEARCHING);
+ }
+ }
+
+ @Override
+ public void onProviderDisabled(@NonNull String provider) {
+ Log.d(DEBUG_TAG, "Location provider "+provider+" disabled");
+ if(provider.equals(LocationManager.GPS_PROVIDER)) {
+ setShowingStatus(LocationShowingStatus.DISABLED);
+ }
+ }
+
+ @Override
+ public void onStatusChanged(@NonNull String provider, int status, @Nullable Bundle extras) {
+ LocationListenerCompat.super.onStatusChanged(provider, status, extras);
+ }
+ }
+
+ */
+
+ companion object {
+ private const val DEBUG_TAG = "NearbyStopsFragment"
+ private const val FRAGMENT_TYPE_KEY = "FragmentType"
+ const val FRAGMENT_TAG: String = "NearbyStopsFrag"
+
+ const val COLUMN_WIDTH_DP: Int = 250
+
+
+ /**
+ * Use this factory method to create a new instance of
+ * this fragment using the provided parameters.
+ * @return A new instance of fragment NearbyStopsFragment.
+ */
+ @JvmStatic
+ fun newInstance(type: FragType): NearbyStopsFragment {
+ //if(fragmentType != TYPE_STOPS && fragmentType != TYPE_ARRIVALS )
+ // throw new IllegalArgumentException("WRONG KIND OF FRAGMENT USED");
+ val fragment = NearbyStopsFragment()
+ val args = Bundle(1)
+ args.putInt(FRAGMENT_TYPE_KEY, type.num)
+ fragment.setArguments(args)
+ return fragment
+ }
+ }
+}
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/ParentFragmentManagerFromChild.kt b/app/src/main/java/it/reyboz/bustorino/fragments/ParentFragmentManagerFromChild.kt
new file mode 100644
--- /dev/null
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/ParentFragmentManagerFromChild.kt
@@ -0,0 +1,9 @@
+package it.reyboz.bustorino.fragments
+
+
+interface ParentFragmentManagerFromChild {
+
+ fun needToPopMainStackOnBack() : Boolean
+
+ fun setMainFragmentManagerTransition(yes: Boolean)
+}
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/util/RoutePositionSorter.java b/app/src/main/java/it/reyboz/bustorino/util/RoutePositionSorter.java
--- a/app/src/main/java/it/reyboz/bustorino/util/RoutePositionSorter.java
+++ b/app/src/main/java/it/reyboz/bustorino/util/RoutePositionSorter.java
@@ -17,21 +17,19 @@
*/
package it.reyboz.bustorino.util;
-import android.location.Location;
import androidx.core.util.Pair;
import android.util.Log;
import it.reyboz.bustorino.backend.*;
-import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
-public class RoutePositionSorter implements Comparator<Pair<Stop, Route>> {
+public class RoutePositionSorter implements Comparator<RouteWithStop> {
private final double latPos, longPos;
- private final double minutialmetro = 6.0/100; //v = 5km/h
- private final double distancemultiplier = 2./3;
+ public final double MINUTI_PER_METRO = 6.0/100; //v = 5km/h
+ public final double DISTANCE_MULTIPLIER = 2./3;
public RoutePositionSorter(double latitude, double longitude){
latPos = latitude;
longPos = longitude;
@@ -41,16 +39,16 @@
}
@Override
- public int compare(Pair<Stop, Route> pair1, Pair<Stop, Route> pair2) throws NullPointerException{
+ public int compare(RouteWithStop pair1, RouteWithStop pair2) throws NullPointerException{
int delta = 0;
- final Stop stop1 = pair1.first, stop2 = pair2.first;
+ final Stop stop1 = pair1.getStop(), stop2 = pair2.getStop();
double dist1 = utils.measuredistanceBetween(latPos,longPos,
stop1.getLatitude(),stop1.getLongitude());
double dist2 = utils.measuredistanceBetween(latPos,longPos,
stop2.getLatitude(),stop2.getLongitude());
- final List<Passaggio> passaggi1 = pair1.second.passaggi,
- passaggi2 = pair2.second.passaggi;
- if(passaggi1.size()<=0 || passaggi2.size()<=0){
+ final List<Passaggio> passaggi1 = pair1.getRoute().passaggi,
+ passaggi2 = pair2.getRoute().passaggi;
+ if(passaggi1.isEmpty() || passaggi2.isEmpty()){
Log.e("ArrivalsStopAdapter","Cannot compare: No arrivals in one of the stops");
} else {
Collections.sort(passaggi1);
@@ -66,7 +64,7 @@
delta = (int) passaggi1.get(0).getDifferenceMinutes(passaggi2.get(0));
}
- delta += (int)((dist1 -dist2)*minutialmetro*distancemultiplier);
+ delta += (int)((dist1 -dist2)* MINUTI_PER_METRO * DISTANCE_MULTIPLIER);
return delta;
}
diff --git a/app/src/main/java/it/reyboz/bustorino/viewmodels/NearbyStopsViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/NearbyStopsViewModel.kt
--- a/app/src/main/java/it/reyboz/bustorino/viewmodels/NearbyStopsViewModel.kt
+++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/NearbyStopsViewModel.kt
@@ -23,19 +23,145 @@
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.map
+import androidx.lifecycle.viewModelScope
+import com.android.volley.Response
import it.reyboz.bustorino.BuildConfig
-import it.reyboz.bustorino.backend.GPSPoint
-import it.reyboz.bustorino.backend.Stop
+import it.reyboz.bustorino.backend.*
+import it.reyboz.bustorino.backend.mato.MapiArrivalRequest
import it.reyboz.bustorino.data.OldDataRepository
-import java.util.ArrayList
+import it.reyboz.bustorino.util.StopSorterByDistance
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import java.util.*
+import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executors
+import java.util.concurrent.atomic.AtomicInteger
+import java.util.concurrent.atomic.AtomicLong
+import kotlin.time.Duration.Companion.seconds
class NearbyStopsViewModel(application: Application): AndroidViewModel(application) {
private val executor = Executors.newFixedThreadPool(2)
private val oldRepo = OldDataRepository(executor, application)
+ val arrivalsNearby = MutableLiveData<List<Palina>>()
+ // map of arrivals by stopID
+ private val arrivalsMapStopID = ConcurrentHashMap<String, Palina>()
+
+ val progressPerc = MutableLiveData<Int>()
+
+ val downloadingArrivals = MutableLiveData<Boolean>()
+ val lastTimeFinished = AtomicLong(0)
+ private var job : Job? = null
+
+ /*private val arrivalsListener = object : NearbyArrivalsDownloader.ArrivalsListener {
+ override fun setProgress(completedRequests: Int, pendingRequests: Int) {
+ val totalReq = completedRequests + pendingRequests
+ progressPerc.postValue( (completedRequests * 100) / totalReq )
+
+ if(pendingRequests == 0)
+ downloadingArrivals.postValue(false)
+ }
+
+ override fun onAllRequestsCancelled() {
+ downloadingArrivals.postValue(false)
+ }
+
+ override fun showCompletedArrivals(completedPalinas: ArrayList<Palina>) {
+ arrivalsNearby.postValue(completedPalinas)
+ }
+
+ }
+
+ */
+
+ //private val nearbyArrivalsDownloader = NearbyArrivalsDownloader(application,arrivalsListener )
+
+ private val volleyManager = NetworkVolleyManager.getInstance(application)
+
+ /**
+ * Response listener for the requests
+ */
+ private val responseListener = Response.Listener<Palina> { p ->
+ val key = p.ID
+ arrivalsMapStopID[key] = p
+
+ arrivalsNearby.postValue(arrivalsMapStopID.values.toList())
+
+ val c = completedRequests.incrementAndGet()
+ val r = runningRequests.decrementAndGet()
+ updateProgressPost(c,errorRequests.get(), totalReqs.get())
+ if(r==0){
+ setFinishedPost()
+ }
+ }
+
+ /**
+ * Error listener for the requests
+ */
+ private val errorListener = Response.ErrorListener { error ->
+ val e = errorRequests.incrementAndGet()
+ val r = runningRequests.decrementAndGet()
+ updateProgressPost(completedRequests.get(), e, totalReqs.get())
+ if(r==0){
+ setFinishedPost()
+ }
+ if(BuildConfig.DEBUG) Log.d(DEBUG_TAG,"query to palina, error "+ error.toString())
+ }
+
+ private fun setFinishedPost(){
+ downloadingArrivals.postValue(false)
+ //val time = System.currentTimeMillis()
+ //lastTimeFinished.set(time)
+ job?.cancel()
+ job = viewModelScope.launch {
+ delay(30.seconds)
+ launch(Dispatchers.Main) {
+ Log.d(DEBUG_TAG, "Updating arrivals from job")
+ stopsAtDistance.value?.let {
+ requestArrivalsForStops(it)
+ }
+ }
+ }
+ }
+
+ private val totalReqs = AtomicInteger(0)
+ private val completedRequests = AtomicInteger(0)
+ private val errorRequests = AtomicInteger(0)
+ private val runningRequests = AtomicInteger(0)
+
+ /**
+ * Run new batch of requests
+ */
+ fun requestArrivalsForStops(stops: List<Stop>) {
+ //nearbyArrivalsDownloader.requestArrivalsForStops(stops)
+ if(runningRequests.get() > 0) {
+ volleyManager.requestQueue.cancelAll(REQUEST_TAG)
+ }
+ val currentDate = Date()
+ val timeRange = 3600
+ val departures = 10
+ totalReqs.set(stops.size)
+ runningRequests.set(stops.size)
+ completedRequests.set(0)
+ errorRequests.set(0)
+ arrivalsMapStopID.clear()
+ for (s in stops) {
+ val req = MapiArrivalRequest(s.ID, currentDate, timeRange, departures, responseListener, errorListener)
+ req.setTag(REQUEST_TAG)
+ volleyManager.addToRequestQueue(req)
+ }
+ downloadingArrivals.value = (true)
+ }
+ private fun updateProgressPost(completed: Int, error: Int, total: Int) {
+ val done = completed + error
+ progressPerc.postValue( (done * 100) / total )
+ }
+ //// ------- LOCATION STUFF ---------
val locationLiveData = MutableLiveData<GPSPoint>()
val distanceMtLiveData = MutableLiveData<Int>(40)
@@ -150,7 +276,79 @@
}
+ val arrivalsDecoupled = arrivalsNearby.map { palinas ->
+ locationLiveData.value?.let { loc ->
+ Collections.sort(palinas, StopSorterByDistance(loc))
+ }
+
+ var routesPairList = ArrayList<RouteWithStop>(10)
+ //int maxNum = Math.min(MAX_STOPS, stopList.size());
+ for (p in palinas) {
+ //if there are no routes available, skip stop
+ if (p.queryAllRoutes().isEmpty()) continue
+ for (r in p.queryAllRoutes()) {
+ //if there are no routes, should not do anything
+ if (r.passaggi != null && !r.passaggi.isEmpty()) routesPairList.add(RouteWithStop(p, r))
+ }
+ }
+
+ val pos = locationLiveData.value
+ if(pos != null) {
+ routesPairList.sortWith { p1, p2 ->
+ comparePairsRoutesArrivals(p1,p2,pos)
+ }
+ }
+ routesPairList
+ }
+
+ fun comparePairsRoutesArrivals(pair1: RouteWithStop, pair2: RouteWithStop, pos: GPSPoint) : Int{
+ var delta = 0
+ val stop1: Stop = pair1.stop
+ val stop2: Stop = pair2.stop
+
+ val dist1 = utils.measuredistanceBetween(
+ pos.latitude, pos.longitude,
+ stop1.getLatitude()!!, stop1.getLongitude()!!
+ )
+ val dist2 = utils.measuredistanceBetween(
+ pos.latitude, pos.longitude,
+ stop2.getLatitude()!!, stop2.getLongitude()!!
+ )
+ val passaggi1 = pair1.route.passaggi
+ val passaggi2 = pair2.route.passaggi
+ if (passaggi1.size <= 0 || passaggi2.size <= 0) {
+ Log.e("ArrivalsStopAdapter", "Cannot compare: No arrivals in one of the stops")
+ } else {
+ Collections.sort(passaggi1)
+ Collections.sort(passaggi2)
+
+ /*int deltaOre = passaggi1.get(0).hh-passaggi2.get(0).hh;
+ if(deltaOre>12)
+ deltaOre -= 24;
+ else if (deltaOre<-12)
+ deltaOre += 24;
+ delta+=deltaOre*60 + passaggi1.get(0).mm-passaggi2.get(0).mm;
+
+ */
+ delta = passaggi1[0]!!.getDifferenceMinutes(passaggi2[0]!!).toInt()
+ }
+ delta += ((dist1 - dist2) * MINUTI_PER_METRO * DISTANCE_MULTIPLIER).toInt()
+
+ return delta
+
+ }
+
+ override fun onCleared() {
+ volleyManager.requestQueue.cancelAll(REQUEST_TAG)
+ super.onCleared()
+ }
+
companion object{
private const val DEBUG_TAG = "BusTO-NearbyStopVwModel"
+
+ const val REQUEST_TAG: String = "NearbyArrivals"
+
+ const val MINUTI_PER_METRO: Double = 6.0 / 100 //v = 5km/h
+ const val DISTANCE_MULTIPLIER: Double = 2.0 / 3
}
}
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_main_screen.xml b/app/src/main/res/layout/fragment_main_screen.xml
--- a/app/src/main/res/layout/fragment_main_screen.xml
+++ b/app/src/main/res/layout/fragment_main_screen.xml
@@ -41,7 +41,7 @@
android:singleLine="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="@id/leftGuide"
- app:layout_constraintEnd_toEndOf="@id/rightGuide"
+ app:layout_constraintEnd_toStartOf="@id/searchButton"
>
</EditText>
@@ -59,17 +59,12 @@
android:imeOptions="actionSearch"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="@id/leftGuide"
- app:layout_constraintEnd_toEndOf="@id/rightGuide"
+ app:layout_constraintEnd_toStartOf="@id/searchButton"
android:singleLine="true"
android:visibility="gone">
</EditText>
- <androidx.constraintlayout.widget.Guideline
- android:id="@+id/rightGuide"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- app:layout_constraintEnd_toStartOf="@id/searchButton"
- />
+
+
<ImageButton
android:id="@+id/searchButton"
style="?android:attr/borderlessButtonStyle"
@@ -89,7 +84,7 @@
android:layout_width="match_parent"
android:layout_height="8dp"
android:layout_below="@+id/QRButton"
- android:layout_marginTop="3dp"
+ android:layout_marginTop="5dp"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:indeterminateOnly="true"
@@ -99,16 +94,20 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>
-
-
+ <androidx.constraintlayout.widget.Barrier
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/barrierSearch"
+ app:barrierDirection="bottom"
+ app:constraint_referenced_ids="searchButton,QRButton,busStopSearchByIDEditText,busStopSearchByNameEditText"
+ />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="@+id/coord_layout"
android:layout_gravity="bottom|end"
- android:layout_below="@+id/searchButton"
- android:layout_marginTop="8dp"
- app:layout_constraintTop_toBottomOf="@id/searchButton"
+ android:layout_marginTop="4dp"
+ app:layout_constraintTop_toBottomOf="@id/barrierSearch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
@@ -116,21 +115,22 @@
app:layout_constraintVertical_bias="0.0"
>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
- android:id="@+id/listRefreshLayout"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
- android:animateLayoutChanges="true"
- android:visibility="gone">
-
- <FrameLayout
+ android:id="@+id/listRefreshLayout"
android:layout_width="match_parent"
- android:id="@+id/resultFrame"
- android:layout_height="fill_parent"
- android:layout_marginTop="5dp"
- android:layout_alignParentStart="true"/>
+ android:layout_height="match_parent"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:animateLayoutChanges="true"
+ android:visibility="gone">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:id="@+id/resultFrame"
+ android:layout_height="fill_parent"
+ android:layout_marginTop="5dp"
+ android:layout_alignParentStart="true"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
+
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/floatingActionButton"
android:layout_width="wrap_content"
diff --git a/app/src/main/res/layout/fragment_nearby_stops.xml b/app/src/main/res/layout/fragment_nearby_stops.xml
--- a/app/src/main/res/layout/fragment_nearby_stops.xml
+++ b/app/src/main/res/layout/fragment_nearby_stops.xml
@@ -39,8 +39,8 @@
android:layout_below="@id/titleTextView"
android:indeterminate="true"
android:visibility="gone"
- android:layout_marginStart="5dp"
- android:layout_marginEnd="5dp"
+ android:layout_marginStart="15dp"
+ android:layout_marginEnd="15dp"
/>
@@ -71,7 +71,8 @@
android:progressDrawable="@color/blue_620"
android:indeterminate="true"
android:indeterminateTint="@color/blue_620"
- android:layout_below="@+id/titleTextView" android:layout_centerHorizontal="true"/>
+ android:layout_below="@+id/titleTextView"
+ android:layout_centerHorizontal="true"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
android:id="@+id/positionLoadingTextView"
android:layout_below="@id/circularProgressBar"
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Jul 12, 05:26 (22 h, 46 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1997919
Default Alt Text
D246.1783826766.diff (114 KB)
Attached To
Mode
D246: Fix working flow to restore exactly the back navigation, update Nearby screen
Attached
Detach File
Event Timeline