commit 0f10c8c44286a93fff22d7a5319ae256899469ab Author: Fabio Mazza Date: Sun May 24 23:48:46 2026 +0200 Add stuff not present in the latest diff Summary: Stuff that was removed by commit: https://gitpull.it/D246?vs=788&id=790#toc 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/D247 diff --git a/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java b/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java index f9a96d7..fb88b9a 100644 --- a/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java +++ b/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java @@ -73,7 +73,7 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen private Snackbar snackbar; private ServiceAlertsViewModel serviceAlertsViewModel; - private FragmentKind showingFragmentKind; + //private FragmentKind showingFragmentKind; private final Map menuActions = Map.of( R.id.drawer_action_settings, () -> { @@ -541,6 +541,7 @@ 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); @@ -555,6 +556,8 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen } } + */ + /** * Show the actual fragment by adding it to the backstack * @param fraMan the fragmentManager @@ -708,8 +711,8 @@ public class ActivityPrincipal extends GeneralActivity implements FragmentListen @Override public void readyGUIfor(FragmentKind fragmentType) { - - updateShowingFragmentKindInternal(fragmentType); + //updateShowingFragmentKindInternal(fragmentType); + if(BuildConfig.DEBUG) Log.d(DEBUG_TAG, "readyGUIfor fragment kind: " + fragmentType); Integer titleResId = null; switch (fragmentType){ diff --git a/app/src/main/java/it/reyboz/bustorino/adapters/ArrivalsStopAdapter.java b/app/src/main/java/it/reyboz/bustorino/adapters/ArrivalsStopAdapter.java index 7102c78..35ee280 100644 --- a/app/src/main/java/it/reyboz/bustorino/adapters/ArrivalsStopAdapter.java +++ b/app/src/main/java/it/reyboz/bustorino/adapters/ArrivalsStopAdapter.java @@ -47,7 +47,7 @@ public class ArrivalsStopAdapter extends RecyclerView.Adapter newList, @Nullable GPSPoint pos) { + if(pos!=null) updatePosition(pos); - public void setRoutesPairListAndPosition(@NonNull List newList) { if (routesPairList == null) { routesPairList = new ArrayList<>(newList); notifyItemRangeInserted(0, newList.size()); diff --git a/app/src/main/java/it/reyboz/bustorino/adapters/SquareStopAdapter.java b/app/src/main/java/it/reyboz/bustorino/adapters/SquareStopAdapter.java index b7e194c..4ff5e5b 100644 --- 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 @@ package it.reyboz.bustorino.adapters; 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.view.ViewGroup; 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 class SquareStopAdapter extends RecyclerView.Adapter stops) { - this.stops = stops; + public void setStops(List 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/fragments/LinesGridShowingFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/LinesGridShowingFragment.kt index e5a3f91..e8dcbc6 100644 --- 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 @@ class LinesGridShowingFragment : ScreenBaseFragment() { } private val routeClickListener = RouteAdapter.ItemClicker { - fragmentListener.openLineFromStop(it.gtfsId, null) + //fragmentListener.openLineFromStop(it.gtfsId, null) + openLine(it) } private val arrows = HashMap() private val durations = HashMap() @@ -223,6 +224,10 @@ class LinesGridShowingFragment : ScreenBaseFragment() { 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 @@ class LinesGridShowingFragment : ScreenBaseFragment() { //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/NearbyStopsFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.kt index 1478be5..6e02307 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.kt @@ -39,7 +39,6 @@ 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.fragments.NearbyArrivalsDownloader.ArrivalsListener import it.reyboz.bustorino.middleware.AutoFitGridLayoutManager import it.reyboz.bustorino.middleware.FusedNativeLocationProvider import it.reyboz.bustorino.middleware.FusedNativeLocationProvider.LocationUpdateListener @@ -99,7 +98,6 @@ class NearbyStopsFragment : ScreenBaseFragment() { private var stopsMinNumber = -1 //These are useful for the case of nearby arrivals - private var arrivalsManager: NearbyArrivalsDownloader? = null private var arrivalsStopAdapter: ArrivalsStopAdapter? = null private var currentNearbyStops = ArrayList() @@ -122,18 +120,10 @@ class NearbyStopsFragment : ScreenBaseFragment() { } } } - private val locationOptionsArrivals = FusedNativeLocationProvider.Options(5 * 1000L, 50f) + // Two different settings for the location provider + private val locationOptionsArrivals = FusedNativeLocationProvider.Options(5 * 1000L, 25f) private val locationOptionsStops = 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 permissionsResultLauncher = getPositionRequestLauncher( - granted ->{ - - } - ); - */ private var locationProvider: FusedNativeLocationProvider? = null @@ -249,7 +239,7 @@ class NearbyStopsFragment : ScreenBaseFragment() { super.onViewCreated(view, savedInstanceState) gridRecyclerView.setVisibility(View.INVISIBLE) gridRecyclerView.addOnScrollListener(scrollListener!!) - mListener?.readyGUIfor(FragmentKind.NEARBY_STOPS) + //mListener?.readyGUIfor(FragmentKind.NEARBY_STOPS) //observe the livedata viewModel.stopsAtDistance.observe(getViewLifecycleOwner()) {stops -> @@ -291,16 +281,16 @@ class NearbyStopsFragment : ScreenBaseFragment() { return@observe } if (firstLocForArrivals) { - arrivalsStopAdapter = ArrivalsStopAdapter(stoprouteList, mListener, getContext(), lastPosition!!) + arrivalsStopAdapter = ArrivalsStopAdapter(stoprouteList, mListener, context, lastPosition!!) gridRecyclerView.setAdapter(arrivalsStopAdapter) firstLocForArrivals = false } else { - arrivalsStopAdapter!!.setRoutesPairListAndPosition(stoprouteList) + arrivalsStopAdapter!!.setRoutesPairListAndPosition(stoprouteList, lastPosition) } //arrivalsStopAdapter.notifyDataSetChanged(); showRecyclerHidingLoadMessage() - if (mListener != null) mListener!!.readyGUIfor(FragmentKind.NEARBY_ARRIVALS) + //if (mListener != null) mListener!!.readyGUIfor(FragmentKind.NEARBY_ARRIVALS) } } @@ -477,7 +467,7 @@ class NearbyStopsFragment : ScreenBaseFragment() { override fun onDetach() { super.onDetach() mListener = null - if (arrivalsManager != null) arrivalsManager!!.cancelAllRequests() + //if (arrivalsManager != null) arrivalsManager!!.cancelAllRequests() } /** @@ -549,29 +539,41 @@ class NearbyStopsFragment : ScreenBaseFragment() { */ private fun 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) + 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!!.setText(getString(R.string.nearby_arrivals_message)) - switchButton!!.setText(getString(R.string.show_stops)) - if (arrivalsStopAdapter != null) gridRecyclerView!!.setAdapter(arrivalsStopAdapter) + 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?) { - if (firstLocForStops) { + if (dataAdapter == null) { dataAdapter = SquareStopAdapter(stops, mListener, lastPosition) gridRecyclerView!!.setAdapter(dataAdapter) firstLocForStops = false } else { - dataAdapter!!.setStops(stops) dataAdapter!!.setUserPosition(lastPosition) + dataAdapter!!.setStops(stops) } - dataAdapter!!.notifyDataSetChanged() //showRecyclerHidingLoadMessage(); if (gridRecyclerView!!.getVisibility() != View.VISIBLE) { @@ -581,7 +583,6 @@ class NearbyStopsFragment : ScreenBaseFragment() { } messageTextView!!.setVisibility(View.GONE) - if (mListener != null) mListener!!.readyGUIfor(FragmentKind.NEARBY_STOPS) } private fun showArrivalsInRecycler(routesPairList: List>) { diff --git a/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesGridShowingViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesGridShowingViewModel.kt index 431416d..16eb930 100644 --- a/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesGridShowingViewModel.kt +++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesGridShowingViewModel.kt @@ -29,7 +29,7 @@ class LinesGridShowingViewModel(application: Application) : AndroidViewModel(app val isTouristExpanded = MutableLiveData(false) val favoritesExpanded = MutableLiveData(true) - val favoritesLinesIDs = MutableLiveData>() + val favoritesLinesIDs = MutableLiveData>() private val queryLiveData = MutableLiveData("") fun setLineQuery(query: String){ @@ -77,9 +77,13 @@ class LinesGridShowingViewModel(application: Application) : AndroidViewModel(app } fun setFavoritesLinesIDs(linesIds: HashSet){ - favoritesLinesIDs.value = linesIds - } + val sameSet = favoritesLinesIDs.value?.let{ + linesIds.containsAll(it) && it.containsAll(linesIds) + } ?: false + if(!sameSet)// trigger LiveData + favoritesLinesIDs.value = linesIds.toSet() + } val favoritesLines = favoritesLinesIDs.map {lineIds -> val linesList = ArrayList() diff --git a/app/src/main/java/it/reyboz/bustorino/viewmodels/NearbyStopsViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/NearbyStopsViewModel.kt index 4eb4a5e..1a3bcbf 100644 --- a/app/src/main/java/it/reyboz/bustorino/viewmodels/NearbyStopsViewModel.kt +++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/NearbyStopsViewModel.kt @@ -24,13 +24,23 @@ 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.* +import it.reyboz.bustorino.backend.mato.MapiArrivalRequest import it.reyboz.bustorino.data.OldDataRepository -import it.reyboz.bustorino.fragments.NearbyArrivalsDownloader 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) { @@ -39,10 +49,16 @@ class NearbyStopsViewModel(application: Application): AndroidViewModel(applicati val arrivalsNearby = MutableLiveData>() + // map of arrivals by stopID + private val arrivalsMapStopID = ConcurrentHashMap() + val progressPerc = MutableLiveData() val downloadingArrivals = MutableLiveData() - private val arrivalsListener = object : NearbyArrivalsDownloader.ArrivalsListener { + 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 ) @@ -61,12 +77,91 @@ class NearbyStopsViewModel(application: Application): AndroidViewModel(applicati } - private val nearbyArrivalsDownloader = NearbyArrivalsDownloader(application,arrivalsListener ) + */ - fun requestArrivalsForStops(stops: List) { - nearbyArrivalsDownloader.requestArrivalsForStops(stops) + //private val nearbyArrivalsDownloader = NearbyArrivalsDownloader(application,arrivalsListener ) + + private val volleyManager = NetworkVolleyManager.getInstance(application) + + /** + * Response listener for the requests + */ + private val responseListener = Response.Listener { 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) { + //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() val distanceMtLiveData = MutableLiveData(40) @@ -243,9 +338,16 @@ class NearbyStopsViewModel(application: Application): AndroidViewModel(applicati } + 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 }