diff --git a/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java b/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java --- a/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java +++ b/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java @@ -49,6 +49,8 @@ import java.util.Arrays; import java.util.Map; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.function.Consumer; import it.reyboz.bustorino.backend.Stop; import it.reyboz.bustorino.data.DBUpdateCheckWorker; @@ -70,9 +72,6 @@ private final static String TAG_FAVORITES="favorites_frag"; private Snackbar snackbar; - private boolean showingMainFragmentFromOther = false; - private boolean onCreateComplete = false; - private ServiceAlertsViewModel serviceAlertsViewModel; private FragmentKind showingFragmentKind; @@ -85,8 +84,7 @@ R.id.nav_favorites_item, () -> checkAndShowFavoritesFragment(getSupportFragmentManager(), true), - R.id.nav_home, () -> - showHomeMainFragmentFromClick(true), + R.id.nav_home, this::showHomeMainFragmentFromClick, R.id.nav_map_item, () -> requestMapFragment(true), @@ -99,6 +97,7 @@ R.id.nav_nearby, this::openNearbyStopsFragment ); + private long lastClosingAttempt = -1L; private final OnBackPressedCallback backPressedCallback = new OnBackPressedCallback(false) { @Override @@ -117,7 +116,6 @@ } }; - @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -279,10 +277,10 @@ case "favorites" -> checkAndShowFavoritesFragment(framan, false); case "lines" -> showLinesFragment(framan, false, null); case "nearby" -> createShowMainFragment(framan, MainScreenFragment.makeArgsNearby(), false); - default -> showHomeMainFragmentFromClick(false); + default -> createShowMainFragment(framan, MainScreenFragment.makeArgsButtonsScreen(), false); } } - onCreateComplete = true; + //boolean onCreateComplete = true; //last but not least, set the good default values checkApplyDefaultSettingsValues(); @@ -475,20 +473,30 @@ */ private boolean activityCustomBackPressed(){ - boolean resolved = true; var mainFragManager = getSupportFragmentManager(); + boolean resolved = mainFragManager.getBackStackEntryCount() > 0; + Fragment shownFrag = mainFragManager.findFragmentById(R.id.mainActContentFrame); - if (mDrawer.isDrawerOpen(GravityCompat.START)) + if (mDrawer.isDrawerOpen(GravityCompat.START)) { mDrawer.closeDrawer(GravityCompat.START); - else if(shownFrag != null && shownFrag.isVisible() && shownFrag.getChildFragmentManager().getBackStackEntryCount() > 0){ - if(shownFrag instanceof MainScreenFragment){ - //we have to stop the arrivals reload - ((MainScreenFragment) shownFrag).cancelReloadArrivalsIfNeeded(); + resolved = true; + } + else if (shownFrag instanceof MainScreenFragment mainFrag) { //the only case when we have to pop both stacks + mainFrag.cancelReloadArrivalsIfNeeded(); + + var chManager = mainFrag.getChildFragmentManager(); + + if(chManager.getBackStackEntryCount() > 0){ + chManager.popBackStack(); + Log.d(DEBUG_TAG, "Child back stack popped"); + resolved = true; + } else{ + Log.d(DEBUG_TAG, "No back stack on child fragment manager"); } - shownFrag.getChildFragmentManager().popBackStack(); - if(showingMainFragmentFromOther && mainFragManager.getBackStackEntryCount() > 0){ - getSupportFragmentManager().popBackStack(); - Log.d(DEBUG_TAG, "Popping main back stack also"); + boolean haveToPopMainFromFragment = mainFrag.needToPopMainStackOnBack(); + //mainFrag.getChildFragmentManager().popBackStack(); + if(haveToPopMainFromFragment){ // pops the stack + mainFragManager.popBackStack(); } } else if (getSupportFragmentManager().getBackStackEntryCount() > 0) { @@ -536,6 +544,7 @@ private void updateShowingFragmentKindInternal(@NonNull FragmentKind newKind){ if(BuildConfig.DEBUG) Log.d(DEBUG_TAG, "Updating fragment kind, new: "+newKind+", current: "+showingFragmentKind); + boolean showingMainFragmentFromOther = false; if(showingFragmentKind == null){ showingFragmentKind = newKind; showingMainFragmentFromOther = false; @@ -562,7 +571,9 @@ R.anim.slide_out // popExit )*/ .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); - if (addToBackStack) ft.addToBackStack(null); + if (addToBackStack) { + ft.addToBackStack(null); + } ft.commit(); } /** @@ -571,7 +582,6 @@ * @param arguments args for the fragment */ private static void createShowMainFragment(FragmentManager fraMan,@Nullable Bundle arguments, boolean addToBackStack){ - //var frag = MainScreenFragment.newInstance(); FragmentTransaction ft = fraMan.beginTransaction() .replace(R.id.mainActContentFrame, MainScreenFragment.class, arguments, MainScreenFragment.FRAGMENT_TAG) .setReorderingAllowed(false) @@ -585,34 +595,18 @@ if (addToBackStack) ft.addToBackStack(null); ft.commit(); } - private void showMainFragmentFromClick(@Nullable Bundle argsToCreate, boolean addToBackStack){ - FragmentManager fraMan = getSupportFragmentManager(); - Fragment fragment = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG); - final MainScreenFragment mainScreenFragment; - if (fragment==null | !(fragment instanceof MainScreenFragment)){ - createShowMainFragment(fraMan, argsToCreate, addToBackStack); - } - else if(!fragment.isVisible()){ - - mainScreenFragment = (MainScreenFragment) fragment; - showMainFragment(fraMan, mainScreenFragment, addToBackStack); - Log.d(DEBUG_TAG, "Found the main fragment"); - } else{ - mainScreenFragment = (MainScreenFragment) fragment; - } - } - - private void showHomeMainFragmentFromClick(boolean addToBackStack){ + private void showHomeMainFragmentFromClick(){ FragmentManager fraMan = getSupportFragmentManager(); var fragment = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG); if(fragment instanceof MainScreenFragment mainFrag){ + var visible = mainFrag.isVisible(); if(!mainFrag.isVisible()){ - showMainFragment(fraMan, mainFrag, addToBackStack); + showMainFragment(fraMan, mainFrag, true); } - mainFrag.showButtonsFragmentIfNotNearby(addToBackStack); + mainFrag.showButtonsFragmentIfNotNearby(true); } else{ - createShowMainFragment(fraMan, MainScreenFragment.makeArgsButtonsScreen(), addToBackStack); + createShowMainFragment(fraMan, MainScreenFragment.makeArgsButtonsScreen(), true); } } @@ -714,10 +708,7 @@ @Override public void readyGUIfor(FragmentKind fragmentType) { - MainScreenFragment mainFragmentIfVisible = getMainFragmentIfVisible(); - if (mainFragmentIfVisible!=null){ - mainFragmentIfVisible.readyGUIfor(fragmentType); - } + updateShowingFragmentKindInternal(fragmentType); Integer titleResId = null; @@ -758,38 +749,24 @@ } if(getSupportActionBar()!=null && titleResId!=null) getSupportActionBar().setTitle(titleResId); + MainScreenFragment mainFragmentIfVisible = getMainFragmentIfVisible(); + if (mainFragmentIfVisible!=null){ + mainFragmentIfVisible.readyGUIfor(fragmentType); + } } @Override public void requestArrivalsForStopID(String ID) { - //register if the request came from the main fragment or not - MainScreenFragment probableFragment = getMainFragmentIfVisible(); - - // this has some contorted logic, but it works - if (probableFragment == null){ - FragmentManager fraMan = getSupportFragmentManager(); - Fragment fragment = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG); - Log.d(DEBUG_TAG, "Requested main fragment, not visible. Search by TAG returned: "+fragment); - if(fragment!=null){ - //the fragment is there but not shown - probableFragment = (MainScreenFragment) fragment; - // set the flag - probableFragment.setSuppressArrivalsReload(true); - showMainFragment(fraMan, probableFragment, true); - probableFragment.requestArrivalsForStopID(ID); - } else { - // we have no fragment - //if onCreate is complete, then we are not asking for the first showing fragment - final Bundle args = MainScreenFragment.makeArgsArrivals(ID); - boolean addtobackstack = onCreateComplete; - createShowMainFragment(fraMan, args ,addtobackstack); - } - } else { - //the MainScreeFragment is shown, nothing to do - probableFragment.requestArrivalsForStopID(ID); + Consumer consumer = fragment -> { + fragment.setSuppressArrivalsReload(true); + fragment.requestArrivalsForStopID(ID); + }; + boolean done = getMainFragmentAndDoStuff(consumer); + if(!done){ + //create the fragment + final Bundle args = MainScreenFragment.makeArgsArrivals(ID); + createShowMainFragment(getSupportFragmentManager(), args ,true); } - - mNavView.setCheckedItem(R.id.nav_home); } @Override public void openLineFromStop(String routeGtfsId, @Nullable String stopIDFrom){ @@ -802,6 +779,23 @@ } + private boolean getMainFragmentAndDoStuff(Consumer consumer){ + FragmentManager fraMan = getSupportFragmentManager(); + var frag = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG); + if( frag instanceof MainScreenFragment mainFrag){ + if(!mainFrag.isVisible()) { + showMainFragment(fraMan, mainFrag, true); + mainFrag.setMainFragmentManagerTransition(true); + } else{ + mainFrag.setMainFragmentManagerTransition(false); + } + consumer.accept(mainFrag); + return true; + } else{ + return false; + } + } + @Override public void openLineFromVehicle(String routeGtfsId, @Nullable String optionalPatternId, @Nullable Bundle args) { @@ -814,17 +808,9 @@ @Override public void openNearbyStopsFragment() { - FragmentManager fraMan = getSupportFragmentManager(); - var fragment = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG); - if(fragment instanceof MainScreenFragment mainFrag){ - if(!mainFrag.isVisible()){ - showMainFragment(fraMan, mainFrag, true); - } - mainFrag.openNearbyStopsFragment(); - } else{ - // there is no fragment and it is not visible - // add to back stack the main fragment, as the NearbyStopsFragment will not be added - createShowMainFragment(fraMan, MainScreenFragment.makeArgsNearby(), true); + boolean done = getMainFragmentAndDoStuff(MainScreenFragment::openNearbyStopsFragment); + if(!done){ + createShowMainFragment(getSupportFragmentManager(), MainScreenFragment.makeArgsNearby(), true); } } diff --git a/app/src/main/java/it/reyboz/bustorino/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 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 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 resultList, String query, boolean addToBackStack){ - listenerMain.hideKeyboard(); + mainFragment.hideKeyboard(); StopListFragment listfragment = StopListFragment.newInstance(query); if(managerWeakRef.get()==null) { //SOMETHING WENT VERY WRONG @@ -143,6 +156,8 @@ } attachFragmentToContainer(managerWeakRef.get(), listfragment, "search_"+query, false, addToBackStack); + //DO NOT DO THE SAME ON THE ARRIVALS (the call goes through MainActivity) + setMainFragmentManagerTransition(false); listfragment.setStopList(resultList); //listenerMain.readyGUIfor(FragmentKind.STOPS); toggleSpinner(false); @@ -154,7 +169,7 @@ * @param on new status of spinner system */ public void toggleSpinner(boolean on){ - listenerMain.toggleSpinner(on); + mainFragment.toggleSpinner(on); } /** diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java --- a/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java +++ b/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java @@ -49,8 +49,10 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton; import java.util.Map; +import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.LinkedBlockingQueue; +import it.reyboz.bustorino.BuildConfig; import it.reyboz.bustorino.R; import it.reyboz.bustorino.backend.*; import it.reyboz.bustorino.util.Permissions; @@ -65,7 +67,7 @@ * Use the {@link MainScreenFragment#newInstance} factory method to * create an instance of this fragment. */ -public class MainScreenFragment extends BarcodeFragment implements FragmentListenerMain{ +public class MainScreenFragment extends BarcodeFragment implements FragmentListenerMain, ParentFragmentManagerFromChild{ private static final String SAVED_FRAGMENT="saved_fragment"; @@ -140,7 +142,8 @@ private CoordinatorLayout coordLayout; //this is really a hackish thing, but it works - private LinkedBlockingQueue thingsToDoOnStart = new LinkedBlockingQueue<>(); + private final LinkedBlockingQueue 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/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