diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt --- a/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt @@ -28,6 +28,7 @@ import android.view.View import android.view.ViewGroup import android.widget.* +import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.content.res.AppCompatResources import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat @@ -53,6 +54,8 @@ import it.reyboz.bustorino.data.gtfs.TripAndPatternWithStops import it.reyboz.bustorino.map.* import it.reyboz.bustorino.map.CustomInfoWindow.TouchResponder +import it.reyboz.bustorino.middleware.LocationUtils +import it.reyboz.bustorino.util.Permissions import it.reyboz.bustorino.viewmodels.LinesViewModel import it.reyboz.bustorino.viewmodels.LivePositionsViewModel import kotlinx.coroutines.delay @@ -88,9 +91,10 @@ private lateinit var switchButton: ImageButton private var favoritesButton: ImageButton? = null - private lateinit var locationIcon: ImageButton + private var locationIcon: ImageButton? = null private var isLineInFavorite = false private var appContext: Context? = null + private var isLocationPermissionOK = false private val lineSharedPrefMonitor = SharedPreferences.OnSharedPreferenceChangeListener { pref, keychanged -> if(keychanged!=PreferencesHolder.PREF_FAVORITE_LINES || lineID.isEmpty()) return@OnSharedPreferenceChangeListener val newFavorites = pref.getStringSet(PreferencesHolder.PREF_FAVORITE_LINES, HashSet()) @@ -146,6 +150,24 @@ private lateinit var stopsOverlay: FolderOverlay private lateinit var locationOverlay: LocationOverlay + private val locationOverlayResponder = object : LocationOverlay.OverlayCallbacks{ + override fun onDisableFollowMyLocation() { + Log.d(DEBUG_TAG, "Follow location disabled") + } + + override fun onEnableFollowMyLocation() { + Log.d(DEBUG_TAG, "Follow location enabled") + } + } + //location request responder + private val locationRequestResLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()){ res -> + //onActivityResult(res: map) + if(res[Permissions.LOCATION_PERMISSIONS[0]] ==true || res[Permissions.LOCATION_PERMISSIONS[1]] ==true) + locationIcon?.let { onPositionIconButtonClick(it) } + else{ + context?.let { Toast.makeText(it,R.string.location_permission_not_granted, Toast.LENGTH_SHORT).show() } + } + } //fragment actions private lateinit var fragmentListener: CommonFragmentListener @@ -209,7 +231,7 @@ if(map.visibility == View.VISIBLE){ map.visibility = View.GONE stopsRecyclerView.visibility = View.VISIBLE - locationIcon.visibility = View.GONE + locationIcon?.visibility = View.GONE viewModel.setMapShowing(false) liveBusViewModel.stopMatoUpdates() @@ -219,7 +241,7 @@ } else{ stopsRecyclerView.visibility = View.GONE map.visibility = View.VISIBLE - locationIcon.visibility = View.VISIBLE + locationIcon?.visibility = View.VISIBLE viewModel.setMapShowing(true) //map.overlayManager.add(busPositionsOverlay) @@ -232,21 +254,15 @@ switchButton.setImageDrawable(AppCompatResources.getDrawable(requireContext(), R.drawable.ic_list_30)) } } - locationIcon.setOnClickListener { - if(locationOverlay.isMyLocationEnabled){ - //switch off - locationOverlay.disableMyLocation() - locationIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.location_circlew_grey)) - //show message - Toast.makeText(requireContext(),R.string.location_disabled,Toast.LENGTH_SHORT).show() - } else{ - //switch on - locationOverlay.enableMyLocation() - locationIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.location_circlew_red)) - //show message - Toast.makeText(requireContext(),R.string.location_enabled,Toast.LENGTH_SHORT).show() - } + locationIcon?.let {view -> + if(!LocationUtils.isLocationEnabled(requireContext()) || !Permissions.anyLocationPermissionsGranted(requireContext())) + setLocationIconEnabled(false) + //set click Listener + view.setOnClickListener(this::onPositionIconButtonClick) } + //set + + viewModel.setRouteIDQuery(lineID) val keySourcePositions = getString(R.string.pref_positions_source) @@ -360,6 +376,48 @@ return rootView } + private fun setLocationIconEnabled(setTrue: Boolean){ + if(setTrue) + locationIcon?.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.location_circlew_red)) + else + locationIcon?.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.location_circlew_grey)) + } + + /** + * Switch position icon from activ + */ + private fun onPositionIconButtonClick(view: View){ + if(locationOverlay.isMyLocationEnabled){ + //switch off + locationOverlay.disableMyLocation() + //set image on respective button + setLocationIconEnabled(false) + if(context!=null) { + if (LocationUtils.isLocationEnabled(context)) { + //show message + Toast.makeText(context, R.string.location_disabled, Toast.LENGTH_SHORT).show() + } + } + } else{ + //switch on + locationOverlay.enableMyLocation() + if(context!=null) { + if(!Permissions.anyLocationPermissionsGranted(context)) { + locationRequestResLauncher.launch(Permissions.LOCATION_PERMISSIONS) + Toast.makeText(context, R.string.enable_position_message_map, Toast.LENGTH_SHORT).show() + } + else if (LocationUtils.isLocationEnabled(context)) { + //set image on button + setLocationIconEnabled(true) + //show message + Toast.makeText(context, R.string.location_enabled, Toast.LENGTH_SHORT).show() + } else{ + Toast.makeText(context, R.string.map_location_disabled_device, Toast.LENGTH_SHORT).show() + } + } + } + } + private fun initializeMap(rootView : View){ val ctx = requireContext().applicationContext Configuration.getInstance().load(ctx, PreferenceManager.getDefaultSharedPreferences(ctx)) @@ -368,16 +426,7 @@ map.let { it.setTileSource(TileSourceFactory.MAPNIK) - locationOverlay = LocationOverlay.createLocationOverlay(true, it, requireContext(), object : LocationOverlay.OverlayCallbacks{ - override fun onDisableFollowMyLocation() { - Log.d(DEBUG_TAG, "Follow location disabled") - } - - override fun onEnableFollowMyLocation() { - Log.d(DEBUG_TAG, "Follow location enabled") - } - - }) + locationOverlay = LocationOverlay.createLocationOverlay(true, it, requireContext(), locationOverlayResponder) locationOverlay.disableFollowLocation() stopsOverlay = FolderOverlay() @@ -409,9 +458,7 @@ mapViewModel.currentZoom.value!!,null,null) //controller.setCenter(GeoPoint(mapViewModel.currentLat.value!!, mapViewModel.currentLong.value!!)) //controller.setZoom(mapViewModel.currentZoom.value!!) - */ - } mapController.setZoom(zoom) mapController.setCenter(centerMap) @@ -424,7 +471,6 @@ zoomToCurrentPattern() firstInit = false - } 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 @@ -53,6 +53,7 @@ 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 @@ */ 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 @@ 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 @@ 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 @@ //showNearbyStopsFragment(); Log.d(DEBUG_TAG, "We have location permission"); if(pendingNearbyStopsFragmentRequest){ - showNearbyFragmentIfNeeded(cr); + showNearbyFragmentIfPossible(); pendingNearbyStopsFragmentRequest = false; } } @@ -203,7 +204,7 @@ //pendingNearbyStopsRequest = false; if (getContext()!= null && !isNearbyFragmentShown()) //mainHandler.post(new NearbyStopsRequester(getContext(), cr)); - showNearbyFragmentIfNeeded(cr); + showNearbyFragmentIfPossible(); } } @@ -227,7 +228,7 @@ "we have no location permission"); pendingNearbyStopsFragmentRequest = true; //mainHandler.post(new NearbyStopsRequester(getContext(), cr)); - showNearbyFragmentIfNeeded(cr); + showNearbyFragmentIfPossible(); } } @@ -313,8 +314,8 @@ 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 @@ 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 @@ pendingNearbyStopsFragmentRequest = true; } else { - showNearbyFragmentIfNeeded(cr); + showNearbyFragmentIfPossible(); } //deactivate flag pendingIntroRun = false; @@ -474,8 +475,21 @@ } //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 @@ } protected boolean isNearbyFragmentShown(){ Fragment fragment = getChildFragmentManager().findFragmentByTag(NearbyStopsFragment.FRAGMENT_TAG); - return (fragment!= null && fragment.isVisible()); + return (fragment!= null && fragment.isResumed()); } /** @@ -650,14 +664,14 @@ 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 @@ 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 --- 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.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 @@ } } } + private enum LocationShowingStatus {SEARCHING, FIRST_FIX, DISABLED, NO_PERMISSION} private FragmentListenerMain mListener; private FragmentLocationListener fragmentLocationListener; @@ -84,9 +91,6 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } } + 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 @@ } Log.d(DEBUG_TAG, "OnAttach called"); viewModel = new ViewModelProvider(this).get(NearbyStopsViewModel.class); + + } @Override @@ -277,7 +341,8 @@ 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 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 @@ /** * 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 @@ 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 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 --- a/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java +++ b/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java @@ -1,17 +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 com.google.android.material.floatingactionbutton.FloatingActionButton; import it.reyboz.bustorino.BuildConfig; +import java.util.Map; + import static android.content.Context.MODE_PRIVATE; public abstract class ScreenBaseFragment extends Fragment { @@ -62,4 +67,24 @@ 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 --- 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.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 @@ -31,6 +32,8 @@ /** * Singleton class used to access location. Possibly extended with other location sources. + * + * 2024: This is far too much. We need to simplify the whole mechanism (no more singleton) */ class AppLocationManager private constructor(context: Context) : LocationListener { private val appContext: Context @@ -259,9 +262,9 @@ 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/java/it/reyboz/bustorino/middleware/LocationUtils.java b/app/src/main/java/it/reyboz/bustorino/middleware/LocationUtils.java new file mode 100644 --- /dev/null +++ b/app/src/main/java/it/reyboz/bustorino/middleware/LocationUtils.java @@ -0,0 +1,28 @@ +package it.reyboz.bustorino.middleware; + +import android.content.Context; +import android.location.LocationManager; +import android.os.Build; +import android.provider.Settings; +import androidx.core.content.ContextCompat; + +public class LocationUtils { + + public static LocationManager getSystemLocationManager(Context context){ + return ContextCompat.getSystemService(context, LocationManager.class); + } + + //thanks to https://stackoverflow.com/questions/10311834/how-to-check-if-location-services-are-enabled + public static Boolean isLocationEnabled(Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + // This is a new method provided in API 28 + LocationManager lm = getSystemLocationManager(context); + return lm.isLocationEnabled(); + } else { + // This was deprecated in API 28 + int mode = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE, + Settings.Secure.LOCATION_MODE_OFF); + return (mode != Settings.Secure.LOCATION_MODE_OFF); + } + } +} diff --git a/app/src/main/java/it/reyboz/bustorino/util/Permissions.java b/app/src/main/java/it/reyboz/bustorino/util/Permissions.java --- a/app/src/main/java/it/reyboz/bustorino/util/Permissions.java +++ b/app/src/main/java/it/reyboz/bustorino/util/Permissions.java @@ -41,7 +41,7 @@ for (String s : providers) { Log.d(DEBUG_TAG, "Provider " + s); } - return providers.size() > 0; + return !providers.isEmpty(); } public static boolean isPermissionGranted(Context con,String permission){ return ContextCompat.checkSelfPermission(con, permission) == PackageManager.PERMISSION_GRANTED; diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -151,6 +151,7 @@ Attiva o disattiva posizione Posizione attivata Posizione disattivata + La posizione รจ disabilitata sul dispositivo