commit 1558d602e2e96b16bb828e9b1daf2b6f1488c863 Author: Fabio Mazza Date: Mon Jan 29 00:13:19 2024 +0100 Show the nearby stops fragment when giving permission 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 2d96048..6d043e9 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java +++ b/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java @@ -53,6 +53,7 @@ import it.reyboz.bustorino.middleware.BarcodeScanOptions; import it.reyboz.bustorino.middleware.BarcodeScanUtils; import it.reyboz.bustorino.util.LocationCriteria; import it.reyboz.bustorino.util.Permissions; +import org.jetbrains.annotations.NotNull; import static it.reyboz.bustorino.backend.utils.getBusStopIDFromUri; import static it.reyboz.bustorino.util.Permissions.LOCATION_PERMISSIONS; @@ -92,11 +93,11 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL */ private static final int SEARCH_BY_NAME = 0; private static final int SEARCH_BY_ID = 1; - private static final int SEARCH_BY_ROUTE = 2; // TODO: implement this -- https://gitpull.it/T12 + //private static final int SEARCH_BY_ROUTE = 2; // implement this -- DONE! private int searchMode; //private ImageButton addToFavorites; //// HIDDEN BUT IMPORTANT ELEMENTS //// - FragmentManager fragMan; + FragmentManager childFragMan; Handler mainHandler; private final Runnable refreshStop = new Runnable() { public void run() { @@ -105,8 +106,8 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL ArrivalsFetcher[] arrivalsFetchers = new ArrivalsFetcher[fetcherList.size()]; arrivalsFetchers = fetcherList.toArray(arrivalsFetchers); - if (fragMan.findFragmentById(R.id.resultFrame) instanceof ArrivalsFragment) { - ArrivalsFragment fragment = (ArrivalsFragment) fragMan.findFragmentById(R.id.resultFrame); + if (childFragMan.findFragmentById(R.id.resultFrame) instanceof ArrivalsFragment) { + ArrivalsFragment fragment = (ArrivalsFragment) childFragMan.findFragmentById(R.id.resultFrame); if (fragment == null){ //we create a new fragment, which is WRONG Log.e("BusTO-RefreshStop", "Asking for refresh when there is no fragment"); @@ -166,7 +167,7 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL Log.d(DEBUG_TAG, "Permissions for location are: "+result); if(Boolean.TRUE.equals(result.get(Manifest.permission.ACCESS_COARSE_LOCATION)) - && Boolean.TRUE.equals(result.get(Manifest.permission.ACCESS_FINE_LOCATION))){ + || Boolean.TRUE.equals(result.get(Manifest.permission.ACCESS_FINE_LOCATION))){ locationPermissionGranted = true; Log.w(DEBUG_TAG, "Starting position"); if (mListener!= null && getContext()!=null){ @@ -178,7 +179,7 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL //showNearbyStopsFragment(); Log.d(DEBUG_TAG, "We have location permission"); if(pendingNearbyStopsFragmentRequest){ - showNearbyFragmentIfNeeded(cr); + showNearbyFragmentIfPossible(); pendingNearbyStopsFragmentRequest = false; } } @@ -203,7 +204,7 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL //pendingNearbyStopsRequest = false; if (getContext()!= null && !isNearbyFragmentShown()) //mainHandler.post(new NearbyStopsRequester(getContext(), cr)); - showNearbyFragmentIfNeeded(cr); + showNearbyFragmentIfPossible(); } } @@ -227,7 +228,7 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL "we have no location permission"); pendingNearbyStopsFragmentRequest = true; //mainHandler.post(new NearbyStopsRequester(getContext(), cr)); - showNearbyFragmentIfNeeded(cr); + showNearbyFragmentIfPossible(); } } @@ -313,8 +314,8 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL searchButton.setOnClickListener(this::onSearchClick); // Fragment stuff - fragMan = getChildFragmentManager(); - fragMan.addOnBackStackChangedListener(() -> Log.d("BusTO Main Fragment", "BACK STACK CHANGED")); + childFragMan = getChildFragmentManager(); + childFragMan.addOnBackStackChangedListener(() -> Log.d("BusTO Main Fragment", "BACK STACK CHANGED")); fragmentHelper = new FragmentHelper(this, getChildFragmentManager(), getContext(), R.id.resultFrame); setSearchModeBusStopID(); @@ -430,7 +431,7 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL pendingNearbyStopsFragmentRequest = true; } else { - showNearbyFragmentIfNeeded(cr); + showNearbyFragmentIfPossible(); } } else { //The Introductory Activity is about to be started, hence pause the request and show later @@ -462,7 +463,7 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL pendingNearbyStopsFragmentRequest = true; } else { - showNearbyFragmentIfNeeded(cr); + showNearbyFragmentIfPossible(); } //deactivate flag pendingIntroRun = false; @@ -474,8 +475,21 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL } //don't request permission // if we have a pending stopID request, do it Log.d(DEBUG_TAG, "Pending stop ID for arrivals: "+pendingStopID); - //this is the second time we are attaching this fragment + //this is the second time we are attaching this fragment -> Log.d(DEBUG_TAG, "Waiting for new stop request: "+ suppressArrivalsReload); + //TODO: if we come back to this from another fragment, and the user has given again the permission + // for the Location, we should show the Nearby Stops + if(!suppressArrivalsReload && pendingStopID==null){ + //none of the following cases are true + // check if we are showing any fragment + final Fragment fragment = getChildFragmentManager().findFragmentById(R.id.resultFrame); + if(fragment==null || swipeRefreshLayout.getVisibility() != View.VISIBLE){ + //we are not showing anything + if(Permissions.anyLocationPermissionsGranted(getContext())){ + showNearbyFragmentIfPossible(); + } + } + } if (suppressArrivalsReload){ // we have to suppress the reloading of the (possible) ArrivalsFragment Fragment fragment = getChildFragmentManager().findFragmentById(R.id.resultFrame); @@ -603,7 +617,7 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL } protected boolean isNearbyFragmentShown(){ Fragment fragment = getChildFragmentManager().findFragmentByTag(NearbyStopsFragment.FRAGMENT_TAG); - return (fragment!= null && fragment.isVisible()); + return (fragment!= null && fragment.isResumed()); } /** @@ -650,14 +664,14 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL private void actuallyShowNearbyStopsFragment(){ swipeRefreshLayout.setVisibility(View.VISIBLE); - final Fragment existingFrag = fragMan.findFragmentById(R.id.resultFrame); + final Fragment existingFrag = childFragMan.findFragmentById(R.id.resultFrame); // fragment; 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); - FragmentTransaction ft = fragMan.beginTransaction(); + FragmentTransaction ft = childFragMan.beginTransaction(); ft.replace(R.id.resultFrame, fragment, NearbyStopsFragment.FRAGMENT_TAG); if (getActivity()!=null && !getActivity().isFinishing()) @@ -777,28 +791,23 @@ public class MainScreenFragment extends ScreenBaseFragment implements FragmentL requestPermissionLauncher.launch(LOCATION_PERMISSIONS); } - private void showNearbyFragmentIfNeeded(Criteria cr){ - if(isNearbyFragmentShown()) { + private void showNearbyFragmentIfPossible() { + if (isNearbyFragmentShown()) { //nothing to do - Log.w(DEBUG_TAG, "launched nearby fragment request but we already are showing"); + Log.w(DEBUG_TAG, "Asked to show nearby fragment but we already are showing it"); return; } - if(getContext()==null){ + if (getContext() == null) { Log.e(DEBUG_TAG, "Wanting to show nearby fragment but context is null"); return; } - AppLocationManager appLocationManager = AppLocationManager.getInstance(getContext()); - final boolean haveProviders = appLocationManager.anyLocationProviderMatchesCriteria(cr); - if (haveProviders - && fragmentHelper.getLastSuccessfullySearchedBusStop() == null - && !fragMan.isDestroyed()) { + if (fragmentHelper.getLastSuccessfullySearchedBusStop() == null + && !childFragMan.isDestroyed()) { //Go ahead with the request actuallyShowNearbyStopsFragment(); pendingNearbyStopsFragmentRequest = false; - } else if(!haveProviders){ - Log.e(DEBUG_TAG, "NO PROVIDERS FOR POSITION"); } } /////////// LOCATION METHODS ////////// 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 7bd2380..1be3f1f 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.java +++ b/app/src/main/java/it/reyboz/bustorino/fragments/NearbyStopsFragment.java @@ -17,14 +17,19 @@ */ package it.reyboz.bustorino.fragments; +import android.annotation.SuppressLint; import android.content.Context; import android.content.SharedPreferences; import android.location.Location; +import android.location.LocationManager; import android.os.Bundle; +import androidx.activity.result.ActivityResultLauncher; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.location.LocationListenerCompat; +import androidx.core.location.LocationManagerCompat; import androidx.fragment.app.Fragment; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; @@ -50,6 +55,7 @@ import it.reyboz.bustorino.middleware.AppLocationManager; import it.reyboz.bustorino.adapters.SquareStopAdapter; import it.reyboz.bustorino.middleware.AutoFitGridLayoutManager; import it.reyboz.bustorino.util.LocationCriteria; +import it.reyboz.bustorino.util.Permissions; import it.reyboz.bustorino.util.StopSorterByDistance; import it.reyboz.bustorino.viewmodels.NearbyStopsViewModel; import org.jetbrains.annotations.NotNull; @@ -73,6 +79,7 @@ public class NearbyStopsFragment extends Fragment { } } } + private enum LocationShowingStatus {SEARCHING, FIRST_FIX, DISABLED, NO_PERMISSION} private FragmentListenerMain mListener; private FragmentLocationListener fragmentLocationListener; @@ -84,9 +91,6 @@ public class NearbyStopsFragment extends Fragment { public final static String FRAGMENT_TAG="NearbyStopsFrag"; - //data Bundle - private final String BUNDLE_LOCATION = "location"; - private final int LOADER_ID = 0; private RecyclerView gridRecyclerView; private SquareStopAdapter dataAdapter; @@ -106,7 +110,7 @@ public class NearbyStopsFragment extends Fragment { private Integer MAX_DISTANCE = -3; private int MIN_NUM_STOPS = -1; private int TIME_INTERVAL_REQUESTS = -1; - private AppLocationManager locManager; + private LocationManager locManager; //These are useful for the case of nearby arrivals private NearbyArrivalsDownloader arrivalsManager = null; @@ -117,6 +121,8 @@ public class NearbyStopsFragment extends Fragment { private ArrayList currentNearbyStops = new ArrayList<>(); private NearbyArrivalsDownloader nearbyArrivalsDownloader; + private LocationShowingStatus showingStatus = LocationShowingStatus.NO_PERMISSION; + private final NearbyArrivalsDownloader.ArrivalsListener arrivalsListener = new NearbyArrivalsDownloader.ArrivalsListener() { @Override public void setProgress(int completedRequests, int pendingRequests) { @@ -172,7 +178,7 @@ public class NearbyStopsFragment extends Fragment { if (getArguments() != null) { setFragmentType(FragType.fromNum(getArguments().getInt(FRAGMENT_TYPE_KEY))); } - locManager = AppLocationManager.getInstance(getContext()); + locManager = (LocationManager) requireContext().getSystemService(Context.LOCATION_SERVICE); fragmentLocationListener = new FragmentLocationListener(); if (getContext()!=null) { globalSharedPref = getContext().getSharedPreferences(getString(R.string.mainSharedPreferences), Context.MODE_PRIVATE); @@ -205,19 +211,23 @@ public class NearbyStopsFragment extends Fragment { switchButton.setOnClickListener(v -> switchFragmentType()); Log.d(DEBUG_TAG, "onCreateView"); + final Context appContext =requireContext().getApplicationContext(); DatabaseUpdate.watchUpdateWorkStatus(getContext(), this, new Observer>() { + @SuppressLint("MissingPermission") @Override public void onChanged(List workInfos) { if(workInfos.isEmpty()) return; WorkInfo wi = workInfos.get(0); - if (wi.getState() == WorkInfo.State.RUNNING && locManager.isRequesterRegistered(fragmentLocationListener)) { - locManager.removeLocationRequestFor(fragmentLocationListener); + if (wi.getState() == WorkInfo.State.RUNNING && fragmentLocationListener.isRegistered) { + locManager.removeUpdates(fragmentLocationListener); + fragmentLocationListener.isRegistered = true; dbUpdateRunning = true; } else{ //start the request - if(!locManager.isRequesterRegistered(fragmentLocationListener)) - locManager.addLocationRequestFor(fragmentLocationListener); + if(!fragmentLocationListener.isRegistered){ + requestLocationUpdates(); + } dbUpdateRunning = false; } } @@ -237,9 +247,27 @@ public class NearbyStopsFragment extends Fragment { showStopsInViews(currentNearbyStops, lastPosition); } }); + if(Permissions.anyLocationPermissionsGranted(appContext)){ + setShowingStatus(LocationShowingStatus.SEARCHING); + } else { + setShowingStatus(LocationShowingStatus.NO_PERMISSION); + + } return root; } + //because linter is stupid and cannot look inside *anyLocationPermissionGranted* + @SuppressLint("MissingPermission") + private boolean requestLocationUpdates(){ + if(Permissions.anyLocationPermissionsGranted(requireContext())) { + locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, + 3000, 10.0f, fragmentLocationListener + ); + fragmentLocationListener.isRegistered = true; + return true; + } else return false; + } + /** @@ -257,6 +285,40 @@ public class NearbyStopsFragment extends Fragment { } } + private void setShowingStatus(@NonNull LocationShowingStatus newStatus){ + if(newStatus == showingStatus){ + Log.d(DEBUG_TAG, "Asked to set new displaying status but it's the same"); + return; + } + switch (newStatus){ + case FIRST_FIX: + circlingProgressBar.setVisibility(View.GONE); + loadingTextView.setVisibility(View.GONE); + gridRecyclerView.setVisibility(View.VISIBLE); + messageTextView.setVisibility(View.GONE); + break; + case NO_PERMISSION: + circlingProgressBar.setVisibility(View.GONE); + loadingTextView.setVisibility(View.GONE); + messageTextView.setText(R.string.enable_position_message_nearby); + messageTextView.setVisibility(View.VISIBLE); + break; + case DISABLED: + if (showingStatus== LocationShowingStatus.SEARCHING){ + circlingProgressBar.setVisibility(View.GONE); + loadingTextView.setVisibility(View.GONE); + } + messageTextView.setText(R.string.enableGpsText); + messageTextView.setVisibility(View.VISIBLE); + break; + case SEARCHING: + circlingProgressBar.setVisibility(View.VISIBLE); + loadingTextView.setVisibility(View.VISIBLE); + gridRecyclerView.setVisibility(View.GONE); + messageTextView.setVisibility(View.GONE); + } + showingStatus = newStatus; + } @Override @@ -270,6 +332,8 @@ public class NearbyStopsFragment extends Fragment { } Log.d(DEBUG_TAG, "OnAttach called"); viewModel = new ViewModelProvider(this).get(NearbyStopsViewModel.class); + + } @Override @@ -277,7 +341,8 @@ public class NearbyStopsFragment extends Fragment { super.onPause(); gridRecyclerView.setAdapter(null); - locManager.removeLocationRequestFor(fragmentLocationListener); + locManager.removeUpdates(fragmentLocationListener); + fragmentLocationListener.isRegistered = false; Log.d(DEBUG_TAG,"On paused called"); } @@ -285,8 +350,9 @@ public class NearbyStopsFragment extends Fragment { public void onResume() { super.onResume(); try{ - if(!dbUpdateRunning && !locManager.isRequesterRegistered(fragmentLocationListener)) - locManager.addLocationRequestFor(fragmentLocationListener); + if(!dbUpdateRunning && !fragmentLocationListener.isRegistered) { + requestLocationUpdates(); + } } catch (SecurityException ex){ //ignored //try another location provider @@ -519,12 +585,10 @@ public class NearbyStopsFragment extends Fragment { /** * Local locationListener, to use for the GPS */ - class FragmentLocationListener implements AppLocationManager.LocationRequester{ + class FragmentLocationListener implements LocationListenerCompat { - private int oldLocStatus = -2; - private LocationCriteria cr; private long lastUpdateTime = -1; - + public boolean isRegistered = false; @Override public void onLocationChanged(Location location) { @@ -548,6 +612,27 @@ public class NearbyStopsFragment extends Fragment { Log.d("BusTO:NearPositListen","can start request for stops: "+ !dbUpdateRunning); } + @Override + public void onProviderEnabled(@NonNull String provider) { + Log.d(DEBUG_TAG, "Location provider "+provider+" enabled"); + if(provider.equals(LocationManager.GPS_PROVIDER)){ + setShowingStatus(LocationShowingStatus.SEARCHING); + } + } + + @Override + public void onProviderDisabled(@NonNull String provider) { + Log.d(DEBUG_TAG, "Location provider "+provider+" disabled"); + if(provider.equals(LocationManager.GPS_PROVIDER)) { + setShowingStatus(LocationShowingStatus.DISABLED); + } + } + + @Override + public void onStatusChanged(@NonNull @NotNull String provider, int status, @Nullable @org.jetbrains.annotations.Nullable Bundle extras) { + LocationListenerCompat.super.onStatusChanged(provider, status, extras); + } + /* @Override public void onLocationStatusChanged(int status) { switch(status){ @@ -587,5 +672,7 @@ public class NearbyStopsFragment extends Fragment { public void onLocationDisabled() { } + + */ } } 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 e2cb13f..48834f5 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java +++ b/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java @@ -1,16 +1,22 @@ package it.reyboz.bustorino.fragments; +import android.Manifest; import android.content.Context; import android.content.SharedPreferences; import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.Toast; +import androidx.activity.result.ActivityResultCallback; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import it.reyboz.bustorino.BuildConfig; +import java.util.Map; + import static android.content.Context.MODE_PRIVATE; public abstract class ScreenBaseFragment extends Fragment { @@ -61,4 +67,24 @@ public abstract class ScreenBaseFragment extends Fragment { editor.putBoolean(optionName, value); editor.apply(); } + public ActivityResultLauncher getPositionRequestLauncher(LocationRequestListener listener){ + return registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<>() { + @Override + public void onActivityResult(Map result) { + if (result == null) return; + + if (result.get(Manifest.permission.ACCESS_COARSE_LOCATION) == null || + result.get(Manifest.permission.ACCESS_FINE_LOCATION) == null) + return; + final boolean coarseGranted = Boolean.TRUE.equals(result.get(Manifest.permission.ACCESS_COARSE_LOCATION)); + final boolean fineGranted = Boolean.TRUE.equals(result.get(Manifest.permission.ACCESS_FINE_LOCATION)); + + listener.onPermissionResult(coarseGranted, fineGranted); + } + }); + } + + public interface LocationRequestListener{ + void onPermissionResult(boolean isCoarseGranted, boolean isFineGranted); + } } diff --git a/app/src/main/java/it/reyboz/bustorino/middleware/AppLocationManager.kt b/app/src/main/java/it/reyboz/bustorino/middleware/AppLocationManager.kt index f3926b1..0ce8a07 100644 --- a/app/src/main/java/it/reyboz/bustorino/middleware/AppLocationManager.kt +++ b/app/src/main/java/it/reyboz/bustorino/middleware/AppLocationManager.kt @@ -24,6 +24,7 @@ import android.location.* import android.os.Bundle import android.util.Log import androidx.core.content.ContextCompat +import androidx.core.location.LocationListenerCompat import it.reyboz.bustorino.util.LocationCriteria import it.reyboz.bustorino.util.Permissions import java.lang.ref.WeakReference @@ -261,9 +262,9 @@ class AppLocationManager private constructor(context: Context) : LocationListene private const val DEBUG_TAG = "BUSTO LocAdapter" private var instance: AppLocationManager? = null @JvmStatic - fun getInstance(con: Context): AppLocationManager? { + fun getInstance(con: Context): AppLocationManager { if (instance == null) instance = AppLocationManager(con) - return instance + return instance!! } fun checkLocationPermission(context: Context?): Boolean { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ee6af06..4697434 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -150,7 +150,8 @@ Launch manual database update Allow access to location to show it on the map - Please enable GPS + Allow access to location to show stops nearby + Please enable location on the device Database update in progress… Updating the database Force database update