fixes issues related to location permission denial even when allowed inside in-app camera flow (#5313)

* fixes issues related to location permission denial even when allowed inside in-app camera flow

* fixes related to in app camera location permissions
This commit is contained in:
Srishti Rohatgi 2023-09-28 19:37:28 +05:30 committed by GitHub
parent 8d604a25fa
commit 9525409b9b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 442 additions and 251 deletions

View file

@ -10,6 +10,7 @@ import static fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.
import static fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.LAST_ZOOM; import static fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.LAST_ZOOM;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.location.Location; import android.location.Location;
import android.os.Bundle; import android.os.Bundle;
@ -21,7 +22,6 @@ import android.view.animation.OvershootInterpolator;
import android.widget.Button; import android.widget.Button;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
@ -56,6 +56,7 @@ import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import fr.free.nrw.commons.MapStyle; import fr.free.nrw.commons.MapStyle;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.filepicker.Constants;
import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.location.LocationPermissionsHelper; import fr.free.nrw.commons.location.LocationPermissionsHelper;
import fr.free.nrw.commons.location.LocationPermissionsHelper.Dialog; import fr.free.nrw.commons.location.LocationPermissionsHelper.Dialog;
@ -72,7 +73,7 @@ import timber.log.Timber;
* Helps to pick location and return the result with an intent * Helps to pick location and return the result with an intent
*/ */
public class LocationPickerActivity extends BaseActivity implements OnMapReadyCallback, public class LocationPickerActivity extends BaseActivity implements OnMapReadyCallback,
OnCameraMoveStartedListener, OnCameraIdleListener, Observer<CameraPosition> { OnCameraMoveStartedListener, OnCameraIdleListener, Observer<CameraPosition>, LocationPermissionCallback {
/** /**
* DROPPED_MARKER_LAYER_ID : id for layer * DROPPED_MARKER_LAYER_ID : id for layer
@ -474,30 +475,21 @@ public class LocationPickerActivity extends BaseActivity implements OnMapReadyCa
R.string.upload_map_location_access R.string.upload_map_location_access
); );
LocationPermissionsHelper locationPermissionsHelper = new LocationPermissionsHelper( LocationPermissionsHelper locationPermissionsHelper = new LocationPermissionsHelper(
this, locationManager, new LocationPermissionCallback() { this, locationManager, this);
@Override
public void onLocationPermissionDenied(String toastMessage) {
// Do nothing
}
@Override
public void onLocationPermissionGranted() {
fr.free.nrw.commons.location.LatLng currLocation = locationManager.getLastLocation();
if (currLocation != null) {
final CameraPosition position;
position = new CameraPosition.Builder()
.target(new com.mapbox.mapboxsdk.geometry.LatLng(currLocation.getLatitude(),
currLocation.getLongitude(), 0)) // Sets the new camera position
.zoom(mapboxMap.getCameraPosition().zoom) // Same zoom level
.build();
mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(position), 1000);
}
}
});
locationPermissionsHelper.handleLocationPermissions(locationAccessDialog, locationOffDialog); locationPermissionsHelper.handleLocationPermissions(locationAccessDialog, locationOffDialog);
} }
@Override
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions,
@NonNull final int[] grantResults) {
if (requestCode == Constants.RequestCodes.LOCATION && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
onLocationPermissionGranted();
} else {
onLocationPermissionDenied("");
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override @Override
protected void onStart() { protected void onStart() {
super.onStart(); super.onStart();
@ -539,4 +531,24 @@ public class LocationPickerActivity extends BaseActivity implements OnMapReadyCa
super.onLowMemory(); super.onLowMemory();
mapView.onLowMemory(); mapView.onLowMemory();
} }
@Override
public void onLocationPermissionDenied(String toastMessage) {
//do nothing
}
@Override
public void onLocationPermissionGranted() {
fr.free.nrw.commons.location.LatLng currLocation = locationManager.getLastLocation();
if (currLocation != null) {
final CameraPosition position;
position = new CameraPosition.Builder()
.target(new com.mapbox.mapboxsdk.geometry.LatLng(currLocation.getLatitude(),
currLocation.getLongitude(), 0)) // Sets the new camera position
.zoom(mapboxMap.getCameraPosition().zoom) // Same zoom level
.build();
mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(position), 1000);
}
}
} }

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons.bookmarks.locations; package fr.free.nrw.commons.bookmarks.locations;
import android.Manifest.permission;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -8,6 +9,9 @@ import android.view.ViewGroup;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
@ -21,6 +25,7 @@ import fr.free.nrw.commons.nearby.Place;
import fr.free.nrw.commons.nearby.fragments.CommonPlaceClickActions; import fr.free.nrw.commons.nearby.fragments.CommonPlaceClickActions;
import fr.free.nrw.commons.nearby.fragments.PlaceAdapter; import fr.free.nrw.commons.nearby.fragments.PlaceAdapter;
import java.util.List; import java.util.List;
import java.util.Map;
import javax.inject.Inject; import javax.inject.Inject;
import kotlin.Unit; import kotlin.Unit;
@ -36,6 +41,25 @@ public class BookmarkLocationsFragment extends DaggerFragment {
@Inject BookmarkLocationsDao bookmarkLocationDao; @Inject BookmarkLocationsDao bookmarkLocationDao;
@Inject CommonPlaceClickActions commonPlaceClickActions; @Inject CommonPlaceClickActions commonPlaceClickActions;
private PlaceAdapter adapter; private PlaceAdapter adapter;
private ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() {
@Override
public void onActivityResult(Map<String, Boolean> result) {
boolean areAllGranted = true;
for(final boolean b : result.values()) {
areAllGranted = areAllGranted && b;
}
if (areAllGranted) {
contributionController.locationPermissionCallback.onLocationPermissionGranted();
} else {
if (shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) {
contributionController.handleShowRationaleFlowCameraLocation(getActivity());
} else {
contributionController.locationPermissionCallback.onLocationPermissionDenied(getActivity().getString(R.string.in_app_camera_location_permission_denied));
}
}
}
});
/** /**
* Create an instance of the fragment with the right bundle parameters * Create an instance of the fragment with the right bundle parameters
@ -67,7 +91,8 @@ public class BookmarkLocationsFragment extends DaggerFragment {
adapter.remove(place); adapter.remove(place);
return Unit.INSTANCE; return Unit.INSTANCE;
}, },
commonPlaceClickActions commonPlaceClickActions,
inAppCameraLocationPermissionLauncher
); );
recyclerView.setAdapter(adapter); recyclerView.setAdapter(adapter);
} }

View file

@ -3,10 +3,12 @@ package fr.free.nrw.commons.contributions;
import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT; import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT;
import android.Manifest; import android.Manifest;
import android.Manifest.permission;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.widget.Toast; import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.filepicker.DefaultCallback; import fr.free.nrw.commons.filepicker.DefaultCallback;
@ -37,9 +39,12 @@ public class ContributionController {
private final JsonKvStore defaultKvStore; private final JsonKvStore defaultKvStore;
private LatLng locationBeforeImageCapture; private LatLng locationBeforeImageCapture;
private boolean isInAppCameraUpload; private boolean isInAppCameraUpload;
public LocationPermissionCallback locationPermissionCallback;
private LocationPermissionsHelper locationPermissionsHelper;
@Inject @Inject
LocationServiceManager locationManager; LocationServiceManager locationManager;
@Inject @Inject
public ContributionController(@Named("default_preferences") JsonKvStore defaultKvStore) { public ContributionController(@Named("default_preferences") JsonKvStore defaultKvStore) {
this.defaultKvStore = defaultKvStore; this.defaultKvStore = defaultKvStore;
@ -48,7 +53,8 @@ public class ContributionController {
/** /**
* Check for permissions and initiate camera click * Check for permissions and initiate camera click
*/ */
public void initiateCameraPick(Activity activity) { public void initiateCameraPick(Activity activity,
ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher) {
boolean useExtStorage = defaultKvStore.getBoolean("useExternalStorage", true); boolean useExtStorage = defaultKvStore.getBoolean("useExternalStorage", true);
if (!useExtStorage) { if (!useExtStorage) {
initiateCameraUpload(activity); initiateCameraUpload(activity);
@ -56,19 +62,20 @@ public class ContributionController {
} }
PermissionUtils.checkPermissionsAndPerformAction(activity, PermissionUtils.checkPermissionsAndPerformAction(activity,
PermissionUtils.PERMISSIONS_STORAGE, () -> {
() -> { if (defaultKvStore.getBoolean("inAppCameraFirstRun")) {
if (defaultKvStore.getBoolean("inAppCameraFirstRun")) { defaultKvStore.putBoolean("inAppCameraFirstRun", false);
defaultKvStore.putBoolean("inAppCameraFirstRun", false); askUserToAllowLocationAccess(activity, inAppCameraLocationPermissionLauncher);
askUserToAllowLocationAccess(activity); } else if (defaultKvStore.getBoolean("inAppCameraLocationPref")) {
} else if(defaultKvStore.getBoolean("inAppCameraLocationPref")) { createDialogsAndHandleLocationPermissions(activity,
createDialogsAndHandleLocationPermissions(activity); inAppCameraLocationPermissionLauncher);
} else { } else {
initiateCameraUpload(activity); initiateCameraUpload(activity);
} }
}, },
R.string.storage_permission_title, R.string.storage_permission_title,
R.string.write_storage_permission_rationale); R.string.write_storage_permission_rationale,
PermissionUtils.PERMISSIONS_STORAGE);
} }
/** /**
@ -76,7 +83,8 @@ public class ContributionController {
* *
* @param activity * @param activity
*/ */
private void createDialogsAndHandleLocationPermissions(Activity activity) { private void createDialogsAndHandleLocationPermissions(Activity activity,
ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher) {
LocationPermissionsHelper.Dialog locationAccessDialog = new Dialog( LocationPermissionsHelper.Dialog locationAccessDialog = new Dialog(
R.string.location_permission_title, R.string.location_permission_title,
R.string.in_app_camera_location_permission_rationale R.string.in_app_camera_location_permission_rationale
@ -86,52 +94,73 @@ public class ContributionController {
R.string.ask_to_turn_location_on, R.string.ask_to_turn_location_on,
R.string.in_app_camera_needs_location R.string.in_app_camera_needs_location
); );
LocationPermissionsHelper locationPermissionsHelper = new LocationPermissionsHelper( locationPermissionCallback = new LocationPermissionCallback() {
activity, locationManager, @Override
new LocationPermissionCallback() { public void onLocationPermissionDenied(String toastMessage) {
@Override Toast.makeText(
public void onLocationPermissionDenied(String toastMessage) { activity,
Toast.makeText( toastMessage,
activity, Toast.LENGTH_LONG
toastMessage, ).show();
Toast.LENGTH_LONG initiateCameraUpload(activity);
).show();
initiateCameraUpload(activity);
}
@Override
public void onLocationPermissionGranted() {
initiateCameraUpload(activity);
}
} }
);
locationPermissionsHelper.handleLocationPermissions( @Override
locationAccessDialog, public void onLocationPermissionGranted() {
locationOffDialog initiateCameraUpload(activity);
); }
};
locationPermissionsHelper = new LocationPermissionsHelper(
activity, locationManager, locationPermissionCallback);
if (inAppCameraLocationPermissionLauncher != null) {
inAppCameraLocationPermissionLauncher.launch(
new String[]{permission.ACCESS_FINE_LOCATION});
} else {
locationPermissionsHelper.handleLocationPermissions(locationAccessDialog,
locationOffDialog);
}
}
public void handleShowRationaleFlowCameraLocation(Activity activity) {
DialogUtil.showAlertDialog(activity, activity.getString(R.string.location_permission_title),
activity.getString(R.string.in_app_camera_location_permission_rationale),
activity.getString(android.R.string.ok),
activity.getString(android.R.string.cancel),
() -> {
if (!locationPermissionsHelper.isLocationAccessToAppsTurnedOn()) {
locationPermissionsHelper.showLocationOffDialog(activity);
}
},
() -> locationPermissionCallback.onLocationPermissionDenied(
activity.getString(R.string.in_app_camera_location_permission_denied)),
null,
false);
} }
/** /**
* Suggest user to attach location information with pictures. * Suggest user to attach location information with pictures. If the user selects "Yes", then:
* If the user selects "Yes", then: * <p>
* * Location is taken from the EXIF if the default camera application does not redact location
* Location is taken from the EXIF if the default camera application * tags.
* does not redact location tags. * <p>
* * Otherwise, if the EXIF metadata does not have location information, then location captured by
* Otherwise, if the EXIF metadata does not have location information, * the app is used
* then location captured by the app is used
* *
* @param activity * @param activity
*/ */
private void askUserToAllowLocationAccess(Activity activity) { private void askUserToAllowLocationAccess(Activity activity,
ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher) {
DialogUtil.showAlertDialog(activity, DialogUtil.showAlertDialog(activity,
activity.getString(R.string.in_app_camera_location_permission_title), activity.getString(R.string.in_app_camera_location_permission_title),
activity.getString(R.string.in_app_camera_location_access_explanation), activity.getString(R.string.in_app_camera_location_access_explanation),
activity.getString(R.string.option_allow), activity.getString(R.string.option_allow),
activity.getString(R.string.option_dismiss), activity.getString(R.string.option_dismiss),
()-> { () -> {
defaultKvStore.putBoolean("inAppCameraLocationPref", true); defaultKvStore.putBoolean("inAppCameraLocationPref", true);
createDialogsAndHandleLocationPermissions(activity); createDialogsAndHandleLocationPermissions(activity,
inAppCameraLocationPermissionLauncher);
}, },
() -> { () -> {
defaultKvStore.putBoolean("inAppCameraLocationPref", false); defaultKvStore.putBoolean("inAppCameraLocationPref", false);
@ -141,15 +170,6 @@ public class ContributionController {
true); true);
} }
/**
* Check if apps have access to location even after having individual access
*
* @return
*/
private boolean isLocationAccessToAppsTurnedOn() {
return (locationManager.isNetworkProviderEnabled() || locationManager.isGPSProviderEnabled());
}
/** /**
* Initiate gallery picker * Initiate gallery picker
*/ */
@ -161,22 +181,24 @@ public class ContributionController {
* Initiate gallery picker with permission * Initiate gallery picker with permission
*/ */
public void initiateCustomGalleryPickWithPermission(final Activity activity) { public void initiateCustomGalleryPickWithPermission(final Activity activity) {
setPickerConfiguration(activity,true); setPickerConfiguration(activity, true);
PermissionUtils.checkPermissionsAndPerformAction(activity, PermissionUtils.checkPermissionsAndPerformAction(activity,
PermissionUtils.PERMISSIONS_STORAGE,
() -> FilePicker.openCustomSelector(activity, 0), () -> FilePicker.openCustomSelector(activity, 0),
R.string.storage_permission_title, R.string.storage_permission_title,
R.string.write_storage_permission_rationale); R.string.write_storage_permission_rationale,
PermissionUtils.PERMISSIONS_STORAGE);
} }
/** /**
* Open chooser for gallery uploads * Open chooser for gallery uploads
*/ */
private void initiateGalleryUpload(final Activity activity, final boolean allowMultipleUploads) { private void initiateGalleryUpload(final Activity activity,
final boolean allowMultipleUploads) {
setPickerConfiguration(activity, allowMultipleUploads); setPickerConfiguration(activity, allowMultipleUploads);
boolean openDocumentIntentPreferred = defaultKvStore.getBoolean("openDocumentPhotoPickerPref", true); boolean openDocumentIntentPreferred = defaultKvStore.getBoolean(
"openDocumentPhotoPickerPref", true);
FilePicker.openGallery(activity, 0, openDocumentIntentPreferred); FilePicker.openGallery(activity, 0, openDocumentIntentPreferred);
} }
@ -184,11 +206,11 @@ public class ContributionController {
* Sets configuration for file picker * Sets configuration for file picker
*/ */
private void setPickerConfiguration(Activity activity, private void setPickerConfiguration(Activity activity,
boolean allowMultipleUploads) { boolean allowMultipleUploads) {
boolean copyToExternalStorage = defaultKvStore.getBoolean("useExternalStorage", true); boolean copyToExternalStorage = defaultKvStore.getBoolean("useExternalStorage", true);
FilePicker.configuration(activity) FilePicker.configuration(activity)
.setCopyTakenPhotosToPublicGalleryAppFolder(copyToExternalStorage) .setCopyTakenPhotosToPublicGalleryAppFolder(copyToExternalStorage)
.setAllowMultiplePickInGallery(allowMultipleUploads); .setAllowMultiplePickInGallery(allowMultipleUploads);
} }
/** /**
@ -206,36 +228,39 @@ public class ContributionController {
/** /**
* Attaches callback for file picker. * Attaches callback for file picker.
*/ */
public void handleActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { public void handleActivityResult(Activity activity, int requestCode, int resultCode,
FilePicker.handleActivityResult(requestCode, resultCode, data, activity, new DefaultCallback() { Intent data) {
FilePicker.handleActivityResult(requestCode, resultCode, data, activity,
new DefaultCallback() {
@Override @Override
public void onCanceled(final ImageSource source, final int type) { public void onCanceled(final ImageSource source, final int type) {
super.onCanceled(source, type); super.onCanceled(source, type);
defaultKvStore.remove(PLACE_OBJECT); defaultKvStore.remove(PLACE_OBJECT);
} }
@Override @Override
public void onImagePickerError(Exception e, FilePicker.ImageSource source, int type) { public void onImagePickerError(Exception e, FilePicker.ImageSource source,
ViewUtil.showShortToast(activity, R.string.error_occurred_in_picking_images); int type) {
} ViewUtil.showShortToast(activity, R.string.error_occurred_in_picking_images);
}
@Override @Override
public void onImagesPicked(@NonNull List<UploadableFile> imagesFiles, FilePicker.ImageSource source, int type) { public void onImagesPicked(@NonNull List<UploadableFile> imagesFiles,
Intent intent = handleImagesPicked(activity, imagesFiles); FilePicker.ImageSource source, int type) {
activity.startActivity(intent); Intent intent = handleImagesPicked(activity, imagesFiles);
} activity.startActivity(intent);
}); }
});
} }
public List<UploadableFile> handleExternalImagesPicked(Activity activity, public List<UploadableFile> handleExternalImagesPicked(Activity activity,
Intent data) { Intent data) {
return FilePicker.handleExternalImagesPicked(data, activity); return FilePicker.handleExternalImagesPicked(data, activity);
} }
/** /**
* Returns intent to be passed to upload activity * Returns intent to be passed to upload activity Attaches place object for nearby uploads and
* Attaches place object for nearby uploads and
* location before image capture if in-app camera is used * location before image capture if in-app camera is used
*/ */
private Intent handleImagesPicked(Context context, private Intent handleImagesPicked(Context context,
@ -263,5 +288,4 @@ public class ContributionController {
isInAppCameraUpload = false; // reset the flag for next use isInAppCameraUpload = false; // reset the flag for next use
return shareIntent; return shareIntent;
} }
} }

View file

@ -7,6 +7,7 @@ import static fr.free.nrw.commons.profile.ProfileActivity.KEY_USERNAME;
import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween; import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
import android.Manifest; import android.Manifest;
import android.Manifest.permission;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
@ -21,6 +22,9 @@ import android.widget.CheckBox;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
@ -35,6 +39,7 @@ import fr.free.nrw.commons.profile.ProfileActivity;
import fr.free.nrw.commons.theme.BaseActivity; import fr.free.nrw.commons.theme.BaseActivity;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import androidx.work.WorkManager; import androidx.work.WorkManager;
@ -117,6 +122,29 @@ public class ContributionsFragment
String userName; String userName;
private boolean isUserProfile; private boolean isUserProfile;
private ActivityResultLauncher<String[]> nearbyLocationPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() {
@Override
public void onActivityResult(Map<String, Boolean> result) {
boolean areAllGranted = true;
for (final boolean b : result.values()) {
areAllGranted = areAllGranted && b;
}
if (areAllGranted) {
onLocationPermissionGranted();
} else {
if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)
&& store.getBoolean("displayLocationPermissionForCardView", true)
&& !store.getBoolean("doNotAskForLocationPermission", false)
&& (((MainActivity) getActivity()).activeFragment == ActiveFragment.CONTRIBUTIONS)) {
nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.ENABLE_LOCATION_PERMISSION;
showNearbyCardPermissionRationale();
} else {
displayYouWontSeeNearbyMessage();
}
}
}
});
@NonNull @NonNull
public static ContributionsFragment newInstance() { public static ContributionsFragment newInstance() {
@ -451,12 +479,7 @@ public class ContributionsFragment
} }
private void requestLocationPermission() { private void requestLocationPermission() {
PermissionUtils.checkPermissionsAndPerformAction(getActivity(), nearbyLocationPermissionLauncher.launch(new String[]{permission.ACCESS_FINE_LOCATION});
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
this::onLocationPermissionGranted,
this::displayYouWontSeeNearbyMessage,
-1,
-1);
} }
private void onLocationPermissionGranted() { private void onLocationPermissionGranted() {

View file

@ -4,6 +4,7 @@ import static android.view.View.GONE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static fr.free.nrw.commons.di.NetworkingModule.NAMED_LANGUAGE_WIKI_PEDIA_WIKI_SITE; import static fr.free.nrw.commons.di.NetworkingModule.NAMED_LANGUAGE_WIKI_PEDIA_WIKI_SITE;
import android.Manifest.permission;
import android.content.Context; import android.content.Context;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.net.Uri; import android.net.Uri;
@ -18,6 +19,9 @@ import android.view.animation.AnimationUtils;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatTextView; import androidx.appcompat.widget.AppCompatTextView;
@ -42,6 +46,7 @@ import fr.free.nrw.commons.media.MediaClient;
import fr.free.nrw.commons.utils.SystemThemeUtils; import fr.free.nrw.commons.utils.SystemThemeUtils;
import fr.free.nrw.commons.utils.ViewUtil; import fr.free.nrw.commons.utils.ViewUtil;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@ -114,6 +119,27 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
private int contributionsSize; private int contributionsSize;
String userName; String userName;
private ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() {
@Override
public void onActivityResult(Map<String, Boolean> result) {
boolean areAllGranted = true;
for (final boolean b : result.values()) {
areAllGranted = areAllGranted && b;
}
if (areAllGranted) {
controller.locationPermissionCallback.onLocationPermissionGranted();
} else {
if (shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) {
controller.handleShowRationaleFlowCameraLocation(getActivity());
} else {
controller.locationPermissionCallback.onLocationPermissionDenied(
getActivity().getString(R.string.in_app_camera_location_permission_denied));
}
}
}
});
@Override @Override
public void onCreate(@Nullable @org.jetbrains.annotations.Nullable final Bundle savedInstanceState) { public void onCreate(@Nullable @org.jetbrains.annotations.Nullable final Bundle savedInstanceState) {
@ -297,7 +323,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
private void setListeners() { private void setListeners() {
fabPlus.setOnClickListener(view -> animateFAB(isFabOpen)); fabPlus.setOnClickListener(view -> animateFAB(isFabOpen));
fabCamera.setOnClickListener(view -> { fabCamera.setOnClickListener(view -> {
controller.initiateCameraPick(getActivity()); controller.initiateCameraPick(getActivity(), inAppCameraLocationPermissionLauncher);
animateFAB(isFabOpen); animateFAB(isFabOpen);
}); });
fabGallery.setOnClickListener(view -> { fabGallery.setOnClickListener(view -> {
@ -393,10 +419,10 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
@Override @Override
public void deleteUpload(final Contribution contribution) { public void deleteUpload(final Contribution contribution) {
DialogUtil.showAlertDialog(getActivity(), DialogUtil.showAlertDialog(getActivity(),
String.format(getString(R.string.cancelling_upload), String.format(Locale.getDefault().getDisplayLanguage(),
Locale.getDefault().getDisplayLanguage()), getString(R.string.cancelling_upload)),
String.format(getString(R.string.cancel_upload_dialog), String.format(Locale.getDefault().getDisplayLanguage(),
Locale.getDefault().getDisplayLanguage()), getString(R.string.cancel_upload_dialog)),
"YES", "NO", "YES", "NO",
() -> { () -> {
ViewUtil.showShortToast(getContext(), R.string.cancelling_upload); ViewUtil.showShortToast(getContext(), R.string.cancelling_upload);
@ -422,8 +448,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
public void addImageToWikipedia(Contribution contribution) { public void addImageToWikipedia(Contribution contribution) {
DialogUtil.showAlertDialog(getActivity(), DialogUtil.showAlertDialog(getActivity(),
getString(R.string.add_picture_to_wikipedia_article_title), getString(R.string.add_picture_to_wikipedia_article_title),
String.format(getString(R.string.add_picture_to_wikipedia_article_desc), getString(R.string.add_picture_to_wikipedia_article_desc),
Locale.getDefault().getDisplayLanguage()),
() -> { () -> {
showAddImageToWikipediaInstructions(contribution); showAddImageToWikipediaInstructions(contribution);
}, () -> { }, () -> {

View file

@ -165,11 +165,10 @@ public class MainActivity extends BaseActivity
if (VERSION.SDK_INT >= VERSION_CODES.Q) { if (VERSION.SDK_INT >= VERSION_CODES.Q) {
PermissionUtils.checkPermissionsAndPerformAction( PermissionUtils.checkPermissionsAndPerformAction(
this, this,
new String[]{permission.ACCESS_MEDIA_LOCATION},
() -> {}, () -> {},
R.string.media_location_permission_denied, R.string.media_location_permission_denied,
R.string.add_location_manually R.string.add_location_manually,
); permission.ACCESS_MEDIA_LOCATION);
} }
} }
} }

View file

@ -7,6 +7,7 @@ import static fr.free.nrw.commons.utils.MapUtils.CAMERA_TARGET_SHIFT_FACTOR_PORT
import static fr.free.nrw.commons.utils.MapUtils.ZOOM_LEVEL; import static fr.free.nrw.commons.utils.MapUtils.ZOOM_LEVEL;
import android.Manifest; import android.Manifest;
import android.Manifest.permission;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
@ -29,6 +30,9 @@ import android.widget.LinearLayout;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatTextView; import androidx.appcompat.widget.AppCompatTextView;
@ -84,6 +88,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import timber.log.Timber; import timber.log.Timber;
@ -144,6 +149,38 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
@BindView(R.id.title) TextView title; @BindView(R.id.title) TextView title;
@BindView(R.id.category) TextView distance; @BindView(R.id.category) TextView distance;
private ActivityResultLauncher<String[]> activityResultLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() {
@Override
public void onActivityResult(Map<String, Boolean> result) {
boolean areAllGranted = true;
for(final boolean b : result.values()) {
areAllGranted = areAllGranted && b;
}
if (areAllGranted) {
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),
() -> {
if (!(locationManager.isNetworkProviderEnabled() || locationManager.isGPSProviderEnabled())) {
showLocationOffDialog();
}
},
() -> isPermissionDenied = true,
null,
false);
} else {
isPermissionDenied = true;
}
}
}
});
@NonNull @NonNull
public static ExploreMapFragment newInstance() { public static ExploreMapFragment newInstance() {
@ -403,12 +440,7 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
@Override @Override
public void checkPermissionsAndPerformAction() { public void checkPermissionsAndPerformAction() {
Timber.d("Checking permission and perfoming action"); Timber.d("Checking permission and perfoming action");
PermissionUtils.checkPermissionsAndPerformAction(getActivity(), activityResultLauncher.launch(new String[]{permission.ACCESS_FINE_LOCATION});
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
this::locationPermissionGranted,
() -> isPermissionDenied = true,
R.string.location_permission_title,
R.string.location_permission_rationale_nearby);
} }
private void locationPermissionGranted() { private void locationPermissionGranted() {

View file

@ -7,6 +7,7 @@ public interface Constants {
* Provides the request codes utilised by the FilePicker * Provides the request codes utilised by the FilePicker
*/ */
interface RequestCodes { interface RequestCodes {
int LOCATION = 1;
int FILE_PICKER_IMAGE_IDENTIFICATOR = 0b1101101100; //876 int FILE_PICKER_IMAGE_IDENTIFICATOR = 0b1101101100; //876
int SOURCE_CHOOSER = 1 << 15; int SOURCE_CHOOSER = 1 << 15;

View file

@ -5,8 +5,10 @@ import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.provider.Settings; import android.provider.Settings;
import android.widget.Toast; import androidx.core.app.ActivityCompat;
import fr.free.nrw.commons.R; 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.DialogUtil;
import fr.free.nrw.commons.utils.PermissionUtils; import fr.free.nrw.commons.utils.PermissionUtils;
@ -55,27 +57,62 @@ public class LocationPermissionsHelper {
Dialog locationAccessDialog, Dialog locationAccessDialog,
Dialog locationOffDialog Dialog locationOffDialog
) { ) {
PermissionUtils.checkPermissionsAndPerformAction(activity, if (PermissionUtils.hasPermission(activity, new String[]{permission.ACCESS_FINE_LOCATION})) {
new String[]{permission.ACCESS_FINE_LOCATION}, callback.onLocationPermissionGranted();
() -> { } else {
if(!isLocationAccessToAppsTurnedOn()) { if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission.ACCESS_FINE_LOCATION)) {
showLocationOffDialog(locationOffDialog); if (locationAccessDialog != null && locationOffDialog != null) {
} else { DialogUtil.showAlertDialog(activity, activity.getString(locationAccessDialog.dialogTitleResource),
if (callback != null) { activity.getString(locationAccessDialog.dialogTextResource),
callback.onLocationPermissionGranted(); 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);
} }
}, } else {
() -> { ActivityCompat.requestPermissions(activity, new String[]{permission.ACCESS_FINE_LOCATION},
if (callback != null) { RequestCodes.LOCATION);
callback.onLocationPermissionDenied(activity.getString( }
R.string.in_app_camera_location_permission_denied)); }
}
},
locationAccessDialog.dialogTitleResource,
locationAccessDialog.dialogTextResource);
} }
public void showLocationOffDialog(Activity activity) {
DialogUtil
.showAlertDialog(activity,
activity.getString(R.string.ask_to_turn_location_on),
activity.getString(R.string.in_app_camera_needs_location),
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)));
}
/**
* Open location source settings so that apps with location access can access it
*
* TODO: modify it to fix https://github.com/commons-app/apps-android-commons/issues/5255
*/
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) {
activity.startActivity(intent);
}
}
/** /**
* Check if apps have access to location even after having individual access * Check if apps have access to location even after having individual access
* *
@ -85,38 +122,6 @@ public class LocationPermissionsHelper {
return (locationManager.isNetworkProviderEnabled() || locationManager.isGPSProviderEnabled()); return (locationManager.isNetworkProviderEnabled() || locationManager.isGPSProviderEnabled());
} }
/**
* Ask user to grant location access to apps
*
*/
private void showLocationOffDialog(Dialog locationOffDialog) {
DialogUtil
.showAlertDialog(activity,
activity.getString(locationOffDialog.dialogTitleResource),
activity.getString(locationOffDialog.dialogTextResource),
activity.getString(R.string.title_app_shortcut_setting),
activity.getString(R.string.cancel),
() -> openLocationSettings(),
() -> callback.onLocationPermissionDenied(activity.getString(
R.string.in_app_camera_location_unavailable)));
}
/**
* Open location source settings so that apps with location access can access it
*
* TODO: modify it to fix https://github.com/commons-app/apps-android-commons/issues/5255
*/
private void openLocationSettings() {
final Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
final PackageManager packageManager = activity.getPackageManager();
if (intent.resolveActivity(packageManager)!= null) {
activity.startActivity(intent);
}
}
/** /**
* Handle onPermissionDenied within individual classes based on the requirements * Handle onPermissionDenied within individual classes based on the requirements
*/ */

View file

@ -367,13 +367,13 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
launchZoomActivityAfterPermissionCheck(view); launchZoomActivityAfterPermissionCheck(view);
} else { } else {
PermissionUtils.checkPermissionsAndPerformAction(getActivity(), PermissionUtils.checkPermissionsAndPerformAction(getActivity(),
PermissionUtils.PERMISSIONS_STORAGE,
() -> { () -> {
launchZoomActivityAfterPermissionCheck(view); launchZoomActivityAfterPermissionCheck(view);
}, },
R.string.storage_permission_title, R.string.storage_permission_title,
R.string.read_storage_permission_rationale R.string.read_storage_permission_rationale,
); PermissionUtils.PERMISSIONS_STORAGE
);
} }
} }

View file

@ -2,6 +2,7 @@ package fr.free.nrw.commons.nearby
import android.view.View import android.view.View
import android.view.View.* import android.view.View.*
import androidx.activity.result.ActivityResultLauncher
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.transition.TransitionManager import androidx.transition.TransitionManager
@ -15,11 +16,12 @@ import fr.free.nrw.commons.databinding.ItemPlaceBinding
fun placeAdapterDelegate( fun placeAdapterDelegate(
bookmarkLocationDao: BookmarkLocationsDao, bookmarkLocationDao: BookmarkLocationsDao,
onItemClick: ((Place) -> Unit)? = null, onItemClick: ((Place) -> Unit)? = null,
onCameraClicked: (Place) -> Unit, onCameraClicked: (Place, ActivityResultLauncher<Array<String>>) -> Unit,
onGalleryClicked: (Place) -> Unit, onGalleryClicked: (Place) -> Unit,
onBookmarkClicked: (Place, Boolean) -> Unit, onBookmarkClicked: (Place, Boolean) -> Unit,
onOverflowIconClicked: (Place, View) -> Unit, onOverflowIconClicked: (Place, View) -> Unit,
onDirectionsClicked: (Place) -> Unit onDirectionsClicked: (Place) -> Unit,
inAppCameraLocationPermissionLauncher: ActivityResultLauncher<Array<String>>
) = adapterDelegateViewBinding<Place, Place, ItemPlaceBinding>({ layoutInflater, parent -> ) = adapterDelegateViewBinding<Place, Place, ItemPlaceBinding>({ layoutInflater, parent ->
ItemPlaceBinding.inflate(layoutInflater, parent, false) ItemPlaceBinding.inflate(layoutInflater, parent, false)
}) { }) {
@ -36,7 +38,7 @@ fun placeAdapterDelegate(
onItemClick?.invoke(item) onItemClick?.invoke(item)
} }
} }
nearbyButtonLayout.cameraButton.setOnClickListener { onCameraClicked(item) } nearbyButtonLayout.cameraButton.setOnClickListener { onCameraClicked(item, inAppCameraLocationPermissionLauncher) }
nearbyButtonLayout.galleryButton.setOnClickListener { onGalleryClicked(item) } nearbyButtonLayout.galleryButton.setOnClickListener { onGalleryClicked(item) }
bookmarkButtonImage.setOnClickListener { bookmarkButtonImage.setOnClickListener {
val isBookmarked = bookmarkLocationDao.updateBookmarkLocation(item) val isBookmarked = bookmarkLocationDao.updateBookmarkLocation(item)

View file

@ -5,6 +5,7 @@ import android.content.Intent
import android.net.Uri import android.net.Uri
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.activity.result.ActivityResultLauncher
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import fr.free.nrw.commons.R import fr.free.nrw.commons.R
@ -26,13 +27,13 @@ class CommonPlaceClickActions @Inject constructor(
private val contributionController: ContributionController private val contributionController: ContributionController
) { ) {
fun onCameraClicked(): (Place) -> Unit = { fun onCameraClicked(): (Place, ActivityResultLauncher<Array<String>>) -> Unit = { place, launcher ->
if (applicationKvStore.getBoolean("login_skipped", false)) { if (applicationKvStore.getBoolean("login_skipped", false)) {
showLoginDialog() showLoginDialog()
} else { } else {
Timber.d("Camera button tapped. Image title: ${it.getName()}Image desc: ${it.longDescription}") Timber.d("Camera button tapped. Image title: ${place.getName()}Image desc: ${place.longDescription}")
storeSharedPrefs(it) storeSharedPrefs(place)
contributionController.initiateCameraPick(activity) contributionController.initiateCameraPick(activity, launcher)
} }
} }

View file

@ -8,6 +8,7 @@ import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT; import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT;
import android.Manifest; import android.Manifest;
import android.Manifest.permission;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
@ -22,7 +23,6 @@ import android.os.Bundle;
import android.provider.Settings; import android.provider.Settings;
import android.text.Html; import android.text.Html;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.view.Gravity; import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
@ -42,6 +42,9 @@ import android.widget.ProgressBar;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.DrawableRes; import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -76,7 +79,6 @@ import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.maps.UiSettings; import com.mapbox.mapboxsdk.maps.UiSettings;
import com.mapbox.pluginscalebar.ScaleBarOptions; import com.mapbox.pluginscalebar.ScaleBarOptions;
import com.mapbox.pluginscalebar.ScaleBarPlugin; import com.mapbox.pluginscalebar.ScaleBarPlugin;
@ -126,6 +128,7 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@ -242,6 +245,56 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
private PlaceAdapter adapter; private PlaceAdapter adapter;
private NearbyParentFragmentInstanceReadyCallback nearbyParentFragmentInstanceReadyCallback; private NearbyParentFragmentInstanceReadyCallback nearbyParentFragmentInstanceReadyCallback;
private boolean isAdvancedQueryFragmentVisible = false; private boolean isAdvancedQueryFragmentVisible = false;
private ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() {
@Override
public void onActivityResult(Map<String, Boolean> result) {
boolean areAllGranted = true;
for (final boolean b : result.values()) {
areAllGranted = areAllGranted && b;
}
if (areAllGranted) {
controller.locationPermissionCallback.onLocationPermissionGranted();
} else {
if (shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) {
controller.handleShowRationaleFlowCameraLocation(getActivity());
} else {
controller.locationPermissionCallback.onLocationPermissionDenied(getActivity().getString(R.string.in_app_camera_location_permission_denied));
}
}
}
});
private ActivityResultLauncher<String[]> locationPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() {
@Override
public void onActivityResult(Map<String, Boolean> result) {
boolean areAllGranted = true;
for (final boolean b : result.values()) {
areAllGranted = areAllGranted && b;
}
if (areAllGranted) {
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),
() -> {
if (!(locationManager.isNetworkProviderEnabled() || locationManager.isGPSProviderEnabled())) {
showLocationOffDialog();
}
},
() -> isPermissionDenied = true,
null,
false);
} else {
isPermissionDenied = true;
}
}
}
});
/** /**
* Holds filtered markers that are to be shown * Holds filtered markers that are to be shown
@ -429,7 +482,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
updateMarker(isBookmarked, place, null); updateMarker(isBookmarked, place, null);
return Unit.INSTANCE; return Unit.INSTANCE;
}, },
commonPlaceClickActions commonPlaceClickActions,
inAppCameraLocationPermissionLauncher
); );
rvNearbyList.setAdapter(adapter); rvNearbyList.setAdapter(adapter);
} }
@ -1214,12 +1268,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
@Override @Override
public void checkPermissionsAndPerformAction() { public void checkPermissionsAndPerformAction() {
Timber.d("Checking permission and perfoming action"); Timber.d("Checking permission and perfoming action");
PermissionUtils.checkPermissionsAndPerformAction(getActivity(), locationPermissionLauncher.launch(new String[]{permission.ACCESS_FINE_LOCATION});
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
this::locationPermissionGranted,
() -> isPermissionDenied = true,
R.string.location_permission_title,
R.string.location_permission_rationale_nearby);
} }
/** /**
@ -1815,7 +1864,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
if (fabCamera.isShown()) { if (fabCamera.isShown()) {
Timber.d("Camera button tapped. Place: %s", selectedPlace.toString()); Timber.d("Camera button tapped. Place: %s", selectedPlace.toString());
storeSharedPrefs(selectedPlace); storeSharedPrefs(selectedPlace);
controller.initiateCameraPick(getActivity()); controller.initiateCameraPick(getActivity(), inAppCameraLocationPermissionLauncher);
} }
}); });

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons.nearby.fragments package fr.free.nrw.commons.nearby.fragments
import androidx.activity.result.ActivityResultLauncher
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao
import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.nearby.Place
import fr.free.nrw.commons.nearby.placeAdapterDelegate import fr.free.nrw.commons.nearby.placeAdapterDelegate
@ -9,7 +10,8 @@ class PlaceAdapter(
bookmarkLocationsDao: BookmarkLocationsDao, bookmarkLocationsDao: BookmarkLocationsDao,
onPlaceClicked: ((Place) -> Unit)? = null, onPlaceClicked: ((Place) -> Unit)? = null,
onBookmarkClicked: (Place, Boolean) -> Unit, onBookmarkClicked: (Place, Boolean) -> Unit,
commonPlaceClickActions: CommonPlaceClickActions commonPlaceClickActions: CommonPlaceClickActions,
inAppCameraLocationPermissionLauncher: ActivityResultLauncher<Array<String>>
) : ) :
BaseDelegateAdapter<Place>( BaseDelegateAdapter<Place>(
placeAdapterDelegate( placeAdapterDelegate(
@ -19,7 +21,8 @@ class PlaceAdapter(
commonPlaceClickActions.onGalleryClicked(), commonPlaceClickActions.onGalleryClicked(),
onBookmarkClicked, onBookmarkClicked,
commonPlaceClickActions.onOverflowClicked(), commonPlaceClickActions.onOverflowClicked(),
commonPlaceClickActions.onDirectionsClicked() commonPlaceClickActions.onDirectionsClicked(),
inAppCameraLocationPermissionLauncher
), ),
areItemsTheSame = {oldItem, newItem -> oldItem.wikiDataEntityId == newItem.wikiDataEntityId } areItemsTheSame = {oldItem, newItem -> oldItem.wikiDataEntityId == newItem.wikiDataEntityId }
) )

View file

@ -2,7 +2,7 @@ package fr.free.nrw.commons.settings;
import static android.content.Context.MODE_PRIVATE; import static android.content.Context.MODE_PRIVATE;
import android.Manifest; import android.Manifest.permission;
import android.app.Activity; import android.app.Activity;
import android.app.Dialog; import android.app.Dialog;
import android.content.Intent; import android.content.Intent;
@ -18,7 +18,9 @@ import android.widget.AdapterView.OnItemClickListener;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ListView; import android.widget.ListView;
import android.widget.TextView; 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.preference.ListPreference; import androidx.preference.ListPreference;
import androidx.preference.MultiSelectListPreference; import androidx.preference.MultiSelectListPreference;
import androidx.preference.Preference; import androidx.preference.Preference;
@ -31,18 +33,15 @@ import androidx.recyclerview.widget.RecyclerView.Adapter;
import com.karumi.dexter.Dexter; import com.karumi.dexter.Dexter;
import com.karumi.dexter.MultiplePermissionsReport; import com.karumi.dexter.MultiplePermissionsReport;
import com.karumi.dexter.PermissionToken; import com.karumi.dexter.PermissionToken;
import com.karumi.dexter.listener.PermissionGrantedResponse;
import com.karumi.dexter.listener.PermissionRequest; import com.karumi.dexter.listener.PermissionRequest;
import com.karumi.dexter.listener.multi.MultiplePermissionsListener; import com.karumi.dexter.listener.multi.MultiplePermissionsListener;
import com.karumi.dexter.listener.single.BasePermissionListener;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.campaigns.CampaignView; import fr.free.nrw.commons.campaigns.CampaignView;
import fr.free.nrw.commons.contributions.ContributionController;
import fr.free.nrw.commons.contributions.MainActivity; import fr.free.nrw.commons.contributions.MainActivity;
import fr.free.nrw.commons.di.ApplicationlessInjection; import fr.free.nrw.commons.di.ApplicationlessInjection;
import fr.free.nrw.commons.kvstore.JsonKvStore; 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.LocationServiceManager;
import fr.free.nrw.commons.logging.CommonsLogSender; import fr.free.nrw.commons.logging.CommonsLogSender;
import fr.free.nrw.commons.recentlanguages.Language; import fr.free.nrw.commons.recentlanguages.Language;
@ -56,7 +55,7 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Objects; import java.util.Map;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@ -72,6 +71,9 @@ public class SettingsFragment extends PreferenceFragmentCompat {
@Inject @Inject
RecentLanguagesDao recentLanguagesDao; RecentLanguagesDao recentLanguagesDao;
@Inject
ContributionController contributionController;
@Inject @Inject
LocationServiceManager locationManager; LocationServiceManager locationManager;
@ -83,6 +85,18 @@ public class SettingsFragment extends PreferenceFragmentCompat {
private View separator; private View separator;
private ListView languageHistoryListView; private ListView languageHistoryListView;
private static final String GET_CONTENT_PICKER_HELP_URL = "https://commons-app.github.io/docs.html#get-content"; private static final String GET_CONTENT_PICKER_HELP_URL = "https://commons-app.github.io/docs.html#get-content";
private ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() {
@Override
public void onActivityResult(Map<String, Boolean> result) {
boolean areAllGranted = true;
for (final boolean b : result.values()) {
areAllGranted = areAllGranted && b;
}
if (!areAllGranted && shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) {
contributionController.handleShowRationaleFlowCameraLocation(getActivity());
}
}
});
@Override @Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
@ -205,32 +219,7 @@ public class SettingsFragment extends PreferenceFragmentCompat {
* @param activity * @param activity
*/ */
private void createDialogsAndHandleLocationPermissions(Activity activity) { private void createDialogsAndHandleLocationPermissions(Activity activity) {
LocationPermissionsHelper.Dialog locationAccessDialog = new LocationPermissionsHelper.Dialog( inAppCameraLocationPermissionLauncher.launch(new String[]{permission.ACCESS_FINE_LOCATION});
R.string.location_permission_title,
R.string.in_app_camera_location_permission_rationale
);
LocationPermissionsHelper.Dialog locationOffDialog = new LocationPermissionsHelper.Dialog(
R.string.ask_to_turn_location_on,
R.string.in_app_camera_needs_location
);
LocationPermissionsHelper locationPermissionsHelper = new LocationPermissionsHelper(
activity, locationManager, new LocationPermissionCallback() {
@Override
public void onLocationPermissionDenied(String toastMessage) {
// dismiss the dialog
}
@Override
public void onLocationPermissionGranted() {
// dismiss the dialog
}
});
locationPermissionsHelper.handleLocationPermissions(
locationAccessDialog,
locationOffDialog
);
} }
/** /**

View file

@ -149,10 +149,10 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
init(); init();
nearbyPopupAnswers = new HashMap<>(); nearbyPopupAnswers = new HashMap<>();
PermissionUtils.checkPermissionsAndPerformAction(this, PermissionUtils.checkPermissionsAndPerformAction(this,
PERMISSIONS_STORAGE,
this::receiveSharedItems, this::receiveSharedItems,
R.string.storage_permission_title, R.string.storage_permission_title,
R.string.write_storage_permission_rationale_for_image_share); R.string.write_storage_permission_rationale_for_image_share,
PERMISSIONS_STORAGE);
//getting the current dpi of the device and if it is less than 320dp i.e. overlapping //getting the current dpi of the device and if it is less than 320dp i.e. overlapping
//threshold, thumbnails automatically minimizes //threshold, thumbnails automatically minimizes
DisplayMetrics metrics = getResources().getDisplayMetrics(); DisplayMetrics metrics = getResources().getDisplayMetrics();
@ -255,12 +255,12 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
final boolean hasAllPermissions = PermissionUtils.hasPermission(this, PERMISSIONS_STORAGE); final boolean hasAllPermissions = PermissionUtils.hasPermission(this, PERMISSIONS_STORAGE);
if (!hasAllPermissions) { if (!hasAllPermissions) {
PermissionUtils.checkPermissionsAndPerformAction(this, PermissionUtils.checkPermissionsAndPerformAction(this,
PERMISSIONS_STORAGE,
() -> { () -> {
//TODO handle this //TODO handle this
}, },
R.string.storage_permission_title, R.string.storage_permission_title,
R.string.write_storage_permission_rationale_for_image_share); R.string.write_storage_permission_rationale_for_image_share,
PERMISSIONS_STORAGE);
} }
} }

View file

@ -38,7 +38,6 @@ object DownloadUtils {
} }
PermissionUtils.checkPermissionsAndPerformAction( PermissionUtils.checkPermissionsAndPerformAction(
activity, activity,
PermissionUtils.PERMISSIONS_STORAGE,
{ enqueueRequest(activity, req) }, { enqueueRequest(activity, req) },
{ {
Toast.makeText( Toast.makeText(
@ -48,8 +47,9 @@ object DownloadUtils {
).show() ).show()
}, },
R.string.storage_permission, R.string.storage_permission,
R.string.write_storage_permission_rationale R.string.write_storage_permission_rationale,
) *PermissionUtils.PERMISSIONS_STORAGE
)
} }
private fun enqueueRequest(activity: Activity, req: DownloadManager.Request) { private fun enqueueRequest(activity: Activity, req: DownloadManager.Request) {

View file

@ -100,11 +100,11 @@ public class PermissionUtils {
* @param rationaleMessage rationale message to be displayed when permission was denied. It * @param rationaleMessage rationale message to be displayed when permission was denied. It
* can be an invalid @StringRes * can be an invalid @StringRes
*/ */
public static void checkPermissionsAndPerformAction(Activity activity, String[] permissions, public static void checkPermissionsAndPerformAction(Activity activity,
Runnable onPermissionGranted, @StringRes int rationaleTitle, Runnable onPermissionGranted, @StringRes int rationaleTitle,
@StringRes int rationaleMessage) { @StringRes int rationaleMessage, String... permissions) {
checkPermissionsAndPerformAction(activity, permissions, onPermissionGranted, null, checkPermissionsAndPerformAction(activity, onPermissionGranted, null,
rationaleTitle, rationaleMessage); rationaleTitle, rationaleMessage, permissions);
} }
/** /**
@ -125,9 +125,9 @@ public class PermissionUtils {
* @param rationaleTitle rationale title to be displayed when permission was denied * @param rationaleTitle rationale title to be displayed when permission was denied
* @param rationaleMessage rationale message to be displayed when permission was denied * @param rationaleMessage rationale message to be displayed when permission was denied
*/ */
public static void checkPermissionsAndPerformAction(Activity activity, String[] permissions, public static void checkPermissionsAndPerformAction(Activity activity,
Runnable onPermissionGranted, Runnable onPermissionDenied, @StringRes int rationaleTitle, Runnable onPermissionGranted, Runnable onPermissionDenied, @StringRes int rationaleTitle,
@StringRes int rationaleMessage) { @StringRes int rationaleMessage, String... permissions) {
Dexter.withActivity(activity) Dexter.withActivity(activity)
.withPermissions(permissions) .withPermissions(permissions)
.withListener(new MultiplePermissionsListener() { .withListener(new MultiplePermissionsListener() {
@ -135,6 +135,7 @@ public class PermissionUtils {
public void onPermissionsChecked(MultiplePermissionsReport report) { public void onPermissionsChecked(MultiplePermissionsReport report) {
if (report.areAllPermissionsGranted()) { if (report.areAllPermissionsGranted()) {
onPermissionGranted.run(); onPermissionGranted.run();
return;
} }
if (report.isAnyPermissionPermanentlyDenied()) { if (report.isAnyPermissionPermanentlyDenied()) {
// permission is denied permanently, we will show user a dialog message. // permission is denied permanently, we will show user a dialog message.