diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/mvp/contract/NearbyParentFragmentContract.java b/app/src/main/java/fr/free/nrw/commons/nearby/mvp/contract/NearbyParentFragmentContract.java index 666cea321..1ae574ef7 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/mvp/contract/NearbyParentFragmentContract.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/mvp/contract/NearbyParentFragmentContract.java @@ -1,10 +1,18 @@ package fr.free.nrw.commons.nearby.mvp.contract; + +import fr.free.nrw.commons.location.LocationServiceManager; + public interface NearbyParentFragmentContract { interface View { void setListFragmentExpanded(); void refreshView(); + void registerLocationUpdates(LocationServiceManager locationServiceManager); + void requestLocationPermissions(LocationServiceManager locationServiceManager); + void showLocationPermissionDeniedErrorDialog(LocationServiceManager locationServiceManager); + void checkGps(LocationServiceManager locationServiceManager); + void checkLocationPermission(LocationServiceManager locationServiceManager); } interface UserActions { diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/mvp/fragments/NearbyParentFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/mvp/fragments/NearbyParentFragment.java index d697fb448..c23c36181 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/mvp/fragments/NearbyParentFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/mvp/fragments/NearbyParentFragment.java @@ -1,5 +1,7 @@ package fr.free.nrw.commons.nearby.mvp.fragments; +import android.content.Intent; +import android.os.Build; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -14,10 +16,12 @@ import javax.inject.Inject; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; import androidx.constraintlayout.widget.ConstraintLayout; import butterknife.BindView; import butterknife.ButterKnife; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.contributions.MainActivity; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.location.LatLng; import fr.free.nrw.commons.location.LocationServiceManager; @@ -27,6 +31,7 @@ import fr.free.nrw.commons.nearby.NearbyListFragment; import fr.free.nrw.commons.nearby.NearbyMapFragment; import fr.free.nrw.commons.nearby.mvp.contract.NearbyParentFragmentContract; import fr.free.nrw.commons.wikidata.WikidataEditListener; +import timber.log.Timber; /** * This fragment is under MainActivity at the came level with ContributionFragment and holds @@ -50,8 +55,6 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment @BindView(R.id.loading_nearby_list) ConstraintLayout loading_nearby_layout; - @Inject - LocationServiceManager locationManager; @Inject NearbyController nearbyController; @Inject @@ -138,4 +141,147 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment public void refreshView() { } + + /** + * This method first checks if the location permissions has been granted and then register the + * location manager for updates. + * @param locationServiceManager passed from presenter to check updates if location + * permissions are given + */ + @Override + public void registerLocationUpdates(LocationServiceManager locationServiceManager) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (locationServiceManager.isLocationPermissionGranted(requireContext())) { + locationServiceManager.registerLocationManager(); + } else { + // Should we show an explanation? + if (locationServiceManager.isPermissionExplanationRequired(getActivity())) { + new AlertDialog.Builder(getActivity()) + .setMessage(getString(R.string.location_permission_rationale_nearby)) + .setPositiveButton(android.R.string.ok, (dialog, which) -> { + requestLocationPermissions(locationServiceManager); + dialog.dismiss(); + }) + .setNegativeButton(android.R.string.cancel, (dialog, id) -> { + showLocationPermissionDeniedErrorDialog(locationServiceManager); + dialog.cancel(); + }) + .create() + .show(); + + } else { + // No explanation needed, we can request the permission. + requestLocationPermissions(locationServiceManager); + } + } + } else { + locationServiceManager.registerLocationManager(); + } + } + + /** + * Request location permission if activity is not null + * @param locationServiceManager passed from presenter, to listen/un-listen location changes + */ + @Override + public void requestLocationPermissions(LocationServiceManager locationServiceManager) { + if (!getActivity().isFinishing()) { + locationServiceManager.requestPermissions(getActivity()); + } + } + + /** + * Will warn user if location is denied + * @param locationServiceManager will be passed to checkGps if needs permission + */ + @Override + public void showLocationPermissionDeniedErrorDialog(LocationServiceManager locationServiceManager) { + new AlertDialog.Builder(getActivity()) + .setMessage(R.string.nearby_needs_permissions) + .setCancelable(false) + .setPositiveButton(R.string.give_permission, (dialog, which) -> { + //will ask for the location permission again + checkGps(locationServiceManager); + }) + .setNegativeButton(R.string.cancel, (dialog, which) -> { + //dismiss dialog and send user to contributions tab instead + dialog.cancel(); + ((MainActivity)getActivity()).viewPager.setCurrentItem(((MainActivity)getActivity()).CONTRIBUTIONS_TAB_POSITION); + }) + .create() + .show(); + + } + + /** + * Checks device GPS permission first for all API levels + * @param locationServiceManager will be used to check if provider is enable + */ + @Override + public void checkGps(LocationServiceManager locationServiceManager) { + Timber.d("checking GPS"); + if (!locationServiceManager.isProviderEnabled()) { + Timber.d("GPS is not enabled"); + new AlertDialog.Builder(getActivity()) + .setMessage(R.string.gps_disabled) + .setCancelable(false) + .setPositiveButton(R.string.enable_gps, + (dialog, id) -> { + Intent callGPSSettingIntent = new Intent( + android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS); + Timber.d("Loaded settings page"); + startActivityForResult(callGPSSettingIntent, 1); + }) + .setNegativeButton(R.string.menu_cancel_upload, (dialog, id) -> { + showLocationPermissionDeniedErrorDialog(); + dialog.cancel(); + }) + .create() + .show(); + } else { + Timber.d("GPS is enabled"); + checkLocationPermission(); + } + } + + /** + * This method ideally should be called from inside of CheckGPS method. If device GPS is enabled + * then we need to control app specific permissions for >=M devices. For other devices, enabled + * GPS is enough for nearby, so directly call refresh view. + * @param locationServiceManager will be used to detect if location permission is granted or not + */ + @Override + public void checkLocationPermission(LocationServiceManager locationServiceManager) { + Timber.d("Checking location permission"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (locationServiceManager.isLocationPermissionGranted(requireContext())) { + refreshView(LOCATION_SIGNIFICANTLY_CHANGED); + } else { + // Should we show an explanation? + if (locationServiceManager.isPermissionExplanationRequired(getActivity())) { + // Show an explanation to the user *asynchronously* -- don't block + // this thread waiting for the user's response! After the user + // sees the explanation, try again to request the permission. + new AlertDialog.Builder(getActivity()) + .setMessage(getString(R.string.location_permission_rationale_nearby)) + .setPositiveButton(android.R.string.ok, (dialog, which) -> { + requestLocationPermissions(); + dialog.dismiss(); + }) + .setNegativeButton(android.R.string.cancel, (dialog, id) -> { + showLocationPermissionDeniedErrorDialog(); + dialog.cancel(); + }) + .create() + .show(); + + } else { + // No explanation needed, we can request the permission. + requestLocationPermissions(locationServiceManager); + } + } + } else { + refreshView(LOCATION_SIGNIFICANTLY_CHANGED); + } + } } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/mvp/presenter/NearbyParentFragmentPresenter.java b/app/src/main/java/fr/free/nrw/commons/nearby/mvp/presenter/NearbyParentFragmentPresenter.java index 89616fd59..e551806c5 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/mvp/presenter/NearbyParentFragmentPresenter.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/mvp/presenter/NearbyParentFragmentPresenter.java @@ -1,6 +1,9 @@ package fr.free.nrw.commons.nearby.mvp.presenter; +import javax.inject.Inject; + import fr.free.nrw.commons.location.LatLng; +import fr.free.nrw.commons.location.LocationServiceManager; import fr.free.nrw.commons.location.LocationUpdateListener; import fr.free.nrw.commons.nearby.mvp.contract.NearbyParentFragmentContract; import fr.free.nrw.commons.wikidata.WikidataEditListener; @@ -9,6 +12,9 @@ public class NearbyParentFragmentPresenter implements NearbyParentFragmentContract.UserActions, WikidataEditListener.WikidataP18EditListener, LocationUpdateListener { + @Inject + LocationServiceManager locationManager; + private NearbyParentFragmentContract.View nearbyParentFragmentView; public NearbyParentFragmentPresenter(NearbyParentFragmentContract.View nearbyParentFragmentView) { @@ -25,8 +31,15 @@ public class NearbyParentFragmentPresenter initializeNearbyOperations(); } + /** + * Initializes nearby operations by following these steps: + * - Add this location listener to location manager + */ @Override public void initializeNearbyOperations() { + locationManager.addLocationListener(this); + nearbyParentFragmentView.registerLocationUpdates(locationManager); + }