mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-30 22:34:02 +01:00
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:
parent
8d604a25fa
commit
9525409b9b
18 changed files with 442 additions and 251 deletions
|
|
@ -3,10 +3,12 @@ package fr.free.nrw.commons.contributions;
|
|||
import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT;
|
||||
|
||||
import android.Manifest;
|
||||
import android.Manifest.permission;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.widget.Toast;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.NonNull;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.filepicker.DefaultCallback;
|
||||
|
|
@ -37,9 +39,12 @@ public class ContributionController {
|
|||
private final JsonKvStore defaultKvStore;
|
||||
private LatLng locationBeforeImageCapture;
|
||||
private boolean isInAppCameraUpload;
|
||||
public LocationPermissionCallback locationPermissionCallback;
|
||||
private LocationPermissionsHelper locationPermissionsHelper;
|
||||
|
||||
@Inject
|
||||
LocationServiceManager locationManager;
|
||||
|
||||
@Inject
|
||||
public ContributionController(@Named("default_preferences") JsonKvStore defaultKvStore) {
|
||||
this.defaultKvStore = defaultKvStore;
|
||||
|
|
@ -48,7 +53,8 @@ public class ContributionController {
|
|||
/**
|
||||
* 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);
|
||||
if (!useExtStorage) {
|
||||
initiateCameraUpload(activity);
|
||||
|
|
@ -56,19 +62,20 @@ public class ContributionController {
|
|||
}
|
||||
|
||||
PermissionUtils.checkPermissionsAndPerformAction(activity,
|
||||
PermissionUtils.PERMISSIONS_STORAGE,
|
||||
() -> {
|
||||
if (defaultKvStore.getBoolean("inAppCameraFirstRun")) {
|
||||
defaultKvStore.putBoolean("inAppCameraFirstRun", false);
|
||||
askUserToAllowLocationAccess(activity);
|
||||
} else if(defaultKvStore.getBoolean("inAppCameraLocationPref")) {
|
||||
createDialogsAndHandleLocationPermissions(activity);
|
||||
} else {
|
||||
initiateCameraUpload(activity);
|
||||
}
|
||||
},
|
||||
R.string.storage_permission_title,
|
||||
R.string.write_storage_permission_rationale);
|
||||
() -> {
|
||||
if (defaultKvStore.getBoolean("inAppCameraFirstRun")) {
|
||||
defaultKvStore.putBoolean("inAppCameraFirstRun", false);
|
||||
askUserToAllowLocationAccess(activity, inAppCameraLocationPermissionLauncher);
|
||||
} else if (defaultKvStore.getBoolean("inAppCameraLocationPref")) {
|
||||
createDialogsAndHandleLocationPermissions(activity,
|
||||
inAppCameraLocationPermissionLauncher);
|
||||
} else {
|
||||
initiateCameraUpload(activity);
|
||||
}
|
||||
},
|
||||
R.string.storage_permission_title,
|
||||
R.string.write_storage_permission_rationale,
|
||||
PermissionUtils.PERMISSIONS_STORAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -76,7 +83,8 @@ public class ContributionController {
|
|||
*
|
||||
* @param activity
|
||||
*/
|
||||
private void createDialogsAndHandleLocationPermissions(Activity activity) {
|
||||
private void createDialogsAndHandleLocationPermissions(Activity activity,
|
||||
ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher) {
|
||||
LocationPermissionsHelper.Dialog locationAccessDialog = new Dialog(
|
||||
R.string.location_permission_title,
|
||||
R.string.in_app_camera_location_permission_rationale
|
||||
|
|
@ -86,52 +94,73 @@ public class ContributionController {
|
|||
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) {
|
||||
Toast.makeText(
|
||||
activity,
|
||||
toastMessage,
|
||||
Toast.LENGTH_LONG
|
||||
).show();
|
||||
initiateCameraUpload(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationPermissionGranted() {
|
||||
initiateCameraUpload(activity);
|
||||
}
|
||||
locationPermissionCallback = new LocationPermissionCallback() {
|
||||
@Override
|
||||
public void onLocationPermissionDenied(String toastMessage) {
|
||||
Toast.makeText(
|
||||
activity,
|
||||
toastMessage,
|
||||
Toast.LENGTH_LONG
|
||||
).show();
|
||||
initiateCameraUpload(activity);
|
||||
}
|
||||
);
|
||||
locationPermissionsHelper.handleLocationPermissions(
|
||||
locationAccessDialog,
|
||||
locationOffDialog
|
||||
);
|
||||
|
||||
@Override
|
||||
public void onLocationPermissionGranted() {
|
||||
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.
|
||||
* If the user selects "Yes", then:
|
||||
*
|
||||
* Location is taken from the EXIF if the default camera application
|
||||
* does not redact location tags.
|
||||
*
|
||||
* Otherwise, if the EXIF metadata does not have location information,
|
||||
* then location captured by the app is used
|
||||
* Suggest user to attach location information with pictures. If the user selects "Yes", then:
|
||||
* <p>
|
||||
* Location is taken from the EXIF if the default camera application does not redact location
|
||||
* tags.
|
||||
* <p>
|
||||
* Otherwise, if the EXIF metadata does not have location information, then location captured by
|
||||
* the app is used
|
||||
*
|
||||
* @param activity
|
||||
*/
|
||||
private void askUserToAllowLocationAccess(Activity activity) {
|
||||
private void askUserToAllowLocationAccess(Activity activity,
|
||||
ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher) {
|
||||
DialogUtil.showAlertDialog(activity,
|
||||
activity.getString(R.string.in_app_camera_location_permission_title),
|
||||
activity.getString(R.string.in_app_camera_location_access_explanation),
|
||||
activity.getString(R.string.option_allow),
|
||||
activity.getString(R.string.option_dismiss),
|
||||
()-> {
|
||||
() -> {
|
||||
defaultKvStore.putBoolean("inAppCameraLocationPref", true);
|
||||
createDialogsAndHandleLocationPermissions(activity);
|
||||
createDialogsAndHandleLocationPermissions(activity,
|
||||
inAppCameraLocationPermissionLauncher);
|
||||
},
|
||||
() -> {
|
||||
defaultKvStore.putBoolean("inAppCameraLocationPref", false);
|
||||
|
|
@ -141,15 +170,6 @@ public class ContributionController {
|
|||
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
|
||||
*/
|
||||
|
|
@ -161,22 +181,24 @@ public class ContributionController {
|
|||
* Initiate gallery picker with permission
|
||||
*/
|
||||
public void initiateCustomGalleryPickWithPermission(final Activity activity) {
|
||||
setPickerConfiguration(activity,true);
|
||||
setPickerConfiguration(activity, true);
|
||||
|
||||
PermissionUtils.checkPermissionsAndPerformAction(activity,
|
||||
PermissionUtils.PERMISSIONS_STORAGE,
|
||||
() -> FilePicker.openCustomSelector(activity, 0),
|
||||
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
|
||||
*/
|
||||
private void initiateGalleryUpload(final Activity activity, final boolean allowMultipleUploads) {
|
||||
private void initiateGalleryUpload(final Activity activity,
|
||||
final boolean allowMultipleUploads) {
|
||||
setPickerConfiguration(activity, allowMultipleUploads);
|
||||
boolean openDocumentIntentPreferred = defaultKvStore.getBoolean("openDocumentPhotoPickerPref", true);
|
||||
boolean openDocumentIntentPreferred = defaultKvStore.getBoolean(
|
||||
"openDocumentPhotoPickerPref", true);
|
||||
FilePicker.openGallery(activity, 0, openDocumentIntentPreferred);
|
||||
}
|
||||
|
||||
|
|
@ -184,11 +206,11 @@ public class ContributionController {
|
|||
* Sets configuration for file picker
|
||||
*/
|
||||
private void setPickerConfiguration(Activity activity,
|
||||
boolean allowMultipleUploads) {
|
||||
boolean allowMultipleUploads) {
|
||||
boolean copyToExternalStorage = defaultKvStore.getBoolean("useExternalStorage", true);
|
||||
FilePicker.configuration(activity)
|
||||
.setCopyTakenPhotosToPublicGalleryAppFolder(copyToExternalStorage)
|
||||
.setAllowMultiplePickInGallery(allowMultipleUploads);
|
||||
.setCopyTakenPhotosToPublicGalleryAppFolder(copyToExternalStorage)
|
||||
.setAllowMultiplePickInGallery(allowMultipleUploads);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -206,36 +228,39 @@ public class ContributionController {
|
|||
/**
|
||||
* Attaches callback for file picker.
|
||||
*/
|
||||
public void handleActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
|
||||
FilePicker.handleActivityResult(requestCode, resultCode, data, activity, new DefaultCallback() {
|
||||
public void handleActivityResult(Activity activity, int requestCode, int resultCode,
|
||||
Intent data) {
|
||||
FilePicker.handleActivityResult(requestCode, resultCode, data, activity,
|
||||
new DefaultCallback() {
|
||||
|
||||
@Override
|
||||
public void onCanceled(final ImageSource source, final int type) {
|
||||
super.onCanceled(source, type);
|
||||
defaultKvStore.remove(PLACE_OBJECT);
|
||||
}
|
||||
@Override
|
||||
public void onCanceled(final ImageSource source, final int type) {
|
||||
super.onCanceled(source, type);
|
||||
defaultKvStore.remove(PLACE_OBJECT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImagePickerError(Exception e, FilePicker.ImageSource source, int type) {
|
||||
ViewUtil.showShortToast(activity, R.string.error_occurred_in_picking_images);
|
||||
}
|
||||
@Override
|
||||
public void onImagePickerError(Exception e, FilePicker.ImageSource source,
|
||||
int type) {
|
||||
ViewUtil.showShortToast(activity, R.string.error_occurred_in_picking_images);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImagesPicked(@NonNull List<UploadableFile> imagesFiles, FilePicker.ImageSource source, int type) {
|
||||
Intent intent = handleImagesPicked(activity, imagesFiles);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void onImagesPicked(@NonNull List<UploadableFile> imagesFiles,
|
||||
FilePicker.ImageSource source, int type) {
|
||||
Intent intent = handleImagesPicked(activity, imagesFiles);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public List<UploadableFile> handleExternalImagesPicked(Activity activity,
|
||||
Intent data) {
|
||||
Intent data) {
|
||||
return FilePicker.handleExternalImagesPicked(data, activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns intent to be passed to upload activity
|
||||
* Attaches place object for nearby uploads and
|
||||
* Returns intent to be passed to upload activity Attaches place object for nearby uploads and
|
||||
* location before image capture if in-app camera is used
|
||||
*/
|
||||
private Intent handleImagesPicked(Context context,
|
||||
|
|
@ -263,5 +288,4 @@ public class ContributionController {
|
|||
isInAppCameraUpload = false; // reset the flag for next use
|
||||
return shareIntent;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import static fr.free.nrw.commons.profile.ProfileActivity.KEY_USERNAME;
|
|||
import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
|
||||
|
||||
import android.Manifest;
|
||||
import android.Manifest.permission;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
|
@ -21,6 +22,9 @@ import android.widget.CheckBox;
|
|||
import android.widget.LinearLayout;
|
||||
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;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
|
@ -35,6 +39,7 @@ import fr.free.nrw.commons.profile.ProfileActivity;
|
|||
import fr.free.nrw.commons.theme.BaseActivity;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import androidx.work.WorkManager;
|
||||
|
|
@ -117,6 +122,29 @@ public class ContributionsFragment
|
|||
|
||||
String userName;
|
||||
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
|
||||
public static ContributionsFragment newInstance() {
|
||||
|
|
@ -451,12 +479,7 @@ public class ContributionsFragment
|
|||
}
|
||||
|
||||
private void requestLocationPermission() {
|
||||
PermissionUtils.checkPermissionsAndPerformAction(getActivity(),
|
||||
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
|
||||
this::onLocationPermissionGranted,
|
||||
this::displayYouWontSeeNearbyMessage,
|
||||
-1,
|
||||
-1);
|
||||
nearbyLocationPermissionLauncher.launch(new String[]{permission.ACCESS_FINE_LOCATION});
|
||||
}
|
||||
|
||||
private void onLocationPermissionGranted() {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import static android.view.View.GONE;
|
|||
import static android.view.View.VISIBLE;
|
||||
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.res.Configuration;
|
||||
import android.net.Uri;
|
||||
|
|
@ -18,6 +19,9 @@ import android.view.animation.AnimationUtils;
|
|||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
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.Nullable;
|
||||
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.ViewUtil;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
|
@ -114,6 +119,27 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
|
|||
|
||||
private int contributionsSize;
|
||||
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
|
||||
public void onCreate(@Nullable @org.jetbrains.annotations.Nullable final Bundle savedInstanceState) {
|
||||
|
|
@ -297,7 +323,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
|
|||
private void setListeners() {
|
||||
fabPlus.setOnClickListener(view -> animateFAB(isFabOpen));
|
||||
fabCamera.setOnClickListener(view -> {
|
||||
controller.initiateCameraPick(getActivity());
|
||||
controller.initiateCameraPick(getActivity(), inAppCameraLocationPermissionLauncher);
|
||||
animateFAB(isFabOpen);
|
||||
});
|
||||
fabGallery.setOnClickListener(view -> {
|
||||
|
|
@ -393,10 +419,10 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
|
|||
@Override
|
||||
public void deleteUpload(final Contribution contribution) {
|
||||
DialogUtil.showAlertDialog(getActivity(),
|
||||
String.format(getString(R.string.cancelling_upload),
|
||||
Locale.getDefault().getDisplayLanguage()),
|
||||
String.format(getString(R.string.cancel_upload_dialog),
|
||||
Locale.getDefault().getDisplayLanguage()),
|
||||
String.format(Locale.getDefault().getDisplayLanguage(),
|
||||
getString(R.string.cancelling_upload)),
|
||||
String.format(Locale.getDefault().getDisplayLanguage(),
|
||||
getString(R.string.cancel_upload_dialog)),
|
||||
"YES", "NO",
|
||||
() -> {
|
||||
ViewUtil.showShortToast(getContext(), R.string.cancelling_upload);
|
||||
|
|
@ -422,8 +448,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
|
|||
public void addImageToWikipedia(Contribution contribution) {
|
||||
DialogUtil.showAlertDialog(getActivity(),
|
||||
getString(R.string.add_picture_to_wikipedia_article_title),
|
||||
String.format(getString(R.string.add_picture_to_wikipedia_article_desc),
|
||||
Locale.getDefault().getDisplayLanguage()),
|
||||
getString(R.string.add_picture_to_wikipedia_article_desc),
|
||||
() -> {
|
||||
showAddImageToWikipediaInstructions(contribution);
|
||||
}, () -> {
|
||||
|
|
|
|||
|
|
@ -165,11 +165,10 @@ public class MainActivity extends BaseActivity
|
|||
if (VERSION.SDK_INT >= VERSION_CODES.Q) {
|
||||
PermissionUtils.checkPermissionsAndPerformAction(
|
||||
this,
|
||||
new String[]{permission.ACCESS_MEDIA_LOCATION},
|
||||
() -> {},
|
||||
R.string.media_location_permission_denied,
|
||||
R.string.add_location_manually
|
||||
);
|
||||
R.string.add_location_manually,
|
||||
permission.ACCESS_MEDIA_LOCATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue