diff --git a/app/src/androidTest/java/fr/free/nrw/commons/UploadCancelledTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/UploadCancelledTest.kt index 4041b92e54..a6ba060e7f 100644 --- a/app/src/androidTest/java/fr/free/nrw/commons/UploadCancelledTest.kt +++ b/app/src/androidTest/java/fr/free/nrw/commons/UploadCancelledTest.kt @@ -10,6 +10,7 @@ import androidx.test.espresso.intent.Intents import androidx.test.espresso.intent.matcher.IntentMatchers import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import androidx.test.rule.ActivityTestRule @@ -37,7 +38,8 @@ class UploadCancelledTest { @JvmField var mGrantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant( - "android.permission.WRITE_EXTERNAL_STORAGE" + "android.permission.WRITE_EXTERNAL_STORAGE", + "android.permission.ACCESS_FINE_LOCATION" ) private val device: UiDevice = @@ -102,6 +104,8 @@ class UploadCancelledTest { ) actionMenuItemView.perform(click()) + UITestHelper.sleep(2000) + val recyclerView = onView( allOf( withId(R.id.rv_nearby_list), @@ -114,6 +118,8 @@ class UploadCancelledTest { ) ) + UITestHelper.sleep(2000) + val linearLayout3 = onView( allOf( withId(R.id.cameraButton), @@ -128,6 +134,11 @@ class UploadCancelledTest { ) linearLayout3.perform(click()) + // Tap "Allow" on the one-time "Record location for in-app shots" dialog + onView(withText(R.string.option_allow)).check { view, _ -> + view?.performClick() + } + val pasteSensitiveTextInputEditText = onView( allOf( withId(R.id.caption_item_edit_text), @@ -158,16 +169,11 @@ class UploadCancelledTest { ) pasteSensitiveTextInputEditText2.perform(replaceText("test"), closeSoftKeyboard()) + UITestHelper.sleep(2000) + val appCompatButton2 = onView( allOf( withId(R.id.btn_next), - childAtPosition( - childAtPosition( - withId(R.id.ll_container_media_detail), - 2 - ), - 1 - ), isDisplayed() ) ) @@ -182,6 +188,8 @@ class UploadCancelledTest { Intents.intended(IntentMatchers.hasComponent(LocationPickerActivity::class.java.name)) + UITestHelper.sleep(2000) + val floatingActionButton3 = onView( allOf( withId(R.id.location_chosen_button), @@ -190,5 +198,9 @@ class UploadCancelledTest { ) UITestHelper.sleep(2000) floatingActionButton3.perform(click()) + + // Cancel Upload + UITestHelper.sleep(2000) + onView(withText(R.string.cancel)).perform(click()) } } diff --git a/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPickerActivity.java b/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPickerActivity.java index ef4b31a36d..c05d4dece8 100644 --- a/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPickerActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPickerActivity.java @@ -4,12 +4,14 @@ import static fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.LAST_ZOOM; import static fr.free.nrw.commons.utils.MapUtils.ZOOM_LEVEL; +import android.Manifest.permission; import android.annotation.SuppressLint; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Color; import android.graphics.Paint; import android.graphics.drawable.Drawable; +import android.location.LocationManager; import android.os.Bundle; import android.preference.PreferenceManager; import android.text.Html; @@ -21,12 +23,14 @@ import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.AppCompatTextView; import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.mapbox.mapboxsdk.camera.CameraPosition; @@ -34,9 +38,9 @@ import fr.free.nrw.commons.R; import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.filepicker.Constants; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.location.LocationPermissionsHelper; -import fr.free.nrw.commons.location.LocationPermissionsHelper.Dialog; import fr.free.nrw.commons.location.LocationPermissionsHelper.LocationPermissionCallback; import fr.free.nrw.commons.location.LocationServiceManager; import fr.free.nrw.commons.theme.BaseActivity; @@ -114,6 +118,7 @@ public class LocationPickerActivity extends BaseActivity implements @Named("default_preferences") public JsonKvStore applicationKvStore; + BasicKvStore store; /** * isDarkTheme: for keeping a track of the device theme and modifying the map theme accordingly */ @@ -124,6 +129,7 @@ public class LocationPickerActivity extends BaseActivity implements @Inject LocationServiceManager locationManager; + LocationPermissionsHelper locationPermissionsHelper; @SuppressLint("ClickableViewAccessibility") @Override @@ -133,6 +139,7 @@ protected void onCreate(@Nullable final Bundle savedInstanceState) { isDarkTheme = systemThemeUtils.isDeviceInNightMode(); moveToCurrentLocation = false; + store = new BasicKvStore(this, "LocationPermissions"); getWindow().requestFeature(Window.FEATURE_ACTION_BAR); final ActionBar actionBar = getSupportActionBar(); @@ -366,19 +373,10 @@ private void removeSelectedLocationMarker() { * Center the map at user's current location */ private void requestLocationPermissions() { - LocationPermissionsHelper.Dialog locationAccessDialog = new Dialog( - R.string.location_permission_title, - R.string.upload_map_location_access - ); - - LocationPermissionsHelper.Dialog locationOffDialog = new Dialog( - R.string.ask_to_turn_location_on, - R.string.upload_map_location_access - ); - LocationPermissionsHelper locationPermissionsHelper = new LocationPermissionsHelper( + locationPermissionsHelper = new LocationPermissionsHelper( this, locationManager, this); - locationPermissionsHelper.handleLocationPermissions(locationAccessDialog, - locationOffDialog); + locationPermissionsHelper.requestForLocationAccess(R.string.location_permission_title, + R.string.upload_map_location_access); } @Override @@ -389,7 +387,7 @@ public void onRequestPermissionsResult(final int requestCode, && grantResults[0] == PackageManager.PERMISSION_GRANTED) { onLocationPermissionGranted(); } else { - onLocationPermissionDenied(""); + onLocationPermissionDenied(getString(R.string.upload_map_location_access)); } super.onRequestPermissionsResult(requestCode, permissions, grantResults); } @@ -408,21 +406,47 @@ protected void onPause() { @Override public void onLocationPermissionDenied(String toastMessage) { - //do nothing + if (!ActivityCompat.shouldShowRequestPermissionRationale(this, + permission.ACCESS_FINE_LOCATION)) { + if (!locationPermissionsHelper.checkLocationPermission(this)) { + if (store.getBoolean("isPermissionDenied", false)) { + // means user has denied location permission twice or checked the "Don't show again" + locationPermissionsHelper.showAppSettingsDialog(this, + R.string.upload_map_location_access); + } else { + Toast.makeText(getBaseContext(), toastMessage, Toast.LENGTH_LONG).show(); + } + store.putBoolean("isPermissionDenied", true); + } + } else { + Toast.makeText(getBaseContext(), toastMessage, Toast.LENGTH_LONG).show(); + } } @Override public void onLocationPermissionGranted() { + if (locationPermissionsHelper.isLocationAccessToAppsTurnedOn()) { + locationManager.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER); + locationManager.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER); + getLocation(); + } else { + getLocation(); + locationPermissionsHelper.showLocationOffDialog(this, + R.string.ask_to_turn_location_on_text); + } + } + + /** + * Gets new location if locations services are on, else gets last location + */ + private void getLocation() { fr.free.nrw.commons.location.LatLng currLocation = locationManager.getLastLocation(); if (currLocation != null) { GeoPoint currLocationGeopoint = new GeoPoint(currLocation.getLatitude(), currLocation.getLongitude()); addLocationMarker(currLocationGeopoint); - if (moveToCurrentLocation) { - mapView.getController().setCenter(currLocationGeopoint); - mapView.getController().animateTo(currLocationGeopoint); - moveToCurrentLocation = false; - } + mapView.getController().setCenter(currLocationGeopoint); + mapView.getController().animateTo(currLocationGeopoint); markerImage.setTranslationY(0); } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java index 66aff1325f..5a2edfc8fc 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java @@ -18,7 +18,6 @@ import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.location.LatLng; import fr.free.nrw.commons.location.LocationPermissionsHelper; -import fr.free.nrw.commons.location.LocationPermissionsHelper.Dialog; import fr.free.nrw.commons.location.LocationPermissionsHelper.LocationPermissionCallback; import fr.free.nrw.commons.location.LocationServiceManager; import fr.free.nrw.commons.nearby.Place; @@ -85,15 +84,6 @@ public void initiateCameraPick(Activity activity, */ private void createDialogsAndHandleLocationPermissions(Activity activity, ActivityResultLauncher inAppCameraLocationPermissionLauncher) { - LocationPermissionsHelper.Dialog locationAccessDialog = new Dialog( - R.string.location_permission_title, - R.string.in_app_camera_location_permission_rationale - ); - - LocationPermissionsHelper.Dialog locationOffDialog = new Dialog( - R.string.ask_to_turn_location_on, - R.string.in_app_camera_needs_location - ); locationPermissionCallback = new LocationPermissionCallback() { @Override public void onLocationPermissionDenied(String toastMessage) { @@ -117,8 +107,8 @@ public void onLocationPermissionGranted() { inAppCameraLocationPermissionLauncher.launch( new String[]{permission.ACCESS_FINE_LOCATION}); } else { - locationPermissionsHelper.handleLocationPermissions(locationAccessDialog, - locationOffDialog); + locationPermissionsHelper.requestForLocationAccess(R.string.location_permission_title, + R.string.in_app_camera_location_permission_rationale); } } @@ -130,7 +120,7 @@ public void handleShowRationaleFlowCameraLocation(Activity activity) { activity.getString(android.R.string.cancel), () -> { if (!locationPermissionsHelper.isLocationAccessToAppsTurnedOn()) { - locationPermissionsHelper.showLocationOffDialog(activity); + locationPermissionsHelper.showLocationOffDialog(activity, R.string.in_app_camera_needs_location); } }, () -> locationPermissionCallback.onLocationPermissionDenied( diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapContract.java b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapContract.java index 92ca2494d2..b3a9651407 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapContract.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapContract.java @@ -15,10 +15,8 @@ public class ExploreMapContract { interface View { boolean isNetworkConnectionEstablished(); void populatePlaces(LatLng curlatLng); - void checkPermissionsAndPerformAction(); + void askForLocationPermission(); void recenterMap(LatLng curLatLng); - void showLocationOffDialog(); - void openLocationSettings(); void hideBottomDetailsSheet(); void displayBottomSheetWithInfo(Marker marker); LatLng getMapCenter(); diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java index 613c6d5f50..0a355d92a6 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java @@ -31,8 +31,6 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; -import android.widget.Toast; -import androidx.activity.result.ActivityResultCallback; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; @@ -53,8 +51,11 @@ import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.explore.ExploreMapRootFragment; import fr.free.nrw.commons.explore.paging.LiveDataConverter; +import fr.free.nrw.commons.filepicker.Constants; import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.location.LatLng; +import fr.free.nrw.commons.location.LocationPermissionsHelper; +import fr.free.nrw.commons.location.LocationPermissionsHelper.LocationPermissionCallback; import fr.free.nrw.commons.location.LocationServiceManager; import fr.free.nrw.commons.location.LocationUpdateListener; import fr.free.nrw.commons.media.MediaClient; @@ -72,7 +73,6 @@ import io.reactivex.schedulers.Schedulers; import java.util.ArrayList; import java.util.List; -import java.util.Map; import javax.inject.Inject; import javax.inject.Named; import org.osmdroid.events.MapEventsReceiver; @@ -94,7 +94,7 @@ import timber.log.Timber; public class ExploreMapFragment extends CommonsDaggerSupportFragment - implements ExploreMapContract.View, LocationUpdateListener { + implements ExploreMapContract.View, LocationUpdateListener, LocationPermissionCallback { private BottomSheetBehavior bottomSheetDetailsBehavior; private BroadcastReceiver broadcastReceiver; @@ -127,6 +127,7 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment BookmarkLocationsDao bookmarkLocationDao; // May be needed in future if we want to integrate bookmarking explore places @Inject SystemThemeUtils systemThemeUtils; + LocationPermissionsHelper locationPermissionsHelper; private ExploreMapPresenter presenter; @@ -156,43 +157,30 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment @BindView(R.id.category) TextView distance; - private ActivityResultLauncher activityResultLauncher = registerForActivityResult( - new ActivityResultContracts.RequestMultiplePermissions(), - new ActivityResultCallback>() { - @Override - public void onActivityResult(Map result) { - boolean areAllGranted = true; - for (final boolean b : result.values()) { - areAllGranted = areAllGranted && b; - } - - if (areAllGranted) { - locationPermissionGranted(); + private ActivityResultLauncher activityResultLauncher = registerForActivityResult( + new ActivityResultContracts.RequestPermission(), isGranted -> { + if (isGranted) { + locationPermissionGranted(); + } else { + if (shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) { + DialogUtil.showAlertDialog(getActivity(), + getActivity().getString(R.string.location_permission_title), + getActivity().getString(R.string.location_permission_rationale_nearby), + getActivity().getString(android.R.string.ok), + getActivity().getString(android.R.string.cancel), + () -> { + askForLocationPermission(); + }, + null, + null, + false); } else { - if (shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) { - DialogUtil.showAlertDialog(getActivity(), - getActivity().getString(R.string.location_permission_title), - getActivity().getString(R.string.location_permission_rationale_nearby), - getActivity().getString(android.R.string.ok), - getActivity().getString(android.R.string.cancel), - () -> { - if (!(locationManager.isNetworkProviderEnabled() - || locationManager.isGPSProviderEnabled())) { - showLocationOffDialog(); - } - }, - () -> isPermissionDenied = true, - null, - false); - } else { - isPermissionDenied = true; - } - + Timber.d("The user checked 'Don't ask again' or denied the permission twice"); + isPermissionDenied = true; } } }); - @NonNull public static ExploreMapFragment newInstance() { ExploreMapFragment fragment = new ExploreMapFragment(); @@ -222,7 +210,7 @@ public void onViewCreated(@NonNull final View view, @Nullable final Bundle saved setSearchThisAreaButtonVisibility(false); tvAttribution.setText(Html.fromHtml(getString(R.string.map_attribution))); initNetworkBroadCastReceiver(); - + locationPermissionsHelper = new LocationPermissionsHelper(getActivity(),locationManager,this); if (presenter == null) { presenter = new ExploreMapPresenter(bookmarkLocationDao); } @@ -316,7 +304,9 @@ public boolean onZoom(ZoomEvent event) { } }); - + if (!locationPermissionsHelper.checkLocationPermission(getActivity())) { + askForLocationPermission(); + } } @Override @@ -326,8 +316,7 @@ public void onResume() { presenter.attachView(this); registerNetworkReceiver(); if (isResumed()) { - if (!isPermissionDenied && !applicationKvStore - .getBoolean("doNotAskForLocationPermission", false)) { + if (locationPermissionsHelper.checkLocationPermission(getActivity())) { performMapReadyActions(); } else { startMapWithoutPermission(); @@ -335,8 +324,14 @@ public void onResume() { } } + @Override + public void onPause() { + super.onPause(); + // unregistering the broadcastReceiver, as it was causing an exception and a potential crash + getActivity().unregisterReceiver(broadcastReceiver); + } + private void startMapWithoutPermission() { - applicationKvStore.putBoolean("doNotAskForLocationPermission", true); lastKnownLocation = MapUtils.defaultLatLng; moveCameraToPosition( new GeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude())); @@ -354,13 +349,14 @@ private void performMapReadyActions() { mapView.getOverlayManager().getTilesOverlay() .setColorFilter(TilesOverlay.INVERT_COLORS); } - if (!applicationKvStore.getBoolean("doNotAskForLocationPermission", false) || - PermissionUtils.hasPermission(getActivity(), - new String[]{Manifest.permission.ACCESS_FINE_LOCATION})) { - checkPermissionsAndPerformAction(); - } else { + if (applicationKvStore.getBoolean("doNotAskForLocationPermission", false) && + !locationPermissionsHelper.checkLocationPermission(getActivity())) { isPermissionDenied = true; } + lastKnownLocation = MapUtils.defaultLatLng; + moveCameraToPosition( + new GeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude())); + presenter.onMapReady(exploreMapController); } private void initViews() { @@ -432,7 +428,6 @@ public boolean isNetworkConnectionEstablished() { public void populatePlaces(LatLng curLatLng) { final Observable nearbyPlacesInfoObservable; if (curLatLng == null) { - checkPermissionsAndPerformAction(); return; } if (curLatLng.equals(getLastMapFocus())) { // Means we are checking around current location @@ -452,8 +447,8 @@ public void populatePlaces(LatLng curLatLng) { }, throwable -> { Timber.d(throwable); - showErrorMessage(getString(R.string.error_fetching_nearby_places) - + throwable.getLocalizedMessage()); + // Not showing the user, throwable localizedErrorMessage + showErrorMessage(getString(R.string.error_fetching_nearby_places)); setProgressBarVisibility(false); presenter.lockUnlockNearby(false); })); @@ -476,9 +471,9 @@ private void showErrorMessage(final String message) { } @Override - public void checkPermissionsAndPerformAction() { - Timber.d("Checking permission and perfoming action"); - activityResultLauncher.launch(new String[]{permission.ACCESS_FINE_LOCATION}); + public void askForLocationPermission() { + Timber.d("Asking for location permission"); + activityResultLauncher.launch(permission.ACCESS_FINE_LOCATION); } private void locationPermissionGranted() { @@ -497,9 +492,9 @@ private void locationPermissionGranted() { locationManager.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER); locationManager.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER); setProgressBarVisibility(true); - } else { - Toast.makeText(getContext(), getString(R.string.nearby_location_not_available), - Toast.LENGTH_LONG).show(); + } + else { + locationPermissionsHelper.showLocationOffDialog(getActivity(), R.string.ask_to_turn_location_on_text); } presenter.onMapReady(exploreMapController); registerUnregisterLocationListener(false); @@ -511,13 +506,25 @@ public void registerUnregisterLocationListener(final boolean removeLocationListe @Override public void recenterMap(LatLng curLatLng) { - if (isPermissionDenied || curLatLng == null) { - recenterToUserLocation = true; - checkPermissionsAndPerformAction(); - if (!isPermissionDenied && !(locationManager.isNetworkProviderEnabled() - || locationManager.isGPSProviderEnabled())) { - showLocationOffDialog(); + // if user has denied permission twice, then show dialog + if (isPermissionDenied) { + if (locationPermissionsHelper.checkLocationPermission(getActivity())) { + // this will run when user has given permission by opening app's settings + isPermissionDenied = false; + recenterMap(curLatLng); + } else { + locationPermissionsHelper.showAppSettingsDialog(getActivity(), + R.string.explore_map_needs_location); + } + } else { + if (!locationPermissionsHelper.checkLocationPermission(getActivity())) { + askForLocationPermission(); + } else { + locationPermissionGranted(); } + } + if (curLatLng == null) { + recenterToUserLocation = true; return; } recenterMarkerToPosition(new GeoPoint(curLatLng.getLatitude(), curLatLng.getLongitude())); @@ -545,31 +552,6 @@ public void recenterMap(LatLng curLatLng) { } } - @Override - public void showLocationOffDialog() { - // This creates a dialog box that prompts the user to enable location - DialogUtil - .showAlertDialog(getActivity(), getString(R.string.ask_to_turn_location_on), - getString(R.string.nearby_needs_location), - getString(R.string.yes), getString(R.string.no), this::openLocationSettings, null); - } - - @Override - public void openLocationSettings() { - // This method opens the location settings of the device along with a followup toast. - final Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); - final PackageManager packageManager = getActivity().getPackageManager(); - - if (intent.resolveActivity(packageManager) != null) { - startActivity(intent); - Toast.makeText(getContext(), R.string.recommend_high_accuracy_mode, Toast.LENGTH_LONG) - .show(); - } else { - Toast.makeText(getContext(), R.string.cannot_open_location_settings, Toast.LENGTH_LONG) - .show(); - } - } - @Override public void hideBottomDetailsSheet() { bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); @@ -895,7 +877,20 @@ public fr.free.nrw.commons.location.LatLng getMapCenter() { if (mapCenter != null) { latLnge = new fr.free.nrw.commons.location.LatLng( mapCenter.getLatitude(), mapCenter.getLongitude(), 100); + } else { + if (applicationKvStore.getString("LastLocation") != null) { + final String[] locationLatLng + = applicationKvStore.getString("LastLocation").split(","); + lastKnownLocation + = new fr.free.nrw.commons.location.LatLng(Double.parseDouble(locationLatLng[0]), + Double.parseDouble(locationLatLng[1]), 1f); + latLnge = lastKnownLocation; + } else { + latLnge = new fr.free.nrw.commons.location.LatLng(51.506255446947776, + -0.07483536015053005, 1f); + } } + moveCameraToPosition(new GeoPoint(latLnge.getLatitude(),latLnge.getLongitude())); return latLnge; } @@ -954,4 +949,10 @@ public void onReceive(final Context context, final Intent intent) { } }; } + + @Override + public void onLocationPermissionDenied(String toastMessage) {} + + @Override + public void onLocationPermissionGranted() {} } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapPresenter.java b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapPresenter.java index 03ce30c7e4..c9759c8cb6 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapPresenter.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapPresenter.java @@ -136,7 +136,6 @@ public boolean backButtonClicked() { public void onMapReady(ExploreMapController exploreMapController) { this.exploreMapController = exploreMapController; - exploreMapFragmentView.addSearchThisAreaButtonAction(); if (null != exploreMapFragmentView) { exploreMapFragmentView.addSearchThisAreaButtonAction(); initializeMapOperations(); @@ -146,7 +145,6 @@ public void onMapReady(ExploreMapController exploreMapController) { public void initializeMapOperations() { lockUnlockNearby(false); updateMap(LOCATION_SIGNIFICANTLY_CHANGED); - exploreMapFragmentView.addSearchThisAreaButtonAction(); } public Observable loadAttractionsFromLocation(LatLng curLatLng, diff --git a/app/src/main/java/fr/free/nrw/commons/location/LocationPermissionsHelper.java b/app/src/main/java/fr/free/nrw/commons/location/LocationPermissionsHelper.java index f2be79c675..35e5ae7f07 100644 --- a/app/src/main/java/fr/free/nrw/commons/location/LocationPermissionsHelper.java +++ b/app/src/main/java/fr/free/nrw/commons/location/LocationPermissionsHelper.java @@ -1,13 +1,15 @@ package fr.free.nrw.commons.location; +import android.Manifest; import android.Manifest.permission; import android.app.Activity; import android.content.Intent; import android.content.pm.PackageManager; +import android.net.Uri; import android.provider.Settings; +import android.widget.Toast; import androidx.core.app.ActivityCompat; import fr.free.nrw.commons.R; -import fr.free.nrw.commons.filepicker.Constants; import fr.free.nrw.commons.filepicker.Constants.RequestCodes; import fr.free.nrw.commons.utils.DialogUtil; import fr.free.nrw.commons.utils.PermissionUtils; @@ -16,117 +18,149 @@ * Helper class to handle location permissions */ public class LocationPermissionsHelper { + Activity activity; LocationServiceManager locationManager; LocationPermissionCallback callback; + public LocationPermissionsHelper(Activity activity, LocationServiceManager locationManager, LocationPermissionCallback callback) { this.activity = activity; this.locationManager = locationManager; this.callback = callback; } - public static class Dialog { - int dialogTitleResource; - int dialogTextResource; - - public Dialog(int dialogTitle, int dialogText) { - dialogTitleResource = dialogTitle; - dialogTextResource = dialogText; - } - } - - /** - * Handles the entire location permissions flow - * - * @param locationAccessDialog - * @param locationOffDialog - */ - public void handleLocationPermissions(Dialog locationAccessDialog, - Dialog locationOffDialog) { - requestForLocationAccess(locationAccessDialog, locationOffDialog); - } /** - * Ask for location permission if the user agrees on attaching location with pictures - * and the app does not have the access to location + * Ask for location permission if the user agrees on attaching location with pictures and the + * app does not have the access to location * - * @param locationAccessDialog - * @param locationOffDialog + * @param dialogTitleResource + * @param dialogTextResource */ - private void requestForLocationAccess( - Dialog locationAccessDialog, - Dialog locationOffDialog + public void requestForLocationAccess( + int dialogTitleResource, + int dialogTextResource ) { - if (PermissionUtils.hasPermission(activity, new String[]{permission.ACCESS_FINE_LOCATION})) { + if (checkLocationPermission(activity)) { callback.onLocationPermissionGranted(); } else { - if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission.ACCESS_FINE_LOCATION)) { - if (locationAccessDialog != null && locationOffDialog != null) { - DialogUtil.showAlertDialog(activity, activity.getString(locationAccessDialog.dialogTitleResource), - activity.getString(locationAccessDialog.dialogTextResource), - activity.getString(android.R.string.ok), - activity.getString(android.R.string.cancel), - () -> { - if (!isLocationAccessToAppsTurnedOn()) { - showLocationOffDialog(activity); - } else { - ActivityCompat.requestPermissions(activity, - new String[]{permission.ACCESS_FINE_LOCATION}, 1); - } - }, - () -> callback.onLocationPermissionDenied(activity.getString(R.string.in_app_camera_location_permission_denied)), - null, - false); - } + if (ActivityCompat.shouldShowRequestPermissionRationale(activity, + permission.ACCESS_FINE_LOCATION)) { + DialogUtil.showAlertDialog(activity, activity.getString(dialogTitleResource), + activity.getString(dialogTextResource), + activity.getString(android.R.string.ok), + activity.getString(android.R.string.cancel), + () -> { + ActivityCompat.requestPermissions(activity, + new String[]{permission.ACCESS_FINE_LOCATION}, 1); + }, + () -> callback.onLocationPermissionDenied( + activity.getString(R.string.in_app_camera_location_permission_denied)), + null, + false); } else { - ActivityCompat.requestPermissions(activity, new String[]{permission.ACCESS_FINE_LOCATION}, + ActivityCompat.requestPermissions(activity, + new String[]{permission.ACCESS_FINE_LOCATION}, RequestCodes.LOCATION); } } } - public void showLocationOffDialog(Activity activity) { + /** + * Shows a dialog for user to open the settings page and turn on location services + * + * @param activity Activity object + * @param dialogTextResource int id of the required string resource + */ + public void showLocationOffDialog(Activity activity, int dialogTextResource) { DialogUtil .showAlertDialog(activity, activity.getString(R.string.ask_to_turn_location_on), - activity.getString(R.string.in_app_camera_needs_location), + activity.getString(dialogTextResource), activity.getString(R.string.title_app_shortcut_setting), activity.getString(R.string.cancel), () -> openLocationSettings(activity), - () -> callback.onLocationPermissionDenied(activity.getString( - R.string.in_app_camera_location_unavailable))); + () -> Toast.makeText(activity, activity.getString(dialogTextResource), + Toast.LENGTH_LONG).show() + ); } /** - * Open location source settings so that apps with location access can access it + * Opens the location access page in settings, for user to turn on location services * - * TODO: modify it to fix https://github.com/commons-app/apps-android-commons/issues/5255 + * @param activity Activtiy object */ - public void openLocationSettings(Activity activity) { final Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); final PackageManager packageManager = activity.getPackageManager(); - if (intent.resolveActivity(packageManager)!= null) { + if (intent.resolveActivity(packageManager) != null) { activity.startActivity(intent); + } else { + Toast.makeText(activity, R.string.cannot_open_location_settings, Toast.LENGTH_LONG) + .show(); } } + /** + * Shows a dialog for user to open the app's settings page and give location permission + * + * @param activity Activity object + * @param dialogTextResource int id of the required string resource + */ + public void showAppSettingsDialog(Activity activity, int dialogTextResource) { + DialogUtil + .showAlertDialog(activity, activity.getString(R.string.location_permission_title), + activity.getString(dialogTextResource), + activity.getString(R.string.title_app_shortcut_setting), + activity.getString(R.string.cancel), + () -> openAppSettings(activity), + () -> Toast.makeText(activity, activity.getString(dialogTextResource), + Toast.LENGTH_LONG).show() + ); + } + + /** + * Opens detailed settings page of the app for the user to turn on location services + * + * @param activity Activity object + */ + public void openAppSettings(Activity activity) { + Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + Uri uri = Uri.fromParts("package", activity.getPackageName(), null); + intent.setData(uri); + activity.startActivity(intent); + } + /** * Check if apps have access to location even after having individual access * - * @return + * @return Returns true ir false depending on if location services are on or not */ public boolean isLocationAccessToAppsTurnedOn() { - return (locationManager.isNetworkProviderEnabled() || locationManager.isGPSProviderEnabled()); + return (locationManager.isNetworkProviderEnabled() + || locationManager.isGPSProviderEnabled()); + } + + /** + * Checks if location permission is already granted or not + * + * @param activity Activity object + * @return Returns true or false depending on whether location permission is granted or not + */ + public boolean checkLocationPermission(Activity activity) { + return PermissionUtils.hasPermission(activity, + new String[]{Manifest.permission.ACCESS_FINE_LOCATION}); } /** * Handle onPermissionDenied within individual classes based on the requirements */ public interface LocationPermissionCallback { + void onLocationPermissionDenied(String toastMessage); + void onLocationPermissionGranted(); } } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/contract/NearbyParentFragmentContract.java b/app/src/main/java/fr/free/nrw/commons/nearby/contract/NearbyParentFragmentContract.java index 3b7a3d09f4..e614cd6532 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/contract/NearbyParentFragmentContract.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/contract/NearbyParentFragmentContract.java @@ -25,7 +25,7 @@ interface View { boolean isListBottomSheetExpanded(); - void checkPermissionsAndPerformAction(); + void askForLocationPermission(); void displayLoginSkippedWarning(); diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java index a9a47baba9..1c97494297 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java @@ -6,7 +6,6 @@ import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.MAP_UPDATED; import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT; -import android.Manifest; import android.Manifest.permission; import android.annotation.SuppressLint; import android.content.BroadcastReceiver; @@ -82,6 +81,8 @@ import fr.free.nrw.commons.contributions.MainActivity.ActiveFragment; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.kvstore.JsonKvStore; +import fr.free.nrw.commons.location.LocationPermissionsHelper; +import fr.free.nrw.commons.location.LocationPermissionsHelper.LocationPermissionCallback; import fr.free.nrw.commons.location.LocationServiceManager; import fr.free.nrw.commons.location.LocationUpdateListener; import fr.free.nrw.commons.nearby.CheckBoxTriStates; @@ -101,9 +102,9 @@ import fr.free.nrw.commons.utils.ExecutorUtils; import fr.free.nrw.commons.utils.LayoutUtils; import fr.free.nrw.commons.utils.LocationUtils; +import fr.free.nrw.commons.utils.MapUtils; import fr.free.nrw.commons.utils.NearbyFABUtils; import fr.free.nrw.commons.utils.NetworkUtils; -import fr.free.nrw.commons.utils.PermissionUtils; import fr.free.nrw.commons.utils.SystemThemeUtils; import fr.free.nrw.commons.utils.ViewUtil; import fr.free.nrw.commons.wikidata.WikidataEditListener; @@ -142,7 +143,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment implements NearbyParentFragmentContract.View, - WikidataEditListener.WikidataP18EditListener, LocationUpdateListener { + WikidataEditListener.WikidataP18EditListener, LocationUpdateListener, + LocationPermissionCallback { @BindView(R.id.bottom_sheet) RelativeLayout rlBottomSheet; @@ -243,6 +245,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment SystemThemeUtils systemThemeUtils; @Inject CommonPlaceClickActions commonPlaceClickActions; + private LocationPermissionsHelper locationPermissionsHelper; private NearbyFilterSearchRecyclerViewAdapter nearbyFilterSearchRecyclerViewAdapter; private BottomSheetBehavior bottomSheetListBehavior; private BottomSheetBehavior bottomSheetDetailsBehavior; @@ -316,12 +319,9 @@ public void onActivityResult(Map result) { getActivity().getString(android.R.string.ok), getActivity().getString(android.R.string.cancel), () -> { - if (!(locationManager.isNetworkProviderEnabled() - || locationManager.isGPSProviderEnabled())) { - showLocationOffDialog(); - } + askForLocationPermission(); }, - () -> isPermissionDenied = true, + null, null, false); } else { @@ -382,7 +382,8 @@ public void onViewCreated(@NonNull final View view, @Nullable final Bundle saved } else { rlContainerWLMMonthMessage.setVisibility(View.GONE); } - + locationPermissionsHelper = new LocationPermissionsHelper(getActivity(), locationManager, + this); presenter.attachView(this); isPermissionDenied = false; recenterToUserLocation = false; @@ -526,6 +527,9 @@ public void apply(@NotNull final String query) { .replace(R.id.fl_container_nearby_children, fragment) .commit(); }); + if (!locationPermissionsHelper.checkLocationPermission(getActivity())) { + askForLocationPermission(); + } } /** @@ -581,14 +585,18 @@ private void addCheckBoxCallback() { private void performMapReadyActions() { if (((MainActivity) getActivity()).activeFragment == ActiveFragment.NEARBY) { - if (!applicationKvStore.getBoolean("doNotAskForLocationPermission", false) || - PermissionUtils.hasPermission(getActivity(), - new String[]{Manifest.permission.ACCESS_FINE_LOCATION})) { - checkPermissionsAndPerformAction(); - } else { + if (applicationKvStore.getBoolean("doNotAskForLocationPermission", false) && + !locationPermissionsHelper.checkLocationPermission(getActivity())) { isPermissionDenied = true; } } + presenter.onMapReady(); + } + + @Override + public void askForLocationPermission() { + Timber.d("Asking for location permission"); + locationPermissionLauncher.launch(permission.ACCESS_FINE_LOCATION); } private void locationPermissionGranted() { @@ -608,8 +616,7 @@ private void locationPermissionGranted() { locationManager.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER); setProgressBarVisibility(true); } else { - Toast.makeText(getContext(), getString(R.string.nearby_location_not_available), - Toast.LENGTH_LONG).show(); + locationPermissionsHelper.showLocationOffDialog(getActivity(), R.string.ask_to_turn_location_on_text); } presenter.onMapReady(); registerUnregisterLocationListener(false); @@ -622,15 +629,10 @@ public void onResume() { presenter.attachView(this); registerNetworkReceiver(); if (isResumed() && ((MainActivity) getActivity()).activeFragment == ActiveFragment.NEARBY) { - if (!isPermissionDenied && !applicationKvStore.getBoolean( - "doNotAskForLocationPermission", false)) { - if (!locationManager.isGPSProviderEnabled()) { - startMapWithCondition("Without GPS"); - } else { - startTheMap(); - } + if (locationPermissionsHelper.checkLocationPermission(getActivity())) { + performMapReadyActions(); } else { - startMapWithCondition("Without Permission"); + startMapWithoutPermission(); } } } @@ -640,12 +642,9 @@ public void onResume() { * coordinates, other than that it points to the last known location which can be get by the key * "LastLocation" from applicationKvStore * - * @param condition : for which condition the map should start */ - private void startMapWithCondition(final String condition) { - if (condition.equals("Without Permission")) { - applicationKvStore.putBoolean("doNotAskForLocationPermission", true); - } + private void startMapWithoutPermission() { + Timber.d("Inside startMapWithoutPerm"); if (applicationKvStore.getString("LastLocation") != null) { final String[] locationLatLng = applicationKvStore.getString("LastLocation").split(","); @@ -653,12 +652,13 @@ private void startMapWithCondition(final String condition) { = new fr.free.nrw.commons.location.LatLng(Double.parseDouble(locationLatLng[0]), Double.parseDouble(locationLatLng[1]), 1f); } else { - lastKnownLocation = new fr.free.nrw.commons.location.LatLng(51.50550, - -0.07520, 1f); + lastKnownLocation = MapUtils.defaultLatLng; } if (mapView != null) { - recenterMap(lastKnownLocation); + moveCameraToPosition( + new GeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude())); } + presenter.onMapReady(); } private void registerNetworkReceiver() { @@ -1326,12 +1326,6 @@ public void setTabItemContributions() { // TODO } - @Override - public void checkPermissionsAndPerformAction() { - Timber.d("Checking permission and perfoming action"); - locationPermissionLauncher.launch(permission.ACCESS_FINE_LOCATION); - } - /** * Starts animation of fab plus (turning on opening) and other FABs */ @@ -1451,6 +1445,14 @@ public boolean backButtonClicked() { return presenter.backButtonClicked(); } + @Override + public void onLocationPermissionDenied(String toastMessage) { + } + + @Override + public void onLocationPermissionGranted() { + } + /** * onLogoutComplete is called after shared preferences and data stored in local database are * cleared. @@ -1785,13 +1787,25 @@ private void removeMarker(Place place){ @Override public void recenterMap(fr.free.nrw.commons.location.LatLng curLatLng) { - if (isPermissionDenied || curLatLng == null) { - recenterToUserLocation = true; - checkPermissionsAndPerformAction(); - if (!isPermissionDenied && !(locationManager.isNetworkProviderEnabled() - || locationManager.isGPSProviderEnabled())) { - showLocationOffDialog(); + // if user has denied permission twice, then show dialog + if (isPermissionDenied) { + if (locationPermissionsHelper.checkLocationPermission(getActivity())) { + // this will run when user has given permission by opening app's settings + isPermissionDenied = false; + recenterMap(curLatLng); + } else { + locationPermissionsHelper.showAppSettingsDialog(getActivity(), + R.string.nearby_needs_location); } + } else { + if (!locationPermissionsHelper.checkLocationPermission(getActivity())) { + askForLocationPermission(); + } else { + locationPermissionGranted(); + } + } + if (curLatLng == null) { + recenterToUserLocation = true; return; } addCurrentLocationMarker(curLatLng); @@ -2034,7 +2048,7 @@ public void setUserVisibleHint(final boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); this.isVisibleToUser = isVisibleToUser; if (isResumed() && isVisibleToUser) { - startTheMap(); + performMapReadyActions(); } else { if (null != bottomSheetListBehavior) { bottomSheetListBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); @@ -2046,10 +2060,6 @@ public void setUserVisibleHint(final boolean isVisibleToUser) { } } - private void startTheMap() { - performMapReadyActions(); - } - /** * Clears all markers from the map and resets certain map overlays and gestures. After clearing * markers, it re-adds a scale bar overlay and rotation gesture overlay to the map. diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/presenter/NearbyParentFragmentPresenter.java b/app/src/main/java/fr/free/nrw/commons/nearby/presenter/NearbyParentFragmentPresenter.java index d7f2919a5b..c8b6dc5a2e 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/presenter/NearbyParentFragmentPresenter.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/presenter/NearbyParentFragmentPresenter.java @@ -94,7 +94,6 @@ public void removeNearbyPreferences(JsonKvStore applicationKvStore) { public void initializeMapOperations() { lockUnlockNearby(false); updateMapAndList(LOCATION_SIGNIFICANTLY_CHANGED); - this.nearbyParentFragmentView.addSearchThisAreaButtonAction(); nearbyParentFragmentView.setCheckBoxAction(); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6c6a2ab741..e438452374 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -354,7 +354,7 @@ Share App Rotate - Error fetching nearby places. + Could not load nearby places No nearby places around Error fetching nearby monuments. No recent searches @@ -584,7 +584,7 @@ Upload your first media by tapping on the add button. Could not add coordinates. Could not add descriptions. Could not add caption. - Unable to get coordinates. + Image\'s coordinates not updated Unable to get descriptions. Edit descriptions and captions @@ -621,8 +621,10 @@ Upload your first media by tapping on the add button. Failed to open location settings. Please turn on location manually For best results, choose the High Accuracy mode. Turn on location? + Kindly turn on location services for the app show your current location Nearby needs location enabled to work properly - You need to give access to your current location to set location automatically. + Explore map needs location permission to display nearby images + You need to give location permission to set location automatically. Did you shoot these two pictures at the same place? Do you want to use the latitude/longitude of the picture on the right? Load More No places found, try changing your search criteria.