-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
5196: Fix in-app camera location loss #5249
Changes from 7 commits
02acabe
d6ac060
655ff4c
a8e0c54
7208018
3c0339b
5f9ff36
e72be00
74f8c13
fa8674c
def8079
5f873d1
f0dd7d2
5837b8b
a8783f6
96c7b4d
4417140
28618ce
efbd199
0a2a10b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,18 +3,24 @@ | |
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.content.pm.PackageManager; | ||
import android.provider.Settings; | ||
import androidx.annotation.NonNull; | ||
import fr.free.nrw.commons.R; | ||
import fr.free.nrw.commons.filepicker.DefaultCallback; | ||
import fr.free.nrw.commons.filepicker.FilePicker; | ||
import fr.free.nrw.commons.filepicker.FilePicker.ImageSource; | ||
import fr.free.nrw.commons.filepicker.UploadableFile; | ||
import fr.free.nrw.commons.kvstore.JsonKvStore; | ||
import fr.free.nrw.commons.location.LatLng; | ||
import fr.free.nrw.commons.location.LocationServiceManager; | ||
import fr.free.nrw.commons.nearby.Place; | ||
import fr.free.nrw.commons.upload.UploadActivity; | ||
import fr.free.nrw.commons.utils.DialogUtil; | ||
import fr.free.nrw.commons.utils.PermissionUtils; | ||
import fr.free.nrw.commons.utils.ViewUtil; | ||
import java.util.ArrayList; | ||
|
@@ -28,7 +34,10 @@ public class ContributionController { | |
|
||
public static final String ACTION_INTERNAL_UPLOADS = "internalImageUploads"; | ||
private final JsonKvStore defaultKvStore; | ||
private LatLng locationBeforeImageCapture; | ||
|
||
@Inject | ||
LocationServiceManager locationManager; | ||
@Inject | ||
public ContributionController(@Named("default_preferences") JsonKvStore defaultKvStore) { | ||
this.defaultKvStore = defaultKvStore; | ||
|
@@ -46,11 +55,112 @@ public void initiateCameraPick(Activity activity) { | |
|
||
PermissionUtils.checkPermissionsAndPerformAction(activity, | ||
Manifest.permission.WRITE_EXTERNAL_STORAGE, | ||
() -> initiateCameraUpload(activity), | ||
() -> { | ||
if (!(PermissionUtils.hasPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION) | ||
&& isLocationAccessToAppsTurnedOn())) { | ||
askUserToAllowLocationAccess(activity); | ||
} else { | ||
initiateCameraUpload(activity); | ||
} | ||
}, | ||
R.string.storage_permission_title, | ||
R.string.write_storage_permission_rationale); | ||
} | ||
|
||
/** | ||
* 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 | ||
* | ||
* @param activity | ||
*/ | ||
private void askUserToAllowLocationAccess(Activity activity) { | ||
DialogUtil.showAlertDialog(activity, | ||
activity.getString(R.string.location_permission_title), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The title for this dialog could be something more meaningful than "Requesting location permission" May be try "Record location for in-app shots" ? |
||
activity.getString(R.string.in_app_camera_location_access_explanation), | ||
activity.getString(R.string.option_allow), | ||
activity.getString(R.string.option_dismiss), | ||
()-> requestForLocationAccess(activity), | ||
() -> initiateCameraUpload(activity), | ||
null, | ||
true); | ||
} | ||
|
||
/** | ||
* Ask for location permission if the user agrees on attaching location with pictures | ||
* and the app does not have the access to location | ||
* | ||
* @param activity | ||
*/ | ||
|
||
private void requestForLocationAccess(Activity activity) { | ||
PermissionUtils.checkPermissionsAndPerformAction(activity, | ||
permission.ACCESS_FINE_LOCATION, | ||
() -> onLocationPermissionGranted(activity), | ||
() -> {}, | ||
R.string.ask_to_turn_location_on, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIUC, this should be |
||
R.string.in_app_camera_location_permission_rationale); | ||
} | ||
|
||
/** | ||
* Check if apps have access to location even after having individual access | ||
* | ||
* @return | ||
*/ | ||
private boolean isLocationAccessToAppsTurnedOn() { | ||
return (locationManager.isNetworkProviderEnabled() || locationManager.isGPSProviderEnabled()); | ||
} | ||
|
||
/** | ||
* Initiate in-app camera if apps have access to location. | ||
* Otherwise, show location-off dialog. | ||
* | ||
* @param activity | ||
*/ | ||
private void onLocationPermissionGranted(Activity activity) { | ||
if (!isLocationAccessToAppsTurnedOn()) { | ||
showLocationOffDialog(activity); | ||
} else { | ||
initiateCameraUpload(activity); | ||
} | ||
} | ||
|
||
/** | ||
* Ask user to grant location access to apps | ||
* | ||
* @param activity | ||
*/ | ||
|
||
private void showLocationOffDialog(Activity activity) { | ||
DialogUtil | ||
.showAlertDialog(activity, | ||
activity.getString(R.string.location_permission_title), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be |
||
activity.getString(R.string.in_app_camera_needs_location), | ||
activity.getString(R.string.title_app_shortcut_setting), | ||
() -> openLocationSettings(activity), | ||
true); | ||
} | ||
|
||
/** | ||
* Open location source settings so that apps with location access can access it | ||
* | ||
* @param activity | ||
*/ | ||
|
||
private void openLocationSettings(Activity activity) { | ||
final Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Strangely, this is opening the app's setting screen instead of the location settings screen on my device (Nord running Android 12). Any thoughts on possible reasons for this ? 🤔 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I observed a similar behavior. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is used at many places in the app already and works pretty well on my device, so I assumed it to be correct. Will check this out. Thank you for mentioning this! |
||
final PackageManager packageManager = activity.getPackageManager(); | ||
|
||
if (intent.resolveActivity(packageManager)!= null) { | ||
activity.startActivity(intent); | ||
} | ||
} | ||
|
||
/** | ||
* Initiate gallery picker | ||
*/ | ||
|
@@ -99,6 +209,7 @@ private void setPickerConfiguration(Activity activity, | |
*/ | ||
private void initiateCameraUpload(Activity activity) { | ||
setPickerConfiguration(activity, false); | ||
locationBeforeImageCapture = locationManager.getLastLocation(); | ||
FilePicker.openCameraForImage(activity, 0); | ||
} | ||
|
||
|
@@ -134,7 +245,8 @@ public List<UploadableFile> handleExternalImagesPicked(Activity activity, | |
|
||
/** | ||
* Returns intent to be passed to upload activity | ||
* Attaches place object for nearby uploads | ||
* Attaches place object for nearby uploads and | ||
* location before image capture if in-app camera is used | ||
*/ | ||
private Intent handleImagesPicked(Context context, | ||
List<UploadableFile> imagesFiles) { | ||
|
@@ -148,6 +260,12 @@ private Intent handleImagesPicked(Context context, | |
shareIntent.putExtra(PLACE_OBJECT, place); | ||
} | ||
|
||
if (locationBeforeImageCapture != null) { | ||
shareIntent.putExtra( | ||
UploadActivity.LOCATION_BEFORE_IMAGE_CAPTURE, | ||
locationBeforeImageCapture); | ||
} | ||
|
||
return shareIntent; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having a check like this to trigger the consent dialog could be a bit incorrect. We attach the location to the upload when the EXIF location is unavailable. This could also occur in cases where the app has location access and location is turned on the device but saving location is turned off in the device camera. The user could've done this intentionally. The app obtained location would be uploaded in this case without the user having any idea about it. I believe it is ideal to get the consent from the user before doing so.
There's also the case of lower Android version devices (such as API 21) for which there is no notion of location permission. So, this dialog is shown to them only when location is turned off.
I also observed this dialog appears each time when trying to use the in-app camera and location is either off / location permissions is unavailable. This might become a bit annoying if the user has intentionally not granted those.
To better handle this, I would suggested showcasing the consent dialog first time (unconditinally) when user users in-app camera and store their preference. Given this would be a one-time dialog, we should also have a corresponding preference in settings in case the user wants to toggle their choice.
When we have user's consent to attach the location, we could then show dialog about lack of location permission / location enabled when in-app camera is triggered.
This seems like a better and reasonable flow to me. Thoughts ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sivaraam Just a clarification -
Is this referring to displaying the consent dialog?
If yes, I'm a little skeptical about having a setting to toggle the choice to display the dialog. In most apps, we usually have an option like "Do not show this message again" and once the user selects the option, we do not see it again. A user who's curious about enabling location could probably go through the information we have already associated while enabling/disabling permissions for location and would not want a pop up enabled again.
I may be wrong but just wanted to understand the perspective behind it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Appeared incorrect to me as well, I felt I was missing out some case but did not know which one. Thank you for elaborating about it, I'll make this dialog compulsory on the first run.
Also, I'd suggested this "Do not show this message again" in my comment above. If this sounds okay, I'll add it for the Dismiss case (it does not appear again if the user allows).