diff --git a/app/build.gradle b/app/build.gradle --- a/app/build.gradle +++ b/app/build.gradle @@ -54,7 +54,6 @@ dependencies { //new libraries - } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + @@ -13,18 +12,18 @@ - - + + - - + + - + - + @@ -36,7 +35,6 @@ android:networkSecurityConfig="@xml/networks_security_config" android:roundIcon="@mipmap/ic_launcher_round" android:theme="@style/AppTheme.NoActionBar"> - + android:windowSoftInputMode="adjustResize"> @@ -105,7 +103,6 @@ android:enabled="true" android:exported="false"> - implements SharedPreferences.OnSharedPreferenceChangeListener { private final static int layoutRes = R.layout.arrivals_nearby_card; //private List stops; - private @Nullable Location userPosition; + private @NonNull GPSPoint userPosition; private FragmentListenerMain listener; private List< Pair > routesPairList; private final Context context; @@ -52,7 +52,7 @@ private NameCapitalize capit; - public ArrivalsStopAdapter(@Nullable List< Pair > routesPairList, FragmentListenerMain fragmentListener, Context con, @Nullable Location pos) { + public ArrivalsStopAdapter(@Nullable List< Pair > routesPairList, FragmentListenerMain fragmentListener, Context con, @NonNull GPSPoint pos) { listener = fragmentListener; userPosition = pos; this.routesPairList = routesPairList; @@ -85,7 +85,7 @@ if(stopRoutePair!=null && stopRoutePair.first!=null){ final Stop stop = stopRoutePair.first; final Route r = stopRoutePair.second; - final Double distance = stop.getDistanceFromLocation(userPosition); + final Double distance = stop.getDistanceFromLocation(userPosition.getLatitude(), userPosition.longitude); if(distance!=Double.POSITIVE_INFINITY){ holder.distancetextView.setText(distance.intValue()+" m"); } else { @@ -181,11 +181,11 @@ } } - public void setUserPosition(@Nullable Location userPosition) { + public void setUserPosition(@Nullable GPSPoint userPosition) { this.userPosition = userPosition; } - public void setRoutesPairListAndPosition(List> mRoutesPairList, @Nullable Location pos) { + public void setRoutesPairListAndPosition(List> mRoutesPairList, @Nullable GPSPoint pos) { if(pos!=null){ this.userPosition = pos; } @@ -260,7 +260,7 @@ /** * Sort and remove the repetitions in the list */ - private static void sortAndRemoveDuplicates(List< Pair > routesPairList, Location positionToSort ){ + private static void sortAndRemoveDuplicates(List< Pair > routesPairList, GPSPoint positionToSort ){ Collections.sort(routesPairList,new RoutePositionSorter(positionToSort)); //All of this to get only the first occurrences of a line (name & direction) ListIterator> iterator = routesPairList.listIterator(); diff --git a/app/src/main/java/it/reyboz/bustorino/adapters/SquareStopAdapter.java b/app/src/main/java/it/reyboz/bustorino/adapters/SquareStopAdapter.java --- a/app/src/main/java/it/reyboz/bustorino/adapters/SquareStopAdapter.java +++ b/app/src/main/java/it/reyboz/bustorino/adapters/SquareStopAdapter.java @@ -26,6 +26,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.Stop; import it.reyboz.bustorino.util.StopSorterByDistance; import it.reyboz.bustorino.fragments.FragmentListenerMain; @@ -36,11 +37,11 @@ public class SquareStopAdapter extends RecyclerView.Adapter { private final static int layoutRes = R.layout.stop_card; //private List stops; - private @Nullable Location userPosition; + private @Nullable GPSPoint userPosition; private FragmentListenerMain listener; private List stops; - public SquareStopAdapter(@Nullable List stopList, FragmentListenerMain fragmentListener, @Nullable Location pos) { + public SquareStopAdapter(@Nullable List stopList, FragmentListenerMain fragmentListener, @Nullable GPSPoint pos) { listener = fragmentListener; userPosition = pos; stops = stopList; @@ -116,7 +117,7 @@ this.stops = stops; } - public void setUserPosition(@Nullable Location userPosition) { + public void setUserPosition(@Nullable GPSPoint userPosition) { this.userPosition = userPosition; } /* diff --git a/app/src/main/java/it/reyboz/bustorino/backend/GPSPoint.java b/app/src/main/java/it/reyboz/bustorino/backend/GPSPoint.java new file mode 100644 --- /dev/null +++ b/app/src/main/java/it/reyboz/bustorino/backend/GPSPoint.java @@ -0,0 +1,34 @@ +package it.reyboz.bustorino.backend; + +import org.osmdroid.api.IGeoPoint; + +public class GPSPoint implements IGeoPoint { + + public final double latitude; + public final double longitude; + + public GPSPoint(double latitude, double longitude) { + this.latitude = latitude; + this.longitude = longitude; + } + + @Override + public int getLatitudeE6() { + return (int) (latitude*1e6d); + } + + @Override + public int getLongitudeE6() { + return (int) (longitude*1e6d); + } + + @Override + public double getLatitude() { + return latitude; + } + + @Override + public double getLongitude() { + return longitude; + } +} diff --git a/app/src/main/java/it/reyboz/bustorino/backend/Stop.java b/app/src/main/java/it/reyboz/bustorino/backend/Stop.java --- a/app/src/main/java/it/reyboz/bustorino/backend/Stop.java +++ b/app/src/main/java/it/reyboz/bustorino/backend/Stop.java @@ -23,6 +23,7 @@ import androidx.annotation.Nullable; import it.reyboz.bustorino.util.LinesNameSorter; +import org.osmdroid.api.IGeoPoint; import java.net.URLEncoder; import java.util.Collections; @@ -297,9 +298,11 @@ } - public Double getDistanceFromLocation(Location loc){ + public Double getDistanceFromLocation(IGeoPoint loc){ + return getDistanceFromLocation(loc.getLatitude(), loc.getLongitude()); + } + public Double getDistanceFromLocation(double latitude, double longitude){ if(this.lat!=null && this.lon !=null) - return utils.measuredistanceBetween(this.lat,this.lon,loc.getLatitude(),loc.getLongitude()); + return utils.measuredistanceBetween(this.lat,this.lon,latitude, longitude); else return Double.POSITIVE_INFINITY; - } -} + }} diff --git a/app/src/main/java/it/reyboz/bustorino/backend/utils.java b/app/src/main/java/it/reyboz/bustorino/backend/utils.java --- a/app/src/main/java/it/reyboz/bustorino/backend/utils.java +++ b/app/src/main/java/it/reyboz/bustorino/backend/utils.java @@ -25,12 +25,9 @@ import android.net.Uri; import android.util.Log; import android.util.TypedValue; -import android.view.View; import androidx.annotation.Nullable; import androidx.preference.PreferenceManager; -import java.io.PrintWriter; -import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -44,7 +41,7 @@ import it.reyboz.bustorino.fragments.SettingsFragment; public abstract class utils { - private static final double EarthRadius = 6371e3; + private static final double EARTH_RADIUS = 6371.009e3; public static Double measuredistanceBetween(double lat1,double long1,double lat2,double long2){ @@ -58,11 +55,11 @@ Math.cos(phi1)*Math.cos(phi2)*Math.sin(deltaTheta/2)*Math.sin(deltaTheta/2); final double c = 2*Math.atan2(Math.sqrt(a),Math.sqrt(1-a)); - return Math.abs(EarthRadius*c); + return Math.abs(EARTH_RADIUS *c); } public static Double angleRawDifferenceFromMeters(double distanceInMeters){ - return Math.toDegrees(distanceInMeters/EarthRadius); + return Math.toDegrees(distanceInMeters/ EARTH_RADIUS); } public static int convertDipToPixelsInt(Context con,double dips) @@ -70,6 +67,28 @@ return (int) (dips * con.getResources().getDisplayMetrics().density + 0.5f); } + /** + * Convert distance in meters on Earth in degrees of latitude, keeping the same longitude + * @param distanceMeters distance in meters + * @return angle in degrees + */ + public static Double latitudeDelta(Double distanceMeters){ + final double angleRad = distanceMeters/EARTH_RADIUS; + return Math.toDegrees(angleRad); + } + + /** + * Convert distance in meters on Earth in degrees of longitude, keeping the same latitude + * @param distanceMeters distance in meters + * @param latitude the latitude that is fixed + * @return angle in degrees + */ + public static Double longitudeDelta(Double distanceMeters, Double latitude){ + final double theta = Math.toRadians(latitude); + final double denom = Math.abs(Math.cos(theta)); + final double angleRad = 2*Math.asin(Math.sin(distanceMeters / EARTH_RADIUS) / denom); + return Math.toDegrees(angleRad); + } public static float convertDipToPixels(Context con, float dp){ return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,con.getResources().getDisplayMetrics()); diff --git a/app/src/main/java/it/reyboz/bustorino/data/AppDataProvider.java b/app/src/main/java/it/reyboz/bustorino/data/AppDataProvider.java --- a/app/src/main/java/it/reyboz/bustorino/data/AppDataProvider.java +++ b/app/src/main/java/it/reyboz/bustorino/data/AppDataProvider.java @@ -27,6 +27,7 @@ import it.reyboz.bustorino.BuildConfig; import it.reyboz.bustorino.backend.DBStatusManager; import it.reyboz.bustorino.backend.Stop; +import it.reyboz.bustorino.backend.utils; import it.reyboz.bustorino.data.NextGenDB.Contract.*; import java.util.List; @@ -219,15 +220,19 @@ if(parts.size()>=4 && "location".equals(parts.get(1))){ Double latitude = Double.parseDouble(parts.get(2)); Double longitude = Double.parseDouble(parts.get(3)); - //converting distance to a float to not lose precision - float distance = parts.size()>=5 ? Float.parseFloat(parts.get(4))/1000 : 0.02f; + //distance in meters + final double distance = parts.size()>=5 ? Double.parseDouble(parts.get(4)) : 50; //if(parts.size()>=5) //Log.d("LocationSearch"," given distance to search is "+parts.get(4)+" m"); - Double distasAngle = (distance/6371)*180/Math.PI; //small angles approximation, still valid for about 500 metres + Double latDelta = utils.latitudeDelta(distance); + Double longDelta = utils.longitudeDelta(distance, latitude); + Log.d(DEBUG_TAG, "Location search around: "+latitude+" , "+longitude); + Log.d(DEBUG_TAG, "Location search: latitude {"+(latitude-latDelta)+", "+(latitude+latDelta)+ + "} longitude {"+(longitude-longDelta)+", "+(longitude+longDelta)+"}"); - String whereClause = StopsTable.COL_LAT+ "< "+(latitude+distasAngle)+" AND " - +StopsTable.COL_LAT +" > "+(latitude-distasAngle)+" AND "+ - StopsTable.COL_LONG+" < "+(longitude+distasAngle)+" AND "+StopsTable.COL_LONG+" > "+(longitude-distasAngle); + String whereClause = StopsTable.COL_LAT+ "< "+(latitude+latDelta)+" AND " + +StopsTable.COL_LAT +" > "+(latitude-latDelta)+" AND "+ + StopsTable.COL_LONG+" < "+(longitude+longDelta)+" AND "+StopsTable.COL_LONG+" > "+(longitude-longDelta); //Log.d("Provider-LOCSearch","Querying stops by position, query args: \n"+whereClause); return db.query(StopsTable.TABLE_NAME,projection,whereClause,null,null,null,null); } diff --git a/app/src/main/java/it/reyboz/bustorino/data/OldDataRepository.kt b/app/src/main/java/it/reyboz/bustorino/data/OldDataRepository.kt --- a/app/src/main/java/it/reyboz/bustorino/data/OldDataRepository.kt +++ b/app/src/main/java/it/reyboz/bustorino/data/OldDataRepository.kt @@ -20,6 +20,8 @@ import android.content.Context import it.reyboz.bustorino.backend.Result import it.reyboz.bustorino.backend.Stop +import it.reyboz.bustorino.backend.utils +import java.util.ArrayList import java.util.concurrent.Executor class OldDataRepository(private val executor: Executor, private val nextGenDB: NextGenDB) { @@ -55,12 +57,25 @@ latitFrom, latitTo, longitFrom, longitTo ) - if (stops!=null) - callback.onComplete(Result.success(stops)) + + callback.onComplete(Result.success(stops)) } } + /** + * Request all the stops in position [latitude], [longitude], in the "square" with radius [distanceMeters] + * Returns nothing, [callback] will be called if the query succeeds + */ + fun requestStopsWithinDistance(latitude: Double, longitude: Double, distanceMeters: Int, callback: Callback>){ + + val latDelta = utils.latitudeDelta(distanceMeters.toDouble()) + val longDelta = utils.longitudeDelta(distanceMeters.toDouble(), latitude) + + requestStopsInArea(latitude-latDelta, + latitude+latDelta, longitude-longDelta, longitude+longDelta, callback) + } + fun interface Callback { diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.java --- a/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.java +++ b/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.java @@ -65,8 +65,11 @@ import it.reyboz.bustorino.util.LinesNameSorter; import it.reyboz.bustorino.util.ViewUtils; +import static it.reyboz.bustorino.fragments.ScreenBaseFragment.setOption; + public class ArrivalsFragment extends ResultBaseFragment implements LoaderManager.LoaderCallbacks { + private static final String OPTION_SHOW_LEGEND = "show_legend"; private final static String KEY_STOP_ID = "stopid"; private final static String KEY_STOP_NAME = "stopname"; private final static String DEBUG_TAG_ALL = "BUSTOArrivalsFragment"; @@ -92,6 +95,9 @@ protected RecyclerView arrivalsRecyclerView; private PalinaAdapter mListAdapter = null; + private TextView howDoesItWorkTextView; + private Button hideHintButton; + //private NestedScrollView theScrollView; protected RecyclerView noArrivalsRecyclerView; @@ -181,6 +187,11 @@ View root = inflater.inflate(R.layout.fragment_arrivals, container, false); messageTextView = root.findViewById(R.id.messageTextView); addToFavorites = root.findViewById(R.id.addToFavorites); + // "How does it work part" + howDoesItWorkTextView = root.findViewById(R.id.howDoesItWorkTextView); + hideHintButton = root.findViewById(R.id.hideHintButton); + hideHintButton.setOnClickListener(this::onHideHint); + //theScrollView = root.findViewById(R.id.arrivalsScrollView); // recyclerview holding the arrival times arrivalsRecyclerView = root.findViewById(R.id.arrivalsRecyclerView); @@ -288,6 +299,10 @@ updateMessage(); } + if (ScreenBaseFragment.getOption(requireContext(),OPTION_SHOW_LEGEND, true)) { + showHints(); + } + } @@ -333,6 +348,23 @@ this.reloadOnResume = reloadOnResume; } + // HINT "HOW TO USE" + private void showHints() { + howDoesItWorkTextView.setVisibility(View.VISIBLE); + hideHintButton.setVisibility(View.VISIBLE); + //actionHelpMenuItem.setVisible(false); + } + + private void hideHints() { + howDoesItWorkTextView.setVisibility(View.GONE); + hideHintButton.setVisibility(View.GONE); + //actionHelpMenuItem.setVisible(true); + } + + public void onHideHint(View v) { + hideHints(); + setOption(requireContext(),OPTION_SHOW_LEGEND, false); + } /** * Give the fetchers * @return the list of the fetchers 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 @@ -68,7 +68,6 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentListenerMain{ - private static final String OPTION_SHOW_LEGEND = "show_legend"; private static final String SAVED_FRAGMENT="saved_fragment"; private static final String DEBUG_TAG = "BusTO - MainFragment"; @@ -82,8 +81,7 @@ private EditText busStopSearchByIDEditText; private EditText busStopSearchByNameEditText; private ProgressBar progressBar; - private TextView howDoesItWorkTextView; - private Button hideHintButton; + private MenuItem actionHelpMenuItem; private FloatingActionButton floatingActionButton; private FrameLayout resultFrameLayout; @@ -279,8 +277,7 @@ busStopSearchByIDEditText = root.findViewById(R.id.busStopSearchByIDEditText); busStopSearchByNameEditText = root.findViewById(R.id.busStopSearchByNameEditText); progressBar = root.findViewById(R.id.progressBar); - howDoesItWorkTextView = root.findViewById(R.id.howDoesItWorkTextView); - hideHintButton = root.findViewById(R.id.hideHintButton); + swipeRefreshLayout = root.findViewById(R.id.listRefreshLayout); floatingActionButton = root.findViewById(R.id.floatingActionButton); resultFrameLayout = root.findViewById(R.id.resultFrame); @@ -311,7 +308,6 @@ coordLayout = root.findViewById(R.id.coord_layout); floatingActionButton.setOnClickListener((this::onToggleKeyboardLayout)); - hideHintButton.setOnClickListener(this::onHideHint); AppCompatImageButton qrButton = root.findViewById(R.id.QRButton); qrButton.setOnClickListener(this::onQRButtonClick); @@ -527,11 +523,7 @@ barcodeLauncher.launch(scanOptions); } } - public void onHideHint(View v) { - hideHints(); - setOption(OPTION_SHOW_LEGEND, false); - } /** * OK this is pure shit * @@ -618,18 +610,6 @@ busStopSearchByIDEditText.setSelection(busStopID.length()); } - private void showHints() { - howDoesItWorkTextView.setVisibility(View.VISIBLE); - hideHintButton.setVisibility(View.VISIBLE); - //actionHelpMenuItem.setVisible(false); - } - - private void hideHints() { - howDoesItWorkTextView.setVisibility(View.GONE); - hideHintButton.setVisibility(View.GONE); - //actionHelpMenuItem.setVisible(true); - } - @Nullable @org.jetbrains.annotations.Nullable @Override @@ -711,9 +691,6 @@ switch (fragmentType) { case ARRIVALS: prepareGUIForBusLines(); - if (getOption(OPTION_SHOW_LEGEND, true)) { - showHints(); - } break; case STOPS: prepareGUIForBusStops(); diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.java --- a/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.java +++ b/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.java @@ -29,13 +29,13 @@ import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProvider; import androidx.loader.app.LoaderManager; import androidx.loader.content.CursorLoader; import androidx.loader.content.Loader; import androidx.core.util.Pair; import androidx.preference.PreferenceManager; import androidx.appcompat.widget.AppCompatButton; -import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.work.WorkInfo; @@ -51,21 +51,21 @@ import it.reyboz.bustorino.R; import it.reyboz.bustorino.adapters.ArrivalsStopAdapter; import it.reyboz.bustorino.backend.*; -import it.reyboz.bustorino.backend.FiveTAPIFetcher.QueryType; import it.reyboz.bustorino.backend.mato.MapiArrivalRequest; import it.reyboz.bustorino.data.DatabaseUpdate; import it.reyboz.bustorino.data.NextGenDB; import it.reyboz.bustorino.middleware.AppLocationManager; import it.reyboz.bustorino.data.AppDataProvider; -import it.reyboz.bustorino.data.NextGenDB.Contract.*; import it.reyboz.bustorino.adapters.SquareStopAdapter; import it.reyboz.bustorino.middleware.AutoFitGridLayoutManager; import it.reyboz.bustorino.util.LocationCriteria; import it.reyboz.bustorino.util.StopSorterByDistance; +import it.reyboz.bustorino.viewmodels.NearbyStopsViewModel; +import org.jetbrains.annotations.NotNull; import java.util.*; -public class NearbyStopsFragment extends Fragment implements LoaderManager.LoaderCallbacks { +public class NearbyStopsFragment extends Fragment { public enum FragType{ STOPS(1), ARRIVALS(2); @@ -100,9 +100,9 @@ private SquareStopAdapter dataAdapter; private AutoFitGridLayoutManager gridLayoutManager; - private Location lastReceivedLocation = null; + private GPSPoint lastPosition = null; private ProgressBar circlingProgressBar,flatProgressBar; - private int distance; + private int distance = 10; protected SharedPreferences globalSharedPref; private SharedPreferences.OnSharedPreferenceChangeListener preferenceChangeListener; private TextView messageTextView,titleTextView; @@ -125,6 +125,9 @@ private ArrayList currentNearbyStops = new ArrayList<>(); + //ViewModel + private NearbyStopsViewModel viewModel; + public NearbyStopsFragment() { // Required empty public constructor } @@ -153,7 +156,7 @@ setFragmentType(FragType.fromNum(getArguments().getInt(FRAGMENT_TYPE_KEY))); } locManager = AppLocationManager.getInstance(getContext()); - fragmentLocationListener = new FragmentLocationListener(this); + fragmentLocationListener = new FragmentLocationListener(); if (getContext()!=null) { globalSharedPref = getContext().getSharedPreferences(getString(R.string.mainSharedPreferences), Context.MODE_PRIVATE); globalSharedPref.registerOnSharedPreferenceChangeListener(preferenceChangeListener); @@ -191,16 +194,33 @@ if (wi.getState() == WorkInfo.State.RUNNING && locManager.isRequesterRegistered(fragmentLocationListener)) { locManager.removeLocationRequestFor(fragmentLocationListener); dbUpdateRunning = true; - } else if(!locManager.isRequesterRegistered(fragmentLocationListener)){ - locManager.addLocationRequestFor(fragmentLocationListener); + } else{ + //start the request + if(!locManager.isRequesterRegistered(fragmentLocationListener)) + locManager.addLocationRequestFor(fragmentLocationListener); dbUpdateRunning = false; } } }); + + //observe the livedata + viewModel.getStopsAtDistance().observe(getViewLifecycleOwner(), stops -> { + if (!dbUpdateRunning && (stops.size() < MIN_NUM_STOPS && distance <= MAX_DISTANCE)) { + distance = distance + 40; + viewModel.requestStopsAtDistance(distance, true); + //Log.d(DEBUG_TAG, "Doubling distance now!"); + return; + } + if(!stops.isEmpty()) { + currentNearbyStops =stops; + showStopsInViews(currentNearbyStops, lastPosition); + } + }); return root; } + /** * Use this method to set the fragment type * @param type the type, TYPE_ARRIVALS or TYPE_STOPS @@ -221,7 +241,6 @@ @Override public void onAttach(@NonNull Context context) { super.onAttach(context); - /// TODO: RISOLVERE PROBLEMA: il context qui e' l'Activity non il Fragment if (context instanceof FragmentListenerMain) { mListener = (FragmentListenerMain) context; } else { @@ -229,6 +248,7 @@ + " must implement OnFragmentInteractionListener"); } Log.d(DEBUG_TAG, "OnAttach called"); + viewModel = new ViewModelProvider(this).get(NearbyStopsViewModel.class); } @Override @@ -289,8 +309,7 @@ MIN_NUM_STOPS = 5; } if(BuildConfig.DEBUG) - Log.d(DEBUG_TAG, "Max distance for stops: "+MAX_DISTANCE+ - ", Min number of stops: "+MIN_NUM_STOPS); + Log.d(DEBUG_TAG, "Max distance for stops: "+MAX_DISTANCE+ ", Min number of stops: "+MIN_NUM_STOPS); } @@ -308,73 +327,29 @@ if(arrivalsManager!=null) arrivalsManager.cancelAllRequests(); } - @NonNull - @Override - public Loader onCreateLoader(int id, Bundle args) { - //BUILD URI - if (args!=null) - lastReceivedLocation = args.getParcelable(BUNDLE_LOCATION); - Uri.Builder builder = new Uri.Builder(); - builder.scheme("content").authority(AppDataProvider.AUTHORITY) - .appendPath("stops").appendPath("location") - .appendPath(String.valueOf(lastReceivedLocation.getLatitude())) - .appendPath(String.valueOf(lastReceivedLocation.getLongitude())) - .appendPath(String.valueOf(distance)); //distance - CursorLoader cl = new CursorLoader(getContext(),builder.build(),NextGenDB.QUERY_COLUMN_stops_all,null,null,null); - cl.setUpdateThrottle(2000); - return cl; - } - - - @Override - public void onLoadFinished(@NonNull Loader loader, Cursor cursor) { - if (0 > MAX_DISTANCE) throw new AssertionError(); - //Cursor might be null - if (cursor == null) { - Log.e(DEBUG_TAG, "Null cursor, something really wrong happened"); - return; - } - Log.d(DEBUG_TAG, "Num stops found: " + cursor.getCount() + ", Current distance: " + distance); - - if (!dbUpdateRunning && (cursor.getCount() < MIN_NUM_STOPS && distance <= MAX_DISTANCE)) { - distance = distance * 2; - Bundle d = new Bundle(); - d.putParcelable(BUNDLE_LOCATION, lastReceivedLocation); - getLoaderManager().restartLoader(LOADER_ID, d, this); - //Log.d(DEBUG_TAG, "Doubling distance now!"); - return; - } - Log.d("LoadFromCursor", "Number of nearby stops: " + cursor.getCount()); - //////// - if(cursor.getCount()>0) - currentNearbyStops = NextGenDB.getStopsFromCursorAllFields(cursor); - - showCurrentStops(); - } - /** * Display the stops, or run new set of requests for arrivals */ - private void showCurrentStops(){ - if (currentNearbyStops.isEmpty()) { + private void showStopsInViews(ArrayList stops, GPSPoint location){ + if (stops.isEmpty()) { setNoStopsLayout(); return; } double minDistance = Double.POSITIVE_INFINITY; - for(Stop s: currentNearbyStops){ - minDistance = Math.min(minDistance, s.getDistanceFromLocation(lastReceivedLocation)); + for(Stop s: stops){ + minDistance = Math.min(minDistance, s.getDistanceFromLocation(location.getLatitude(), location.getLongitude())); } //quick trial to hopefully always get the stops in the correct order - Collections.sort(currentNearbyStops,new StopSorterByDistance(lastReceivedLocation)); + Collections.sort(stops,new StopSorterByDistance(location)); switch (fragment_type){ case STOPS: - showStopsInRecycler(currentNearbyStops); + showStopsInRecycler(stops); break; case ARRIVALS: - arrivalsManager = new ArrivalsManager(currentNearbyStops); + arrivalsManager = new ArrivalsManager(stops); flatProgressBar.setVisibility(View.VISIBLE); flatProgressBar.setProgress(0); flatProgressBar.setIndeterminate(false); @@ -386,10 +361,6 @@ } - @Override - public void onLoaderReset(@NonNull Loader loader) { - } - /** * To enable targeting from the Button */ @@ -420,7 +391,7 @@ fragmentLocationListener.lastUpdateTime = -1; //locManager.removeLocationRequestFor(fragmentLocationListener); //locManager.addLocationRequestFor(fragmentLocationListener); - showCurrentStops(); + showStopsInViews(currentNearbyStops, lastPosition); } //useful methods @@ -429,12 +400,12 @@ private void showStopsInRecycler(List stops){ if(firstLocForStops) { - dataAdapter = new SquareStopAdapter(stops, mListener, lastReceivedLocation); + dataAdapter = new SquareStopAdapter(stops, mListener, lastPosition); gridRecyclerView.setAdapter(dataAdapter); firstLocForStops = false; }else { dataAdapter.setStops(stops); - dataAdapter.setUserPosition(lastReceivedLocation); + dataAdapter.setUserPosition(lastPosition); } dataAdapter.notifyDataSetChanged(); @@ -449,7 +420,7 @@ } private void showArrivalsInRecycler(List palinas){ - Collections.sort(palinas,new StopSorterByDistance(lastReceivedLocation)); + Collections.sort(palinas,new StopSorterByDistance(lastPosition)); final ArrayList> routesPairList = new ArrayList<>(10); //int maxNum = Math.min(MAX_STOPS, stopList.size()); @@ -467,11 +438,11 @@ return; } if(firstLocForArrivals){ - arrivalsStopAdapter = new ArrivalsStopAdapter(routesPairList,mListener,getContext(),lastReceivedLocation); + arrivalsStopAdapter = new ArrivalsStopAdapter(routesPairList,mListener,getContext(),lastPosition); gridRecyclerView.setAdapter(arrivalsStopAdapter); firstLocForArrivals = false; } else { - arrivalsStopAdapter.setRoutesPairListAndPosition(routesPairList,lastReceivedLocation); + arrivalsStopAdapter.setRoutesPairListAndPosition(routesPairList,lastPosition); } //arrivalsStopAdapter.notifyDataSetChanged(); @@ -583,27 +554,31 @@ */ class FragmentLocationListener implements AppLocationManager.LocationRequester{ - LoaderManager.LoaderCallbacks callbacks; private int oldLocStatus = -2; private LocationCriteria cr; private long lastUpdateTime = -1; - public FragmentLocationListener(LoaderManager.LoaderCallbacks callbacks) { - this.callbacks = callbacks; - } @Override public void onLocationChanged(Location location) { //set adapter - float accuracy = location.getAccuracy(); - if(accuracy<100 && !dbUpdateRunning) { - distance = 20; - final Bundle msgBundle = new Bundle(); - msgBundle.putParcelable(BUNDLE_LOCATION,location); - getLoaderManager().restartLoader(LOADER_ID,msgBundle,callbacks); + + if(location==null){ + Log.e(DEBUG_TAG, "Location is null, cannot request stops"); + return; + } else if(viewModel==null){ + return; + } + if(location.getAccuracy()<100 && !dbUpdateRunning) { + if(viewModel.getDistanceMtLiveData().getValue()==null){ + //never run request + distance = 40; + } + lastPosition = new GPSPoint(location.getLatitude(), location.getLongitude()); + viewModel.requestStopsAtDistance(location.getLatitude(), location.getLongitude(), distance, true); } lastUpdateTime = System.currentTimeMillis(); - Log.d("BusTO:NearPositListen","can start loader "+ !dbUpdateRunning); + Log.d("BusTO:NearPositListen","can start request for stops: "+ !dbUpdateRunning); } @Override @@ -623,9 +598,9 @@ } @Override - public LocationCriteria getLocationCriteria() { + public @NotNull LocationCriteria getLocationCriteria() { - return new LocationCriteria(120,TIME_INTERVAL_REQUESTS); + return new LocationCriteria(200,TIME_INTERVAL_REQUESTS); } @Override diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java --- a/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java +++ b/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java @@ -16,7 +16,7 @@ public abstract class ScreenBaseFragment extends Fragment { - protected final String PREF_FILE= BuildConfig.APPLICATION_ID+".fragment_prefs"; + protected final static String PREF_FILE= BuildConfig.APPLICATION_ID+".fragment_prefs"; protected void setOption(String optionName, boolean value) { Context mContext = getContext(); @@ -27,8 +27,8 @@ protected boolean getOption(String optionName, boolean optDefault) { Context mContext = getContext(); - SharedPreferences preferences = mContext.getSharedPreferences(PREF_FILE, MODE_PRIVATE); - return preferences.getBoolean(optionName, optDefault); + assert mContext != null; + return getOption(mContext, optionName, optDefault); } protected void showToastMessage(int messageID, boolean short_lenght) { @@ -52,4 +52,14 @@ */ @Nullable public abstract View getBaseViewForSnackBar(); + + public static boolean getOption(Context context, String optionName, boolean optDefault){ + SharedPreferences preferences = context.getSharedPreferences(PREF_FILE, MODE_PRIVATE); + return preferences.getBoolean(optionName, optDefault); + } + public static void setOption(Context context,String optionName, boolean value) { + SharedPreferences.Editor editor = context.getSharedPreferences(PREF_FILE, MODE_PRIVATE).edit(); + editor.putBoolean(optionName, value); + editor.apply(); + } } diff --git a/app/src/main/java/it/reyboz/bustorino/util/RoutePositionSorter.java b/app/src/main/java/it/reyboz/bustorino/util/RoutePositionSorter.java --- a/app/src/main/java/it/reyboz/bustorino/util/RoutePositionSorter.java +++ b/app/src/main/java/it/reyboz/bustorino/util/RoutePositionSorter.java @@ -25,26 +25,31 @@ import it.reyboz.bustorino.backend.Route; import it.reyboz.bustorino.backend.Stop; import it.reyboz.bustorino.backend.utils; +import org.osmdroid.api.IGeoPoint; import java.util.Collections; import java.util.Comparator; import java.util.List; public class RoutePositionSorter implements Comparator> { - private final Location loc; + private final double latPos, longPos; private final double minutialmetro = 6.0/100; //v = 5km/h private final double distancemultiplier = 2./3; - public RoutePositionSorter(Location loc) { - this.loc = loc; + public RoutePositionSorter(double latitude, double longitude){ + latPos = latitude; + longPos = longitude; + } + public RoutePositionSorter(IGeoPoint position){ + this(position.getLatitude(), position.getLongitude()); } @Override public int compare(Pair pair1, Pair pair2) throws NullPointerException{ int delta = 0; final Stop stop1 = pair1.first, stop2 = pair2.first; - double dist1 = utils.measuredistanceBetween(loc.getLatitude(),loc.getLongitude(), + double dist1 = utils.measuredistanceBetween(latPos,longPos, stop1.getLatitude(),stop1.getLongitude()); - double dist2 = utils.measuredistanceBetween(loc.getLatitude(),loc.getLongitude(), + double dist2 = utils.measuredistanceBetween(latPos,longPos, stop2.getLatitude(),stop2.getLongitude()); final List passaggi1 = pair1.second.passaggi, passaggi2 = pair2.second.passaggi; diff --git a/app/src/main/java/it/reyboz/bustorino/util/StopSorterByDistance.java b/app/src/main/java/it/reyboz/bustorino/util/StopSorterByDistance.java --- a/app/src/main/java/it/reyboz/bustorino/util/StopSorterByDistance.java +++ b/app/src/main/java/it/reyboz/bustorino/util/StopSorterByDistance.java @@ -18,19 +18,21 @@ package it.reyboz.bustorino.util; import android.location.Location; +import it.reyboz.bustorino.backend.GPSPoint; import it.reyboz.bustorino.backend.Stop; import java.util.Comparator; public class StopSorterByDistance implements Comparator { - private final Location locToCompare; + private final double latitude; + private final double longitude; - public StopSorterByDistance(Location locToCompare) { - this.locToCompare = locToCompare; + public StopSorterByDistance(GPSPoint geoPoint) { + latitude = geoPoint.getLatitude(); + longitude = geoPoint.getLongitude(); } - @Override public int compare(Stop o1, Stop o2) { - return (int) (o1.getDistanceFromLocation(locToCompare)-o2.getDistanceFromLocation(locToCompare)); + return (int) (o1.getDistanceFromLocation(latitude, longitude)-o2.getDistanceFromLocation(latitude, longitude)); } } diff --git a/app/src/main/java/it/reyboz/bustorino/viewmodels/NearbyStopsViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/NearbyStopsViewModel.kt new file mode 100644 --- /dev/null +++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/NearbyStopsViewModel.kt @@ -0,0 +1,75 @@ +package it.reyboz.bustorino.viewmodels + +import android.app.Application +import android.location.Location +import android.util.Log +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.MutableLiveData +import it.reyboz.bustorino.backend.GPSPoint +import it.reyboz.bustorino.backend.Stop +import it.reyboz.bustorino.data.OldDataRepository +import java.util.ArrayList +import java.util.concurrent.Executors + +class NearbyStopsViewModel(application: Application): AndroidViewModel(application) { + + private val executor = Executors.newFixedThreadPool(2) + private val oldRepo = OldDataRepository(executor, application) + + + val locationLiveData = MutableLiveData() + val distanceMtLiveData = MutableLiveData(40) + + + val stopsAtDistance = MutableLiveData>() + + private val callback = + OldDataRepository.Callback> { res -> + if(res.isSuccess){ + stopsAtDistance.postValue(res.result) + Log.d(DEBUG_TAG, "Setting value of stops in bounding box") + } + } + + /** + * Request stop in location [latitude], [longitude], at distance [distanceMeters] + * If [saveValues] is true, store the position and the distance used + */ + fun requestStopsAtDistance(latitude: Double, longitude: Double, distanceMeters: Int, saveValues: Boolean){ + if(saveValues){ + locationLiveData.postValue(GPSPoint(latitude, longitude)) + distanceMtLiveData.postValue(distanceMeters) + } + oldRepo.requestStopsWithinDistance(latitude, longitude, distanceMeters, callback) + } + + /** + * Request stops using the previously saved location + */ + fun requestStopsAtDistance(distanceMeters: Int, saveValue: Boolean){ + if(saveValue){ + distanceMtLiveData.postValue(distanceMeters) + } + oldRepo.requestStopsWithinDistance( + locationLiveData.value!!.latitude, + locationLiveData.value!!.longitude, distanceMeters, callback) + } + + + fun setLocation(location: Location){ + locationLiveData.postValue(GPSPoint(location.latitude, location.longitude)) + } + fun setLocation(location: GPSPoint){ + locationLiveData.postValue(location) + } + fun setLastDistance(distanceMeters: Int){ + distanceMtLiveData.postValue(distanceMeters) + } + + + + + companion object{ + private const val DEBUG_TAG = "BusTO-NearbyStopVwModel" + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/top_bottom_border_radius.xml b/app/src/main/res/drawable/top_bottom_border_radius.xml new file mode 100644 --- /dev/null +++ b/app/src/main/res/drawable/top_bottom_border_radius.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/top_bottom_border_radius_with_border.xml b/app/src/main/res/drawable/top_bottom_border_radius_with_border.xml new file mode 100644 --- /dev/null +++ b/app/src/main/res/drawable/top_bottom_border_radius_with_border.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_arrivals.xml b/app/src/main/res/layout/fragment_arrivals.xml --- a/app/src/main/res/layout/fragment_arrivals.xml +++ b/app/src/main/res/layout/fragment_arrivals.xml @@ -5,7 +5,41 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - android:paddingTop="8dp"> + android:paddingTop="8dp" + android:animateLayoutChanges="true"> + + +