Page MenuHomeGitPull.it

D246.1783826764.diff
No OneTemporary

Authored By
Unknown
Size
116 KB
Referenced Files
None
Subscribers
None

D246.1783826764.diff

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,11 +72,8 @@
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;
+ //private FragmentKind showingFragmentKind;
private final Map<Integer, Runnable> menuActions = Map.of(
R.id.drawer_action_settings, () -> {
@@ -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) {
@@ -533,9 +541,11 @@
Log.e(DEBUG_TAG, "Asked to show the snackbar but the baseView is null");
}
}
+ /*
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;
@@ -546,6 +556,8 @@
}
}
+ */
+
/**
* Show the actual fragment by adding it to the backstack
* @param fraMan the fragmentManager
@@ -562,7 +574,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 +585,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 +598,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,11 +711,8 @@
@Override
public void readyGUIfor(FragmentKind fragmentType) {
- MainScreenFragment mainFragmentIfVisible = getMainFragmentIfVisible();
- if (mainFragmentIfVisible!=null){
- mainFragmentIfVisible.readyGUIfor(fragmentType);
- }
- updateShowingFragmentKindInternal(fragmentType);
+ //updateShowingFragmentKindInternal(fragmentType);
+ if(BuildConfig.DEBUG) Log.d(DEBUG_TAG, "readyGUIfor fragment kind: " + fragmentType);
Integer titleResId = null;
switch (fragmentType){
@@ -758,38 +752,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 +782,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 +811,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/LinesGridShowingFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/LinesGridShowingFragment.kt
--- a/app/src/main/java/it/reyboz/bustorino/fragments/LinesGridShowingFragment.kt
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/LinesGridShowingFragment.kt
@@ -90,7 +90,8 @@
}
private val routeClickListener = RouteAdapter.ItemClicker {
- fragmentListener.openLineFromStop(it.gtfsId, null)
+ //fragmentListener.openLineFromStop(it.gtfsId, null)
+ openLine(it)
}
private val arrows = HashMap<String, ImageView>()
private val durations = HashMap<String, Long>()
@@ -223,6 +224,10 @@
viewModel.setLineQuery(textSearch)
}
+ fun openLine(route: GtfsRoute){
+ fragmentListener.openLineFromStop(route.gtfsId, null)
+ }
+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val menuHost: MenuHost = requireActivity()
@@ -232,7 +237,8 @@
//create new item click listener every time
val adapter = RouteOnlyLineAdapter(routesNames){ pos, _ ->
val r = routes[pos]
- fragmentListener.openLineFromStop(r.gtfsId, null)
+ //fragmentListener.openLineFromStop(r.gtfsId, null)
+ openLine(r)
}
favoritesRecyclerView.adapter = adapter
}
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/LinesGridShowingViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesGridShowingViewModel.kt
--- a/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesGridShowingViewModel.kt
+++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesGridShowingViewModel.kt
@@ -77,7 +77,12 @@
}
fun setFavoritesLinesIDs(linesIds: HashSet<String>){
- favoritesLinesIDs.value = linesIds
+ val sameSet = favoritesLinesIDs.value?.let{
+ linesIds.containsAll(it) && it.containsAll(linesIds)
+ } ?: false
+ if(!sameSet)// trigger LiveData
+ favoritesLinesIDs.value = linesIds
+
}
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

Mime Type
text/plain
Expires
Sun, Jul 12, 05:26 (22 h, 46 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1997918
Default Alt Text
D246.1783826764.diff (116 KB)

Event Timeline