commit b2441546f0da8567ffc6b184648b8399e794722e Author: Fabio Mazza Date: Thu May 21 17:53:04 2026 +0200 Refine interface to show nearby fragment in the nav bar as well Summary: Use Flex layout manager also for no arrivals lines, enable click on the line Reviewers: #libre_busto_hackers, valerio.bozzolan Reviewed By: #libre_busto_hackers, valerio.bozzolan Subscribers: valerio.bozzolan Project Tags: #libre_busto Differential Revision: https://gitpull.it/D245 diff --git a/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java b/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java index c4ee229..e90e6df 100644 --- a/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java +++ b/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java @@ -48,6 +48,7 @@ import com.google.android.material.navigation.NavigationView; import com.google.android.material.snackbar.Snackbar; import java.util.Arrays; +import java.util.Map; import it.reyboz.bustorino.backend.Stop; import it.reyboz.bustorino.data.DBUpdateCheckWorker; @@ -73,6 +74,30 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen private boolean onCreateComplete = false; private ServiceAlertsViewModel serviceAlertsViewModel; + private FragmentKind showingFragmentKind; + + private final Map menuActions = Map.of( + R.id.drawer_action_settings, () -> { + Log.d("MAINBusTO", "Pressed button preferences"); + startActivity(new Intent(this, ActivitySettings.class)); + }, + + R.id.nav_favorites_item, () -> + checkAndShowFavoritesFragment(getSupportFragmentManager(), true), + + R.id.nav_home, () -> + showHomeMainFragmentFromClick(true), + + R.id.nav_map_item, () -> + requestMapFragment(true), + + R.id.nav_lines_item, () -> + showLinesFragment(getSupportFragmentManager(), true, null), + + R.id.drawer_action_info, () -> + startActivity(new Intent(this, ActivityAbout.class)), + R.id.nav_nearby, this::openNearbyStopsFragment + ); private long lastClosingAttempt = -1L; private final OnBackPressedCallback backPressedCallback = new OnBackPressedCallback(false) { @@ -99,7 +124,7 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen Log.d(DEBUG_TAG, "onCreate, savedInstanceState is: "+savedInstanceState); setContentView(R.layout.activity_principal); serviceAlertsViewModel = new ViewModelProvider(this).get(ServiceAlertsViewModel.class); - + //Use LiveModel to sync fragment state /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { getWindow().setNavigationBarContrastEnforced(false); } @@ -249,14 +274,12 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen }else if (savedInstanceState==null) { var framan = getSupportFragmentManager(); //we are not restarting the activity from nothing - if (vl.equals("map")) { - requestMapFragment(false); - } else if (vl.equals("favorites")) { - checkAndShowFavoritesFragment(framan, false); - } else if (vl.equals("lines")) { - showLinesFragment(framan, false, null); - } else { - showMainFragmentFromClick(false); + switch (vl){ + case "map" -> {requestMapFragment(false);} + case "favorites" -> checkAndShowFavoritesFragment(framan, false); + case "lines" -> showLinesFragment(framan, false, null); + case "nearby" -> createShowMainFragment(framan, MainScreenFragment.makeArgsNearby(), false); + default -> showHomeMainFragmentFromClick(false); } } onCreateComplete = true; @@ -347,38 +370,15 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen private void setupDrawerContent(NavigationView navigationView) { navigationView.setNavigationItemSelectedListener( menuItem -> { - if (menuItem.getItemId() == R.id.drawer_action_settings) { - Log.d("MAINBusTO", "Pressed button preferences"); - closeDrawerIfOpen(); - startActivity(new Intent(ActivityPrincipal.this, ActivitySettings.class)); - return true; - } else if(menuItem.getItemId() == R.id.nav_favorites_item){ - closeDrawerIfOpen(); - //get Fragment - checkAndShowFavoritesFragment(getSupportFragmentManager(), true); - return true; - } else if(menuItem.getItemId() == R.id.nav_arrivals){ - closeDrawerIfOpen(); - showMainFragmentFromClick(true); - return true; - } else if(menuItem.getItemId() == R.id.nav_map_item){ + int menuId = menuItem.getItemId(); + if( menuActions.containsKey(menuId)){ closeDrawerIfOpen(); - requestMapFragment(true); - return true; - } else if (menuItem.getItemId() == R.id.nav_lines_item) { - closeDrawerIfOpen(); - showLinesFragment(getSupportFragmentManager(), true,null); - return true; - } else if(menuItem.getItemId() == R.id.drawer_action_info) { - closeDrawerIfOpen(); - startActivity(new Intent(ActivityPrincipal.this, ActivityAbout.class)); + var runnable = menuActions.get(menuId); + if(runnable!=null) runnable.run(); return true; + } else{ + return false; } - //selectDrawerItem(menuItem); - Log.d(DEBUG_TAG, "pressed item "+menuItem); - - return true; - }); } @@ -451,10 +451,7 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { - int[] cases = {R.id.nav_arrivals, R.id.nav_favorites_item}; Log.d(DEBUG_TAG, "Item pressed"); - - if (item.getItemId() == android.R.id.home) { mDrawer.openDrawer(GravityCompat.START); return true; @@ -479,24 +476,29 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen private boolean activityCustomBackPressed(){ boolean resolved = true; - Fragment shownFrag = getSupportFragmentManager().findFragmentById(R.id.mainActContentFrame); + var mainFragManager = getSupportFragmentManager(); + Fragment shownFrag = mainFragManager.findFragmentById(R.id.mainActContentFrame); if (mDrawer.isDrawerOpen(GravityCompat.START)) mDrawer.closeDrawer(GravityCompat.START); else if(shownFrag != null && shownFrag.isVisible() && shownFrag.getChildFragmentManager().getBackStackEntryCount() > 0){ - //if we have been asked to show a stop from another fragment, we should go back even in the main if(shownFrag instanceof MainScreenFragment){ //we have to stop the arrivals reload ((MainScreenFragment) shownFrag).cancelReloadArrivalsIfNeeded(); } shownFrag.getChildFragmentManager().popBackStack(); - if(showingMainFragmentFromOther && getSupportFragmentManager().getBackStackEntryCount() > 0){ + if(showingMainFragmentFromOther && mainFragManager.getBackStackEntryCount() > 0){ getSupportFragmentManager().popBackStack(); Log.d(DEBUG_TAG, "Popping main back stack also"); } } else if (getSupportFragmentManager().getBackStackEntryCount() > 0) { - getSupportFragmentManager().popBackStack(); - Log.d(DEBUG_TAG, "Popping main frame backstack for fragments"); + mainFragManager.popBackStack(); + var newFrag = mainFragManager.findFragmentById(R.id.mainActContentFrame); + if(newFrag != null) { + int backStackCount = newFrag.getChildFragmentManager().getBackStackEntryCount(); + Log.d(DEBUG_TAG, "new fragment is "+newFrag.getClass().getSimpleName() +", count of the backstack: "+backStackCount); + } + Log.d(DEBUG_TAG, "Popping main backstack"); } else{ resolved = false; @@ -531,6 +533,18 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen 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); + if(showingFragmentKind == null){ + showingFragmentKind = newKind; + showingMainFragmentFromOther = false; + } else if(newKind != showingFragmentKind) { + showingMainFragmentFromOther = ( + FragmentKind.getSuperKind(newKind) != FragmentKind.getSuperKind(showingFragmentKind)); + showingFragmentKind = newKind; + } + } /** * Show the actual fragment by adding it to the backstack @@ -589,8 +603,17 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen } } - private void showMainFragmentFromClick(boolean addToBackStack){ - showMainFragmentFromClick(MainScreenFragment.makeArgsButtonsScreen(), addToBackStack); + private void showHomeMainFragmentFromClick(boolean addToBackStack){ + FragmentManager fraMan = getSupportFragmentManager(); + var fragment = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG); + if(fragment instanceof MainScreenFragment mainFrag){ + if(!mainFrag.isVisible()){ + showMainFragment(fraMan, mainFrag, addToBackStack); + } + mainFrag.showButtonsFragmentIfNotNearby(addToBackStack); + } else{ + createShowMainFragment(fraMan, MainScreenFragment.makeArgsButtonsScreen(), addToBackStack); + } } private void requestMapFragment(final boolean allowReturn){ @@ -667,7 +690,10 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen @Override public void showFloatingActionButton(boolean yes) { - //TODO + var frag = getMainFragmentIfVisible(); + if(frag!=null){ + frag.showFloatingActionButton(yes); + } } /* @@ -692,6 +718,8 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen if (mainFragmentIfVisible!=null){ mainFragmentIfVisible.readyGUIfor(fragmentType); } + updateShowingFragmentKindInternal(fragmentType); + Integer titleResId = null; switch (fragmentType){ case MAP: @@ -704,18 +732,24 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen break; case ARRIVALS: titleResId = R.string.nav_arrivals_text; - mNavView.setCheckedItem(R.id.nav_arrivals); + mNavView.setCheckedItem(R.id.nav_home); + //TODO: Figure out way to change title + //mNavView.getCheckedItem().setTitle(R.string.nav_arrivals_text); break; case STOPS: titleResId = R.string.stop_search_view_title; - mNavView.setCheckedItem(R.id.nav_arrivals); + mNavView.setCheckedItem(R.id.nav_home); break; case MAIN_SCREEN_FRAGMENT: + case HOME_BUTTONS: + titleResId=R.string.app_name_full; + mNavView.setCheckedItem(R.id.nav_home); + //mNavView.getCheckedItem().setTitle(R.string.nav_home_text); + break; case NEARBY_STOPS: case NEARBY_ARRIVALS: - case HOME_BUTTONS: titleResId=R.string.app_name_full; - mNavView.setCheckedItem(R.id.nav_arrivals); + mNavView.setCheckedItem(R.id.nav_nearby); break; case LINES: titleResId=R.string.lines; @@ -730,9 +764,9 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen public void requestArrivalsForStopID(String ID) { //register if the request came from the main fragment or not MainScreenFragment probableFragment = getMainFragmentIfVisible(); - showingMainFragmentFromOther = (probableFragment==null); - if (showingMainFragmentFromOther){ + // 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); @@ -755,23 +789,21 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen probableFragment.requestArrivalsForStopID(ID); } - mNavView.setCheckedItem(R.id.nav_arrivals); + mNavView.setCheckedItem(R.id.nav_home); } @Override public void openLineFromStop(String routeGtfsId, @Nullable String stopIDFrom){ - readyGUIfor(FragmentKind.LINES); - FragmentTransaction tr = getSupportFragmentManager().beginTransaction(); tr.replace(R.id.mainActContentFrame, LinesDetailFragment.class, LinesDetailFragment.Companion.makeArgs(routeGtfsId, stopIDFrom)); tr.addToBackStack("LineFromStop-"+routeGtfsId); tr.commit(); + } @Override public void openLineFromVehicle(String routeGtfsId, @Nullable String optionalPatternId, @Nullable Bundle args) { - readyGUIfor(FragmentKind.LINES); FragmentTransaction tr = getSupportFragmentManager().beginTransaction(); tr.replace(R.id.mainActContentFrame, LinesDetailFragment.class, @@ -786,7 +818,7 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen var fragment = fraMan.findFragmentByTag(MainScreenFragment.FRAGMENT_TAG); if(fragment instanceof MainScreenFragment mainFrag){ if(!mainFrag.isVisible()){ - showMainFragment(fraMan, mainFrag, false); + showMainFragment(fraMan, mainFrag, true); } mainFrag.openNearbyStopsFragment(); } else{ diff --git a/app/src/main/java/it/reyboz/bustorino/adapters/RecyclerViewMargin.java b/app/src/main/java/it/reyboz/bustorino/adapters/RecyclerViewMargin.java index d5a38b7..d765c39 100644 --- a/app/src/main/java/it/reyboz/bustorino/adapters/RecyclerViewMargin.java +++ b/app/src/main/java/it/reyboz/bustorino/adapters/RecyclerViewMargin.java @@ -80,6 +80,14 @@ public class RecyclerViewMargin extends RecyclerView.ItemDecoration { outRect.right = margin; sb.append("right "); } + if(position % columns != 0){ + outRect.left = margin; + sb.append("left "); + } + if (position >= columns){ + outRect.top = margin; + sb.append("top "); + } int row = (int)((double) position / columns) ; if(nrows == -2 || row < nrows-1){ outRect.bottom = margin; diff --git a/app/src/main/java/it/reyboz/bustorino/adapters/RouteOnlyLineAdapter.kt b/app/src/main/java/it/reyboz/bustorino/adapters/RouteOnlyLineAdapter.kt index d12e0e8..83470b9 100644 --- a/app/src/main/java/it/reyboz/bustorino/adapters/RouteOnlyLineAdapter.kt +++ b/app/src/main/java/it/reyboz/bustorino/adapters/RouteOnlyLineAdapter.kt @@ -4,35 +4,28 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView +import androidx.cardview.widget.CardView import androidx.recyclerview.widget.RecyclerView import it.reyboz.bustorino.R import it.reyboz.bustorino.backend.FiveTNormalizer -import it.reyboz.bustorino.backend.Palina -import java.lang.ref.WeakReference class RouteOnlyLineAdapter (val routeNames: List, onItemClick: OnClick?) : RecyclerView.Adapter() { - private val clickreference: WeakReference? - init { - clickreference = if(onItemClick!=null) WeakReference(onItemClick) else null - } + private val clickreference = onItemClick + /** * Provide a reference to the type of views that you are using * (custom ViewHolder) */ class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { - val textView: TextView + val textView: TextView = view.findViewById(R.id.routeBallID) + val cardView: CardView = view.findViewById(R.id.headerCardView) - init { - // Define click listener for the ViewHolder's View - textView = view.findViewById(R.id.routeBallID) - } } - constructor(palina: Palina, showOnlyEmpty: Boolean): this(palina.routesNamesWithNoPassages, null) // Create new views (invoked by the layout manager) override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder { @@ -50,8 +43,8 @@ class RouteOnlyLineAdapter (val routeNames: List, // contents of the view with that element // SHOW "STAR" as "ST" viewHolder.textView.text = FiveTNormalizer.filterFullStarName(routeNames[position]) - viewHolder.itemView.setOnClickListener{ - clickreference?.get()?.onItemClick(position, routeNames[position]) + viewHolder.cardView.setOnClickListener{ + clickreference?.onItemClick(position, routeNames[position]) } } diff --git a/app/src/main/java/it/reyboz/bustorino/backend/Palina.java b/app/src/main/java/it/reyboz/bustorino/backend/Palina.java index 4031fb5..8ecafd5 100644 --- a/app/src/main/java/it/reyboz/bustorino/backend/Palina.java +++ b/app/src/main/java/it/reyboz/bustorino/backend/Palina.java @@ -431,6 +431,10 @@ public class Palina extends Stop implements Parcelable { return mList; } + public List getRoutesWithNoPassages(){ + return routes.stream().filter(r -> r.numPassaggi()==0).toList(); + } + private static String pick(String a, String b) { return (a != null && !a.isEmpty()) ? a : b; } diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.kt index 6b7854d..fcca08b 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.kt @@ -1,6 +1,6 @@ /* BusTO - Fragments components - Copyright (C) 2018 Fabio Mazza + Copyright (C) 2018-2026 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 @@ -37,6 +37,7 @@ import androidx.recyclerview.widget.RecyclerView import it.reyboz.bustorino.R import it.reyboz.bustorino.adapters.PalinaAdapter import it.reyboz.bustorino.adapters.PalinaAdapter.PalinaClickListener +import it.reyboz.bustorino.adapters.RouteAdapter import it.reyboz.bustorino.adapters.RouteOnlyLineAdapter import it.reyboz.bustorino.backend.* import it.reyboz.bustorino.backend.DBStatusManager.OnDBUpdateStatusChangeListener @@ -89,8 +90,8 @@ class ArrivalsFragment : ResultBaseFragment(), LoaderManager.LoaderCallbacks = ArrayList() private val arrivalsViewModel : ArrivalsViewModel by viewModels() - private var reloadOnResume = true + private var routesNoPassages = listOf() fun getStopID() = stopID @@ -113,19 +114,22 @@ class ArrivalsFragment : ResultBaseFragment(), LoaderManager.LoaderCallbacks @@ -324,10 +335,8 @@ class ArrivalsFragment : ResultBaseFragment(), LoaderManager.LoaderCallbacks updateStarIcon(isFavorite) }) - return root } - private fun showShortToast(id: Int) = showToastMessage(id,true) private fun showFetcherMessage(id: Int, source: Source?){ @@ -513,14 +522,20 @@ class ArrivalsFragment : ResultBaseFragment(), LoaderManager.LoaderCallbacks sorter.compare(r1.displayCode, r2.displayCode) } + noArrivalsAdapter = RouteOnlyLineAdapter(routesNoPassages.map{r->r.displayCode}, ){ idx,_ -> + val route = routesNoPassages[idx] + showRoutesInLinesFragment(route) + + } noArrivalsRecyclerView.adapter = noArrivalsAdapter noArrivalsRecyclerView.visibility = View.VISIBLE diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/ButtonsFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/ButtonsFragment.kt index 3ae55e8..8ae3d11 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/ButtonsFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/ButtonsFragment.kt @@ -1,3 +1,20 @@ +/* + BusTO - Fragments components + Copyright (C) 2026 Fabio Mazza + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ package it.reyboz.bustorino.fragments import android.content.Context @@ -10,6 +27,7 @@ import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import com.google.android.material.card.MaterialCardView @@ -42,7 +60,7 @@ class ButtonsFragment : BarcodeFragment() { } } private val marginHoriz = 30 - private val margin = 22 + private val margin = 11 override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -50,8 +68,10 @@ class ButtonsFragment : BarcodeFragment() { ): View? { // Inflate the layout for this fragment val root = inflater.inflate(R.layout.fragment_buttons, container, false) + + // this is the actual list of the buttons items = listOf( - CardMenuItem(CardAction.NEARBY, getString(R.string.nearby_message_home_card), R.drawable.compass_3_fill), + CardMenuItem(CardAction.NEARBY, getString(R.string.near_me_title), R.drawable.compass_3_fill), CardMenuItem(CardAction.MAP, getString(R.string.map), R.drawable.map), CardMenuItem(CardAction.FAVORITES_STOPS, getString(R.string.action_favorites), R.drawable.ic_star_filled_white), CardMenuItem(CardAction.LINES, getString(R.string.lines), R.drawable.ic_moving_emph), diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/FavoritesFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/FavoritesFragment.java index b52c78c..d5476b5 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/FavoritesFragment.java +++ b/app/src/main/java/it/reyboz/bustorino/fragments/FavoritesFragment.java @@ -161,6 +161,7 @@ public class FavoritesFragment extends ScreenBaseFragment { showStops(new ArrayList<>()); return root; } + @Override public void onAttach(@NonNull Context context) { super.onAttach(context); diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/FragmentKind.java b/app/src/main/java/it/reyboz/bustorino/fragments/FragmentKind.java index 10d6acb..15ff058 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/FragmentKind.java +++ b/app/src/main/java/it/reyboz/bustorino/fragments/FragmentKind.java @@ -17,7 +17,19 @@ */ package it.reyboz.bustorino.fragments; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + public enum FragmentKind { STOPS,ARRIVALS,FAVORITES,NEARBY_STOPS,NEARBY_ARRIVALS, MAP, MAIN_SCREEN_FRAGMENT, - LINES, HOME_BUTTONS + LINES, HOME_BUTTONS; + + @NonNull + public static FragmentKind getSuperKind(@NonNull FragmentKind kind){ + return switch (kind) { + case STOPS, ARRIVALS, NEARBY_STOPS, NEARBY_ARRIVALS, HOME_BUTTONS -> + MAIN_SCREEN_FRAGMENT; + default -> kind; + }; + } } diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt index 82fe686..59bbe00 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt @@ -281,6 +281,67 @@ class LinesDetailFragment() : GeneralMapLibreFragment() { LivePositionsDialogFragment().show(parentFragmentManager, "LivePositionsDialog") } //set + + lineInfoButton.setOnClickListener { + AlertsDialogFragment(lineID).show(parentFragmentManager, "Alerts-Line$lineID") + } + /* + + */ + + Log.d(DEBUG_TAG,"Data ${viewModel.stopsForPatternLiveData.value}") + + //listeners + patternsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(p0: AdapterView<*>?, p1: View?, position: Int, p3: Long) { + val currentShownPattern = patternShown?.pattern + val patternWithStops = currentPatterns[position] + + Log.d(DEBUG_TAG, "request stops for pattern ${patternWithStops.pattern.code}") + setPatternAndReqStops(patternWithStops) + + if(mapView?.visibility == View.VISIBLE) { + //Clear buses if we are changing direction + currentShownPattern?.let { patt -> + if(patt.directionId != patternWithStops.pattern.directionId){ + stopAnimations() + updatesByVehDict.clear() + updatePositionsIcons(true) + livePositionsViewModel.retriggerPositionUpdate() + } + if (shownStopInBottomSheet!=null){ + //check if the stop is inside the new pattern + /*val s = shownStopInBottomSheet!! + val newPatternStops = patternWithStops.stopsIndices + val filterPStops = newPatternStops.filter { ps -> ps.stopGtfsId == "gtt:${s.ID}" } + if (filterPStops.isEmpty()){ + hideStopOrBusBottomSheet() + } + */ + // do another thing, just close the stop when the pattern is changed + if (patt.code != patternWithStops.pattern.code){ + hideStopOrBusBottomSheet() + } + } + } + } + livePositionsViewModel.setGtfsLineToFilterPos(lineID, patternWithStops.pattern) + + } + + override fun onNothingSelected(p0: AdapterView<*>?) { + } + } + Log.d(DEBUG_TAG, "Views created!") + + observeStatusLivePositions() + + return rootView + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + //reflect UI //INITIALIZE VIEW MODELS viewModel.setRouteIDQuery(lineID) livePositionsViewModel.setGtfsLineToFilterPos(lineID, null) @@ -333,7 +394,7 @@ class LinesDetailFragment() : GeneralMapLibreFragment() { //TODO: Decide if we should follow the camera view given by the previous screen (probably the map fragment) // use !restoredCameraInMap to do so - // val shouldZoom = (shownStopInBottomSheet == null) //use this if we want to avoid zoom when we're keeping the stop open + // val shouldZoom = (shownStopInBottomSheet == null) //use this if we want to avoid zoom when we're keeping the stop open displayPatternWithStopsOnMap(pattern, stops, true) } else { if(stopsRecyclerView.visibility==View.VISIBLE) { @@ -348,7 +409,7 @@ class LinesDetailFragment() : GeneralMapLibreFragment() { activity?.supportFragmentManager?.popBackStack() return@observe } - descripTextView.text = route.longName + descripTextView.text = route.longName descripTextView.visibility = View.VISIBLE } mapStateViewModel.locationUserActive.observe(viewLifecycleOwner) { @@ -365,63 +426,7 @@ class LinesDetailFragment() : GeneralMapLibreFragment() { } else lineInfoButton.visibility = View.GONE } - lineInfoButton.setOnClickListener { - AlertsDialogFragment(lineID).show(parentFragmentManager, "Alerts-Line$lineID") - } - /* - - */ - - Log.d(DEBUG_TAG,"Data ${viewModel.stopsForPatternLiveData.value}") - - //listeners - patternsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onItemSelected(p0: AdapterView<*>?, p1: View?, position: Int, p3: Long) { - val currentShownPattern = patternShown?.pattern - val patternWithStops = currentPatterns[position] - - Log.d(DEBUG_TAG, "request stops for pattern ${patternWithStops.pattern.code}") - setPatternAndReqStops(patternWithStops) - - if(mapView?.visibility == View.VISIBLE) { - //Clear buses if we are changing direction - currentShownPattern?.let { patt -> - if(patt.directionId != patternWithStops.pattern.directionId){ - stopAnimations() - updatesByVehDict.clear() - updatePositionsIcons(true) - livePositionsViewModel.retriggerPositionUpdate() - } - if (shownStopInBottomSheet!=null){ - //check if the stop is inside the new pattern - /*val s = shownStopInBottomSheet!! - val newPatternStops = patternWithStops.stopsIndices - val filterPStops = newPatternStops.filter { ps -> ps.stopGtfsId == "gtt:${s.ID}" } - if (filterPStops.isEmpty()){ - hideStopOrBusBottomSheet() - } - */ - // do another thing, just close the stop when the pattern is changed - if (patt.code != patternWithStops.pattern.code){ - hideStopOrBusBottomSheet() - } - } - } - } - livePositionsViewModel.setGtfsLineToFilterPos(lineID, patternWithStops.pattern) - - } - - override fun onNothingSelected(p0: AdapterView<*>?) { - } - } - Log.d(DEBUG_TAG, "Views created!") - - observeStatusLivePositions() - - return rootView } - // ------------- UI switch stuff --------- private fun hideMapAndShowStopList(){ diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/LinesFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/LinesFragment.kt index 34a4fb4..dcabf16 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/LinesFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/LinesFragment.kt @@ -127,40 +127,7 @@ class LinesFragment : ScreenBaseFragment() { Log.d(DEBUG_TAG, "OnCreateView, selected line spinner pos: ${linesSpinner.selectedItemPosition}") Log.d(DEBUG_TAG, "OnCreateView, selected patterns spinner pos: ${patternsSpinner.selectedItemPosition}") - //set requests - viewModel.routesGTTLiveData.observe(viewLifecycleOwner) { - setRoutes(it) - } - - viewModel.patternsWithStopsByRouteLiveData.observe(viewLifecycleOwner){ - patterns -> - run { - selectedPatterns = patterns.sortedBy { p-> p.pattern.code } - //patterns. //sortedBy {-1*it.stopsIndices.size}// "${p.pattern.directionId} - ${p.pattern.headsign}" } - patternsAdapter?.let { - it.clear() - it.addAll(selectedPatterns.map { p->"${p.pattern.directionId} - ${p.pattern.headsign}" }) - it.notifyDataSetChanged() - } - viewModel.selectedPatternLiveData.value?.let { - setSelectedPattern(it) - } - val pos = patternsSpinner.selectedItemPosition - //might be possible that the selectedItem is different (larger than list size) - if(pos!= INVALID_POSITION && pos >= 0 && (pos < selectedPatterns.size)){ - val p = selectedPatterns[pos] - Log.d(DEBUG_TAG, "Setting patterns with pos $pos and p gtfsID ${p.pattern.code}") - setPatternAndReqStops(selectedPatterns[pos]) - } - - } - } - - viewModel.stopsForPatternLiveData.observe(viewLifecycleOwner){stops-> - Log.d("BusTO-LinesFragment", "Setting stops from DB") - setCurrentStops(stops) - } if(context!=null) { patternsAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, ArrayList()) @@ -209,6 +176,44 @@ class LinesFragment : ScreenBaseFragment() { return rootView } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + //set requests + viewModel.routesGTTLiveData.observe(viewLifecycleOwner) { + setRoutes(it) + } + + viewModel.patternsWithStopsByRouteLiveData.observe(viewLifecycleOwner){ + patterns -> + run { + selectedPatterns = patterns.sortedBy { p-> p.pattern.code } + //patterns. //sortedBy {-1*it.stopsIndices.size}// "${p.pattern.directionId} - ${p.pattern.headsign}" } + patternsAdapter?.let { + it.clear() + it.addAll(selectedPatterns.map { p->"${p.pattern.directionId} - ${p.pattern.headsign}" }) + it.notifyDataSetChanged() + } + viewModel.selectedPatternLiveData.value?.let { + setSelectedPattern(it) + } + + val pos = patternsSpinner.selectedItemPosition + //might be possible that the selectedItem is different (larger than list size) + if(pos!= INVALID_POSITION && pos >= 0 && (pos < selectedPatterns.size)){ + val p = selectedPatterns[pos] + Log.d(DEBUG_TAG, "Setting patterns with pos $pos and p gtfsID ${p.pattern.code}") + setPatternAndReqStops(selectedPatterns[pos]) + } + + } + } + + viewModel.stopsForPatternLiveData.observe(viewLifecycleOwner){stops-> + Log.d("BusTO-LinesFragment", "Setting stops from DB") + setCurrentStops(stops) + } + } + override fun onAttach(context: Context) { super.onAttach(context) if(context is CommonFragmentListener) diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/LinesGridShowingFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/LinesGridShowingFragment.kt index 11c1a67..e5a3f91 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/LinesGridShowingFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/LinesGridShowingFragment.kt @@ -1,3 +1,20 @@ +/* + BusTO - Fragments components + Copyright (C) 2018-2026 Fabio Mazza + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ package it.reyboz.bustorino.fragments import android.content.Context @@ -81,14 +98,6 @@ class LinesGridShowingFragment : ScreenBaseFragment() { private val lastQueryEmptyForAgency = HashMap(3) private var openRecyclerView = "AG_URBAN" - private fun getFlexLayoutManager(context: Context): FlexboxLayoutManager{ - val layoutManager = FlexboxLayoutManager(context) - layoutManager.flexDirection = FlexDirection.ROW - layoutManager.justifyContent = JustifyContent.FLEX_START - - return layoutManager - } - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -122,7 +131,6 @@ class LinesGridShowingFragment : ScreenBaseFragment() { //init favorites recyclerview favoritesRecyclerView.layoutManager = getFlexLayoutManager(requireContext()) - viewModel.getLinesLiveData().observe(viewLifecycleOwner){ rL -> routesByAgency.clear() @@ -167,16 +175,6 @@ class LinesGridShowingFragment : ScreenBaseFragment() { } } - viewModel.favoritesLines.observe(viewLifecycleOwner){ routes-> - val routesNames = routes.map { it.shortName } - //create new item click listener every time - val adapter = RouteOnlyLineAdapter(routesNames){ pos, _ -> - val r = routes[pos] - fragmentListener.openLineFromStop(r.gtfsId, null) - } - favoritesRecyclerView.adapter = adapter - } - //onClicks urbanLinesTitle.setOnClickListener { openLinesAndCloseOthersIfNeeded(AG_URBAN) @@ -220,6 +218,7 @@ class LinesGridShowingFragment : ScreenBaseFragment() { return rootView } + fun setUserSearch(textSearch:String){ viewModel.setLineQuery(textSearch) } @@ -228,6 +227,16 @@ class LinesGridShowingFragment : ScreenBaseFragment() { super.onViewCreated(view, savedInstanceState) val menuHost: MenuHost = requireActivity() + viewModel.favoritesLines.observe(viewLifecycleOwner){ routes-> + val routesNames = routes.map { it.shortName } + //create new item click listener every time + val adapter = RouteOnlyLineAdapter(routesNames){ pos, _ -> + val r = routes[pos] + fragmentListener.openLineFromStop(r.gtfsId, null) + } + favoritesRecyclerView.adapter = adapter + } + // Add menu items without using the Fragment Menu APIs // Note how we can tie the MenuProvider to the viewLifecycleOwner // and an optional Lifecycle.State (here, RESUMED) to indicate when diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java index f98e7e5..1282296 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java +++ b/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java @@ -19,10 +19,7 @@ package it.reyboz.bustorino.fragments; import android.Manifest; import android.content.Context; -import android.content.Intent; import android.content.pm.PackageManager; -import android.net.Uri; -import android.os.Build; import android.os.Bundle; import androidx.activity.result.ActivityResultCallback; @@ -33,7 +30,6 @@ import androidx.annotation.Nullable; import androidx.appcompat.widget.AppCompatImageButton; import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; @@ -42,31 +38,25 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import android.util.Log; import android.view.LayoutInflater; -import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; -import android.widget.FrameLayout; import android.widget.ProgressBar; import android.widget.Toast; import com.google.android.material.floatingactionbutton.FloatingActionButton; -import java.security.InvalidParameterException; import java.util.Map; +import java.util.concurrent.LinkedBlockingQueue; import it.reyboz.bustorino.R; import it.reyboz.bustorino.backend.*; -import it.reyboz.bustorino.middleware.BarcodeScanContract; -import it.reyboz.bustorino.middleware.BarcodeScanOptions; -import it.reyboz.bustorino.middleware.BarcodeScanUtils; import it.reyboz.bustorino.util.Permissions; import it.reyboz.bustorino.viewmodels.IntroViewModel; import org.jetbrains.annotations.NotNull; -import static it.reyboz.bustorino.backend.utils.getBusStopIDFromUri; import static it.reyboz.bustorino.util.Permissions.LOCATION_PERMISSIONS; @@ -89,20 +79,34 @@ public class MainScreenFragment extends BarcodeFragment implements FragmentList public final static String FRAGMENT_TAG = "MainScreenFragment"; private enum SearchMode {SEARCH_ID,SEARCH_NAME,INITIAL} - public enum InitialScreen { + public enum InternalScreen { HOME_BUTTONS(0), NEARBY_STOPS(1), ARRIVALS(2), - STOP_SEARCH(3); + STOP_SEARCH(3), + NEARBY_ARRIVALS(4); public final int code; - InitialScreen(int code) { this.code = code; } + InternalScreen(int code) { this.code = code; } @Nullable - public static InitialScreen fromCode(int code) { - for (InitialScreen c : values()) if (c.code == code) return c; + public static InternalScreen fromCode(int code) { + for (InternalScreen c : values()) if (c.code == code) return c; return null; } + @NonNull + public static InternalScreen fromFragmentKind(@NonNull FragmentKind kind){ + switch (kind){ + case HOME_BUTTONS -> { return InternalScreen.HOME_BUTTONS; } + case NEARBY_STOPS -> { return InternalScreen.NEARBY_STOPS; } + case FragmentKind.ARRIVALS -> { return InternalScreen.ARRIVALS; } + case FragmentKind.STOPS -> { return InternalScreen.STOP_SEARCH; } + case FragmentKind.NEARBY_ARRIVALS -> { return InternalScreen.NEARBY_ARRIVALS; } + default -> { + throw new IllegalArgumentException("Unknown fragment kind"); + } + } + } } private FragmentHelper fragmentHelper; @@ -110,23 +114,34 @@ public class MainScreenFragment extends BarcodeFragment implements FragmentList private EditText busStopSearchByIDEditText; private EditText busStopSearchByNameEditText; private ProgressBar progressBar; - - private MenuItem actionHelpMenuItem; private FloatingActionButton floatingActionButton; - private FrameLayout resultFrameLayout; + + /// VIEW MODELS in BaseFragment + private boolean setupOnStart = true; private boolean suppressArrivalsReload = false; - //private Snackbar snackbar; - /* - * Search mode - */ - + private boolean initialScreenShown = false; private SearchMode searchMode = SearchMode.INITIAL; - //private ImageButton addToFavorites; - //// HIDDEN BUT IMPORTANT ELEMENTS //// private FragmentManager childFragMan; - private IntroViewModel introViewModel; + + /// LOCATION STUFF /// + boolean pendingIntroRun = false; + boolean pendingNearbyStopsFragmentRequest = false; + boolean pendingNearbyAddToBackStack = false; + boolean locationPermissionGranted, locationPermissionAsked = false; + + //// ACTIVITY ATTACHED (LISTENER /// + private CommonFragmentListener mListener; + + private String pendingStopID = null; + private String pendingSearchQuery = null; + private InternalScreen internalScreen = InternalScreen.HOME_BUTTONS; + private CoordinatorLayout coordLayout; + + //this is really a hackish thing, but it works + private LinkedBlockingQueue thingsToDoOnStart = new LinkedBlockingQueue<>(); + private void refreshStop() { if(getContext() == null){ Log.w(DEBUG_TAG,"Asked to refresh stop but context is null"); @@ -146,13 +161,6 @@ public class MainScreenFragment extends BarcodeFragment implements FragmentList } } - - /// LOCATION STUFF /// - boolean pendingIntroRun = false; - boolean pendingNearbyStopsFragmentRequest = false; - boolean pendingNearbyAddToBackStack = false; - boolean locationPermissionGranted, locationPermissionAsked = false; - //AppLocationManager locationManager; private final ActivityResultLauncher requestPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<>() { @Override @@ -187,28 +195,19 @@ public class MainScreenFragment extends BarcodeFragment implements FragmentList } }); - - //// ACTIVITY ATTACHED (LISTENER /// - private CommonFragmentListener mListener; - - private String pendingStopID = null; - private String pendingSearchQuery = null; - private InitialScreen initialScreen = InitialScreen.HOME_BUTTONS; - private CoordinatorLayout coordLayout; - public MainScreenFragment() { // Required empty public constructor } - public static MainScreenFragment newInstance(@NonNull InitialScreen kind, + public static MainScreenFragment newInstance(@NonNull InternalScreen kind, @Nullable String stopId, @Nullable String query) { MainScreenFragment f = new MainScreenFragment(); f.setArguments(makeArgs(kind, stopId, query)); return f; } - public static MainScreenFragment newInstance(@NonNull InitialScreen kind, @Nullable Bundle args){ + public static MainScreenFragment newInstance(@NonNull InternalScreen kind, @Nullable Bundle args){ MainScreenFragment f = new MainScreenFragment(); if (args != null) { f.setArguments(args); @@ -223,7 +222,7 @@ public class MainScreenFragment extends BarcodeFragment implements FragmentList * @param query * @return */ - public static Bundle makeArgs(@NonNull InitialScreen kind, @Nullable String stopId, @Nullable String query) { + public static Bundle makeArgs(@NonNull InternalScreen kind, @Nullable String stopId, @Nullable String query) { Bundle b = new Bundle(); b.putInt(ARG_INITIAL_CONTENT, kind.code); if (stopId != null) b.putString(ARG_STOP_ID, stopId); @@ -231,16 +230,16 @@ public class MainScreenFragment extends BarcodeFragment implements FragmentList return b; } public static Bundle makeArgsArrivals(@NonNull String stopID){ - return makeArgs(InitialScreen.ARRIVALS, stopID, null); + return makeArgs(InternalScreen.ARRIVALS, stopID, null); } public static Bundle makeArgsStops(@NonNull String query){ - return makeArgs(InitialScreen.STOP_SEARCH, query, null); + return makeArgs(InternalScreen.STOP_SEARCH, query, null); } public static Bundle makeArgsNearby(){ - return makeArgs(InitialScreen.NEARBY_STOPS, null, null); + return makeArgs(InternalScreen.NEARBY_STOPS, null, null); } public static Bundle makeArgsButtonsScreen(){ - return makeArgs(InitialScreen.HOME_BUTTONS, null, null); + return makeArgs(InternalScreen.HOME_BUTTONS, null, null); } @@ -253,9 +252,9 @@ public class MainScreenFragment extends BarcodeFragment implements FragmentList Log.d(DEBUG_TAG, "ARGS ARE NOT NULL: "+ args); if (args.containsKey(ARG_INITIAL_CONTENT)) { - int code = args.getInt(ARG_INITIAL_CONTENT, InitialScreen.HOME_BUTTONS.code); - InitialScreen parsed = InitialScreen.fromCode(code); - initialScreen = (parsed != null) ? parsed : InitialScreen.HOME_BUTTONS; + int code = args.getInt(ARG_INITIAL_CONTENT, InternalScreen.HOME_BUTTONS.code); + InternalScreen parsed = InternalScreen.fromCode(code); + internalScreen = (parsed != null) ? parsed : InternalScreen.HOME_BUTTONS; } String stopId = args.getString(ARG_STOP_ID); if (stopId != null) pendingStopID = stopId; @@ -276,7 +275,6 @@ public class MainScreenFragment extends BarcodeFragment implements FragmentList swipeRefreshLayout = root.findViewById(R.id.listRefreshLayout); floatingActionButton = root.findViewById(R.id.floatingActionButton); - resultFrameLayout = root.findViewById(R.id.resultFrame); busStopSearchByIDEditText.setSelectAllOnFocus(true); busStopSearchByIDEditText .setOnEditorActionListener((v, actionId, event) -> { @@ -337,14 +335,30 @@ public class MainScreenFragment extends BarcodeFragment implements FragmentList cr.setPowerRequirement(Criteria.NO_REQUIREMENT); */ //locationManager = AppLocationManager.getInstance(requireContext()); - introViewModel = new ViewModelProvider(requireActivity()).get(IntroViewModel.class); + IntroViewModel introViewModel = new ViewModelProvider(requireActivity()).get(IntroViewModel.class); introViewModel.getIntroIsRunning().observe(getViewLifecycleOwner(), isRunning -> { pendingIntroRun = isRunning; }); - Log.d(DEBUG_TAG, "OnCreateView, savedInstanceState null: "+(savedInstanceState==null)); + // TODO: Figure out how to go back to home when pressing home in the nav side bar + /*fragShowingViewModel.getKindShowingFragment().observe(getViewLifecycleOwner(), kind -> { + Log.w(DEBUG_TAG, "showing fragment kind: " + kind); + try { + var screenType = InternalScreen.fromFragmentKind(kind); + if(screenType != internalScreen) { + showDifferentSubFragments(screenType); + internalScreen = screenType; + } + } catch (IllegalArgumentException e) { + //ignored + Log.d(DEBUG_TAG, "no update from fragment kind"); + } + + }); + */ + Log.d(DEBUG_TAG, "OnCreateView, savedInstanceState null: "+(savedInstanceState==null)); return root; } @@ -371,16 +385,19 @@ public class MainScreenFragment extends BarcodeFragment implements FragmentList if (savedInstanceState != null) return; - dispatchInitialContent(); + showDifferentSubFragments(internalScreen); } /** * Installs the initial child fragment based on the arguments supplied as arguments */ - private void dispatchInitialContent() { - switch (initialScreen) { + private void showDifferentSubFragments(@NonNull InternalScreen screen) { + boolean firstTime = !initialScreenShown; + switch (screen) { case NEARBY_STOPS: - showNearbyStopsFragmentChecking(false); + case NEARBY_ARRIVALS: // TODO differentiate later + //add to back stack if it is not just created + showNearbyStopsFragmentChecking(!firstTime); break; case ARRIVALS: // pendingStopID is consumed in onResume → requestArrivalsForStopID @@ -389,13 +406,16 @@ public class MainScreenFragment extends BarcodeFragment implements FragmentList if (pendingSearchQuery != null && pendingSearchQuery.length() >= 2) { fragmentHelper.requestStopSearch(pendingSearchQuery); } else { - showButtonsFragment(true); + showButtonsFragment(firstTime); } pendingSearchQuery = null; break; case HOME_BUTTONS: default: - showButtonsFragment(true); + showButtonsFragment(firstTime); + } + if(!initialScreenShown){ + initialScreenShown = true; } } @@ -461,6 +481,15 @@ public class MainScreenFragment extends BarcodeFragment implements FragmentList public void onStart() { super.onStart(); Log.d(DEBUG_TAG, "onStart called, setupOnStart: "+setupOnStart); + try { + while (!thingsToDoOnStart.isEmpty()) { + var task = thingsToDoOnStart.take(); + task.run(); + } + } catch (InterruptedException e) { + Log.w(DEBUG_TAG, "Interrupted while doing task for start"); + thingsToDoOnStart.clear(); + } if (setupOnStart) { if (pendingStopID==null){ @@ -492,6 +521,35 @@ public class MainScreenFragment extends BarcodeFragment implements FragmentList ft.commit(); } + public void showButtonsFragmentIfNotNearby(boolean addToBackStack){ + if(isAdded()) { + var framan = getChildFragmentManager(); + var showingFrag = framan.findFragmentById(R.id.resultFrame); + if (showingFrag == null || showingFrag instanceof NearbyStopsFragment) { + var fragHome = ButtonsFragment.newInstance(); + var ft = framan.beginTransaction(); + if (showingFrag == null) { + ft.add(R.id.resultFrame, fragHome, ButtonsFragment.FRAGMENT_TAG); + } else { + ft.replace(R.id.resultFrame, fragHome, ButtonsFragment.FRAGMENT_TAG); + } + if (addToBackStack) ft.addToBackStack(null); + ft.commit(); + } else { + Log.d(DEBUG_TAG, "attempting to show buttons home fragment but have other types (different than nearby)"); + } + } else{ + Log.d(DEBUG_TAG, "Fragment is not added, putting in queue of things to do"); + try { + thingsToDoOnStart.put(() -> { + showButtonsFragmentIfNotNearby(addToBackStack); + }); + } catch (InterruptedException e) { + Log.e(DEBUG_TAG,"Cannot add task"); + } + } + } + private void showNearbyStopsFragmentChecking(boolean addToBackStack){ if(!checkLocationPermission()){ requestLocationPermission(); @@ -554,7 +612,8 @@ public class MainScreenFragment extends BarcodeFragment implements FragmentList requestArrivalsForStopID(pendingStopID); pendingStopID = null; } - mListener.readyGUIfor(FragmentKind.MAIN_SCREEN_FRAGMENT); + + //mListener.readyGUIfor(FragmentKind.MAIN_SCREEN_FRAGMENT); //fragmentHelper.setBlockAllActivities(false); @@ -719,7 +778,11 @@ public class MainScreenFragment extends BarcodeFragment implements FragmentList @Override public void showFloatingActionButton(boolean yes) { - mListener.showFloatingActionButton(yes); + //mListener.showFloatingActionButton(yes); + if(yes) + floatingActionButton.setVisibility(View.VISIBLE); + else + floatingActionButton.setVisibility(View.GONE); } /** @@ -773,7 +836,14 @@ public class MainScreenFragment extends BarcodeFragment implements FragmentList @Override public void openNearbyStopsFragment() { - showNearbyStopsFragmentChecking(true); + if(isAdded()) + showNearbyStopsFragmentChecking(true); + else + try{ + thingsToDoOnStart.put(() -> showNearbyStopsFragmentChecking(true)); + } catch (InterruptedException e) { + Log.e(DEBUG_TAG, "trying to put open nearby in task but was interrupted"); + } } @Override @@ -791,6 +861,7 @@ public class MainScreenFragment extends BarcodeFragment implements FragmentList if(mListener!=null) mListener.showMapCenteredOnStop(stop); } + /** * Main method for stops requests * @param ID the Stop ID @@ -869,11 +940,13 @@ public class MainScreenFragment extends BarcodeFragment implements FragmentList if (!(existingFrag instanceof NearbyStopsFragment)){ Log.d(DEBUG_TAG, "actually showing Nearby Stops Fragment"); //there is no fragment showing - final NearbyStopsFragment fragment = NearbyStopsFragment.newInstance(NearbyStopsFragment.FragType.STOPS); - + var nearbyFrag = (NearbyStopsFragment) childFragMan.findFragmentByTag(NearbyStopsFragment.FRAGMENT_TAG); + if(nearbyFrag==null){ + nearbyFrag = NearbyStopsFragment.newInstance(NearbyStopsFragment.FragType.STOPS); + } FragmentTransaction ft = childFragMan.beginTransaction(); - ft.replace(R.id.resultFrame, fragment, NearbyStopsFragment.FRAGMENT_TAG); + ft.replace(R.id.resultFrame, nearbyFrag, NearbyStopsFragment.FRAGMENT_TAG); if(addToBackStack) ft.addToBackStack(null); if (getActivity()!=null && !getActivity().isFinishing()) ft.commit(); diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt index 337bb10..30a0614 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt @@ -196,13 +196,21 @@ class MapLibreFragment : GeneralMapLibreFragment() { } */ - - // Setup close button rootView.findViewById(R.id.btnClose).setOnClickListener { hideStopOrBusBottomSheet() } - observeStatusLivePositions() + + + Log.d(DEBUG_TAG, "Fragment View Created!") + + //TODO: Reshow last open stop when switching back to the map fragment + return rootView + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + //observe change in source of the live positions livePositionsViewModel.useMQTTPositionsLiveData.observe(viewLifecycleOwner){ useMQTT-> //Log.d(DEBUG_TAG, "Changed MQTT positions, now have to use MQTT: $useMQTT") @@ -237,10 +245,8 @@ class MapLibreFragment : GeneralMapLibreFragment() { setLocationIconEnabled(it)} mapStateViewModel.followingUserPosition.observe(viewLifecycleOwner){ updateFollowingIcon(it)} - Log.d(DEBUG_TAG, "Fragment View Created!") + observeStatusLivePositions() - //TODO: Reshow last open stop when switching back to the map fragment - return rootView } /** diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.java index 0800925..cc0eca8 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.java +++ b/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.java @@ -486,6 +486,7 @@ public class NearbyStopsFragment extends ScreenBaseFragment { super.onViewCreated(view, savedInstanceState); gridRecyclerView.setVisibility(View.INVISIBLE); gridRecyclerView.addOnScrollListener(scrollListener); + if(mListener!=null) mListener.readyGUIfor(FragmentKind.NEARBY_STOPS); } @Override @@ -711,4 +712,5 @@ public class NearbyStopsFragment extends ScreenBaseFragment { } */ + } diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/ResultBaseFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/ResultBaseFragment.java index 1a77b6c..11e05bf 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/ResultBaseFragment.java +++ b/app/src/main/java/it/reyboz/bustorino/fragments/ResultBaseFragment.java @@ -2,7 +2,6 @@ package it.reyboz.bustorino.fragments; import android.content.Context; import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; public abstract class ResultBaseFragment extends ScreenBaseFragment { diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/ResultListFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/ResultListFragment.java index dd07dee..999c427 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/ResultListFragment.java +++ b/app/src/main/java/it/reyboz/bustorino/fragments/ResultListFragment.java @@ -41,7 +41,6 @@ import it.reyboz.bustorino.backend.Palina; import it.reyboz.bustorino.backend.Route; import it.reyboz.bustorino.backend.Stop; import it.reyboz.bustorino.data.UserDB; - /** * This is a generalized fragment that can be used both for * diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java index 6f5306f..24c3234 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java +++ b/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java @@ -1,8 +1,26 @@ +/* + BusTO - Fragments components + Copyright (C) 2018-2026 Fabio Mazza + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ package it.reyboz.bustorino.fragments; import android.Manifest; import android.content.Context; import android.content.SharedPreferences; +import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -19,6 +37,9 @@ import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; import androidx.fragment.app.Fragment; +import com.google.android.flexbox.FlexDirection; +import com.google.android.flexbox.FlexboxLayoutManager; +import com.google.android.flexbox.JustifyContent; import com.google.android.material.snackbar.Snackbar; import it.reyboz.bustorino.BuildConfig; @@ -30,6 +51,13 @@ public abstract class ScreenBaseFragment extends Fragment { protected final static String PREF_FILE= BuildConfig.APPLICATION_ID+".fragment_prefs"; + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + + protected void setOption(String optionName, boolean value) { Context mContext = getContext(); assert mContext != null; @@ -114,6 +142,14 @@ public abstract class ScreenBaseFragment extends Fragment { } }); } + + protected FlexboxLayoutManager getFlexLayoutManager(@NonNull Context context) { + var layoutManager = new FlexboxLayoutManager(context); + layoutManager.setFlexDirection(FlexDirection.ROW); + layoutManager.setJustifyContent(JustifyContent.FLEX_START); + + return layoutManager; + } /*protected void runActionFavorites(@NonNull Stop s, @NonNull FavoritesChangeWorker.Action action, @NonNull FavoritesChangeWorker.Companion.ResultListener resultListener){ Context mContext = requireContext(); @@ -172,6 +208,7 @@ public abstract class ScreenBaseFragment extends Fragment { }); } + public interface LocationRequestListener{ void onPermissionResult(boolean locationGranted); } diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/StopListFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/StopListFragment.java index 4ec5795..17f23db 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/StopListFragment.java +++ b/app/src/main/java/it/reyboz/bustorino/fragments/StopListFragment.java @@ -17,11 +17,9 @@ */ package it.reyboz.bustorino.fragments; -import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; -import androidx.annotation.NonNull; import androidx.loader.app.LoaderManager; import androidx.loader.content.CursorLoader; import androidx.loader.content.Loader; @@ -31,7 +29,6 @@ import it.reyboz.bustorino.backend.Stop; import it.reyboz.bustorino.data.AppDataProvider; import it.reyboz.bustorino.data.NextGenDB.Contract.StopsTable; import it.reyboz.bustorino.adapters.StopAdapter; -import org.jetbrains.annotations.NotNull; import java.util.Arrays; import java.util.List; diff --git a/app/src/main/res/layout/round_line_header.xml b/app/src/main/res/layout/round_line_header.xml index 66159b4..3448368 100644 --- a/app/src/main/res/layout/round_line_header.xml +++ b/app/src/main/res/layout/round_line_header.xml @@ -3,10 +3,10 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="wrap_content" android:layout_height="wrap_content" - + android:id="@+id/headerCardView" android:background="@color/orange_500" app:cardCornerRadius="26sp" - app:cardElevation="0sp" + app:cardElevation="1sp" android:layout_gravity="center_vertical" android:layout_margin="5dp" android:padding="3dp" diff --git a/app/src/main/res/menu/drawer_main.xml b/app/src/main/res/menu/drawer_main.xml index 0c2f4a4..ed03092 100644 --- a/app/src/main/res/menu/drawer_main.xml +++ b/app/src/main/res/menu/drawer_main.xml @@ -3,9 +3,14 @@ + Visualizza sulla mappa Non trovo un\'applicazione dove mostrarla Posizione della fermata non trovata - Vicino a me + Vicino a me Fermate vicine Ricerca della posizione Nessuna fermata nei dintorni diff --git a/app/src/main/res/values/keys.xml b/app/src/main/res/values/keys.xml index b6dfada..c54d6ee 100644 --- a/app/src/main/res/values/keys.xml +++ b/app/src/main/res/values/keys.xml @@ -8,7 +8,9 @@ + arrivals + nearby favorites map lines diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b11345d..56bce34 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -152,8 +152,9 @@ ListFragment - BusTO it.reyboz.bustorino.preferences db_is_updating - - Near me + + + Near me Nearby stops Nearby connections App version @@ -293,6 +294,7 @@ @string/nav_home_text + @string/near_me_title @string/nav_favorites_text @string/nav_map_text @string/lines