From f117b3fce9388aa0ef914abea325954781dc3128 Mon Sep 17 00:00:00 2001 From: ilgazer Date: Fri, 13 Jul 2018 19:46:03 +0300 Subject: [PATCH 01/16] build.gradle: add ExifInterface support library --- app/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/app/build.gradle b/app/build.gradle index bb0220b30c..dd1ea27752 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -22,6 +22,7 @@ dependencies { implementation 'info.debatty:java-string-similarity:0.24' implementation 'com.borjabravo:readmoretextview:2.1.0' implementation 'com.android.support.constraint:constraint-layout:1.0.2' + implementation "com.android.support:exifinterface:27.1.1" implementation('com.mapbox.mapboxsdk:mapbox-android-sdk:5.5.0@aar') { transitive = true } From 827f3e0b3acf73b3987e4a2c08e13c3a5b0be3c4 Mon Sep 17 00:00:00 2001 From: ilgazer Date: Fri, 13 Jul 2018 20:01:06 +0300 Subject: [PATCH 02/16] FileProcessor: implement methods for EXIF and location anonymization *Add method anonymizeCoord() that reduces the resolution of GPS coordinates. Think of each coordinate as a pixel. This function reduces the resolution of "the world" to one coordinate(pixel) for every x kilometers(x is the variable prefLocationAccuracy) *Add method getAnonymizedDecimalCoords() that invokes anonymizeCoord() for latitude and longtitude and returns the coordinates with reduced accuracy in "lat|long" format. *Add method redactEXIFData() that removes exif tags from image and returns the Uri referring to the new, anonymized image. *Change behaviour of getPathOfMediaOrCopy() such that if anonymization is enabled, the function makes a copy of the file. The path to this copy is stored in variable fileOrCopyPath and the function always returns this path. --- .../nrw/commons/upload/FileProcessor.java | 107 +++++++++++++++--- 1 file changed, 94 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java b/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java index 2845b8d1fa..a2fe81393c 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java @@ -5,6 +5,8 @@ import android.content.ContentResolver; import android.content.Context; import android.content.SharedPreferences; +import android.location.Location; +import android.support.media.ExifInterface; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -16,8 +18,11 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.lang.ref.WeakReference; +import java.util.Arrays; +import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.Set; import javax.inject.Inject; import javax.inject.Named; @@ -51,47 +56,58 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse { private String decimalCoords; private boolean haveCheckedForOtherImages = false; private String filePath; - private boolean useExtStorage; + private String fileOrCopyPath=null; + private boolean prefUseExtStorage; private boolean cacheFound; private GPSExtractor tempImageObj; + private Set prefRedactEXIFTags; + private double prefLocationAccuracy; + FileProcessor(Uri mediaUri, ContentResolver contentResolver, Context context) { this.mediaUri = mediaUri; + Timber.d("mediaUri:"+ (this.mediaUri != null ? this.mediaUri.getPath() : "null")); this.contentResolver = contentResolver; this.context = context; ApplicationlessInjection.getInstance(context.getApplicationContext()).getCommonsApplicationComponent().inject(this); - useExtStorage = prefs.getBoolean("useExternalStorage", true); + prefUseExtStorage = prefs.getBoolean("useExternalStorage", true); + prefRedactEXIFTags = prefs.getStringSet("redactExifTags", Collections.emptySet() ); + prefLocationAccuracy = Double.valueOf(prefs.getString("locationAccuracy", "0"))/111300; //about 111300 meters in one degree + Timber.d("prefLocationAccuracy:"+prefLocationAccuracy); } /** * Gets file path from media URI. * In older devices getPath() may fail depending on the source URI, creating and using a copy of the file seems to work instead. + * If cleansing EXIF tags is enabled, it always copies the file. * * @return file path of media */ @Nullable private String getPathOfMediaOrCopy() { + if (fileOrCopyPath!=null) + return fileOrCopyPath; filePath = FileUtils.getPath(context, mediaUri); Timber.d("Filepath: " + filePath); - if (filePath == null) { - String copyPath = null; + if (filePath == null || !prefRedactEXIFTags.isEmpty()) { try { ParcelFileDescriptor descriptor = contentResolver.openFileDescriptor(mediaUri, "r"); if (descriptor != null) { - if (useExtStorage) { - copyPath = FileUtils.createCopyPath(descriptor); - return copyPath; + if (prefUseExtStorage) { + fileOrCopyPath = FileUtils.createCopyPath(descriptor); + return fileOrCopyPath; } - copyPath = getApplicationContext().getCacheDir().getAbsolutePath() + "/" + new Date().getTime() + ".jpg"; - FileUtils.copy(descriptor.getFileDescriptor(), copyPath); - Timber.d("Filepath (copied): %s", copyPath); - return copyPath; + fileOrCopyPath = getApplicationContext().getCacheDir().getAbsolutePath() + "/" + new Date().getTime() + ".jpg"; + FileUtils.copy(descriptor.getFileDescriptor(), fileOrCopyPath); + Timber.d("Filepath (copied): %s", fileOrCopyPath); + return fileOrCopyPath; } } catch (IOException e) { - Timber.w(e, "Error in file " + copyPath); + Timber.w(e, "Error in file " + fileOrCopyPath); return null; } } + fileOrCopyPath=filePath; return filePath; } @@ -130,10 +146,41 @@ GPSExtractor processFileCoordinates(boolean gpsEnabled) { return imageObj; } + /** + * @return The coordinates with reduced accuracy in "lat|long" format + */ String getDecimalCoords() { return decimalCoords; } + /** + * Reduces the accuracy of the coordinate according to location accuracy preference. + * + * @param input + * @return The coordinate with reduced accuracy. + */ + double anonymizeCoord(double input){ + double intermediate=Math.round(input/prefLocationAccuracy)*prefLocationAccuracy; + return Math.round(intermediate*100000.0)/100000.0; //Round to 5th decimal place. + } + + /** + * Reduces the accuracy of file coordinates according to location accuracy preference. + * + * @return The coordinates with reduced accuracy in "lat|long" format + */ + String getAnonymizedDecimalCoords(){ + Timber.d("Anonymizing coords with setting:"+prefLocationAccuracy); + if(prefLocationAccuracy<0) + return null; + else if (prefLocationAccuracy==0) + return decimalCoords; + else{ + return String.valueOf(anonymizeCoord(imageObj.getDecLatitude())) + "|" + + String.valueOf(anonymizeCoord(imageObj.getDecLongitude())); + } + } + /** * Find other images around the same location that were taken within the last 20 sec * @@ -237,13 +284,47 @@ boolean isCacheFound() { return cacheFound; } + + /** + *Redacts EXIF data from the file. + * + * @return Uri of the new file. + **/ + public Uri redactEXIFData() { + try { + Timber.d("Tags to be redacted:"+ Arrays.toString(prefRedactEXIFTags.toArray())); + Timber.v("File path:"+getPathOfMediaOrCopy()); + if (!prefRedactEXIFTags.isEmpty() && getPathOfMediaOrCopy() != null) { + ExifInterface exif = new ExifInterface(getPathOfMediaOrCopy());//Temporary EXIF interface to redact data. + for (String tag : prefRedactEXIFTags) { + String oldValue = exif.getAttribute(tag); + if (oldValue != null && !oldValue.isEmpty()) { + Timber.d("Exif tag " + tag + " with value " + oldValue + " redacted."); + exif.setAttribute(tag, null); + } + } + if (prefLocationAccuracy<0) { + Timber.d("Setting EXIF coordinates to 0"); + exif.setLatLong(0d, 0d); + }else if (prefLocationAccuracy!=0){ + exif.setLatLong(anonymizeCoord(imageObj.getDecLatitude()), anonymizeCoord(imageObj.getDecLongitude())); + } + exif.saveAttributes(); + } + } catch (IOException e) { + Timber.w(e); + throw new RuntimeException("EXIF redaction failed."); + } + return Uri.parse("file://" + getPathOfMediaOrCopy()); + } + /** * Calls the async task that detects if image is fuzzy, too dark, etc */ void detectUnwantedPictures() { String imageMediaFilePath = FileUtils.getPath(context, mediaUri); DetectUnwantedPicturesAsync detectUnwantedPicturesAsync - = new DetectUnwantedPicturesAsync(new WeakReference((Activity) context), imageMediaFilePath); + = new DetectUnwantedPicturesAsync(new WeakReference<>((Activity) context), imageMediaFilePath); detectUnwantedPicturesAsync.execute(); } From 8c1430e529cfd2565dbb07b0f1858d6d15d689fa Mon Sep 17 00:00:00 2001 From: ilgazer Date: Fri, 13 Jul 2018 20:01:51 +0300 Subject: [PATCH 03/16] MultipleShareActivity: upload anonymized photo and coords *Call FileProcessor.redactEXIFFields() and pass the uri returned by redactEXIFFields() to the Contribution object instead of the Uri of the unprocessed file. *Move coordinate extraction logic from local function to FileProcessor to decrease code reuse. --- .../free/nrw/commons/upload/MultipleShareActivity.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/MultipleShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/MultipleShareActivity.java index 58d6d61ca9..e897350744 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/MultipleShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/MultipleShareActivity.java @@ -269,14 +269,18 @@ protected void onAuthCookieAcquired(String authCookie) { for (int i = 0; i < urisList.size(); i++) { Contribution up = new Contribution(); Uri uri = urisList.get(i); - up.setLocalUri(uri); + if(uri==null) + Timber.w("Uri is null."); + FileProcessor fileObj= new FileProcessor(uri, this.getContentResolver(), this); + fileObj.processFileCoordinates(false); + up.setLocalUri(fileObj.redactEXIFData()); up.setTag("mimeType", intent.getType()); up.setTag("sequence", i); up.setSource(Contribution.SOURCE_EXTERNAL); up.setMultiple(true); - String imageGpsCoordinates = extractImageGpsData(uri); + String imageGpsCoordinates=fileObj.getAnonymizedDecimalCoords(); if (imageGpsCoordinates != null) { - Timber.d("GPS data for image found!"); + Timber.d("GPS data will be used for image!"); up.setDecimalCoords(imageGpsCoordinates); } photosList.add(up); From ad312b49832f9dd61eaccb37251da0e1f6e553b1 Mon Sep 17 00:00:00 2001 From: ilgazer Date: Wed, 25 Jul 2018 09:51:43 +0300 Subject: [PATCH 04/16] preferences: add global options for anonymization --- app/src/main/res/values/arrays.xml | 33 ++++++++++++++ app/src/main/res/values/keys.xml | 5 +++ app/src/main/res/xml/preferences.xml | 65 +++++++++++++++------------- 3 files changed, 74 insertions(+), 29 deletions(-) diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 3cfe500d49..8ae840e190 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -14,4 +14,37 @@ @string/license_pref_cc_by_sa_3_0 @string/license_pref_cc_by_sa_4_0 + + + @string/exif_tag_name_author + @string/exif_tag_name_copyright + @string/exif_tag_name_model + @string/exif_tag_name_software + + + + @string/exif_tag_author + @string/exif_tag_copyright + @string/exif_tag_model + @string/exif_tag_software + + + + @string/location_accuracy_pref_remove + @string/location_accuracy_pref_20km + @string/location_accuracy_pref_10km + @string/location_accuracy_pref_5km + @string/location_accuracy_pref_1km + @string/location_accuracy_pref_100m + @string/location_accuracy_pref_do_nothing + + + -1 + 20000 + 10000 + 5000 + 1000 + 100 + 0 + \ No newline at end of file diff --git a/app/src/main/res/values/keys.xml b/app/src/main/res/values/keys.xml index a2bb6860d2..3cb9d5bb8d 100644 --- a/app/src/main/res/values/keys.xml +++ b/app/src/main/res/values/keys.xml @@ -5,4 +5,9 @@ CC BY-SA 3.0 CC BY 4.0 CC BY-SA 4.0 + + Author + Copyright + Camera Model + Software \ No newline at end of file diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 49720b2470..226d749216 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -1,67 +1,74 @@ - + - + + android:title="@string/preference_theme" /> - + + android:key="defaultLicense" + android:title="@string/preference_license" /> + android:key="useExternalStorage" + android:summary="@string/use_external_storage_summary" + android:title="@string/use_external_storage" /> + android:key="uploads" + android:maxLength="3" + android:title="@string/set_limit" /> - + + + + android:key="allowGps" + android:summary="@string/allow_gps_summary" + android:title="@string/allow_gps" /> + + - + - + android:summary="@string/become_a_tester_description" + android:title="@string/become_a_tester_title" /> + android:summary="@string/send_log_file_description" + android:title="@string/send_log_file" /> \ No newline at end of file From 35ce75a9effca2f8d4af009d648c523ed012f75e Mon Sep 17 00:00:00 2001 From: ilgazer Date: Wed, 25 Jul 2018 09:52:29 +0300 Subject: [PATCH 05/16] strings.xml:added strings --- app/src/main/res/values/strings.xml | 507 +++++++++++++++------------- 1 file changed, 263 insertions(+), 244 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1372d5866d..183e7396a4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,245 +1,247 @@ - Appearance - General - Feedback - Location - Commons - - Settings - Username - Password - Log in to your Commons Beta account - Log in - Forgot Password? - Sign up - Logging in - Please wait… - Login success! - Login failed! - File not found. Please try another file. - Authentication failed, please login again - Upload started! - %1$s uploaded! - Tap to view your upload - Starting %1$s upload - %1$s uploading - Finishing uploading %1$s - Uploading %1$s failed - Tap to view - - %1$d file uploading - %1$d files uploading - - My Recent Uploads - Queued - Failed - %1$d%% complete - Uploading - From Gallery - Take photo - Nearby - My uploads - Share - View in Browser - Title - Please provide a title for this file - Description - Unable to login - network failure - Unable to login - please check your username and password - Too many unsuccessful attempts. Please try again in a few minutes. - Sorry, this user has been blocked on Commons - You must provide your two factor authentication code. - Login failed - Upload - Name this set - Modifications - Upload - Search categories - Save - Refresh - List - GPS is disabled in your device. Would you like to enable it? - Enable GPS - No uploads yet - - - @string/contributions_subtitle_zero - %1$d upload - %1$d uploads - - - Starting %1$d upload - Starting %1$d uploads - - - %1$d upload - %1$d uploads - - No categories matching %1$s found - Add categories to make your images more discoverable on Wikimedia Commons.\nStart typing to add categories. - Categories - Settings - Sign Up - Featured Images - About - The Wikimedia Commons app is an open-source app created and maintained by grantees and volunteers of the Wikimedia community. The Wikimedia Foundation is not involved in the creation, development, or maintenance of the app. - Wikimedia Commons - Create a new <a href=\"https://github.com/commons-app/apps-android-commons/issues\">GitHub issue</a> for bug reports and suggestions. - Privacy policy]]> - Credits]]> - About - Send Feedback (via Email) - No email client installed - Recently used categories - Waiting for first sync… - You have not yet uploaded any photos. - Retry - Cancel - This image will be licensed under %1$s - By submitting this picture, I declare that this is my own work, that it does not contain copyrighted material or selfies, and otherwise adheres to <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">Wikimedia Commons policies</a>. - Download - Default License - Use previous title/description - Automatically get current location - Retrieves current location if image is not geotagged, and geotags image with it. Warning: This will reveal your current location. - Night mode - Use dark theme - Attribution-ShareAlike 4.0 - Attribution 4.0 - Attribution-ShareAlike 3.0 - Attribution 3.0 - CC0 - CC BY-SA 3.0 - CC BY-SA 3.0 (Austria) - CC BY-SA 3.0 (Germany) - CC BY-SA 3.0 (Estonia) - CC BY-SA 3.0 (Spain) - CC BY-SA 3.0 (Croatia) - CC BY-SA 3.0 (Luxembourg) - CC BY-SA 3.0 (Netherlands) - CC BY-SA 3.0 (Norway) - CC BY-SA 3.0 (Poland) - CC BY-SA 3.0 (Romania) - CC BY 3.0 - CC BY-SA 4.0 - CC BY 4.0 - CC Zero - Wikimedia Commons hosts most of the images that are used in Wikipedia. - Your images help educate people around the world! - Please upload pictures that are taken or created entirely by yourself: - Natural objects (flowers, animals, mountains)\n• Useful objects (bicycles, train stations)\n• Famous people (your mayor, Olympic athletes you met) - Natural objects (flowers, animals, mountains) - Useful objects (bicycles, train stations) - Famous people (your mayor, Olympic athletes you met) - Please do NOT upload: - - Selfies or pictures of your friends\n- Pictures you downloaded from the Internet\n- Screenshots of proprietary apps - Selfies or pictures of your friends - Pictures you downloaded from the Internet - Screenshots of proprietary apps - Example upload: - - Title: Sydney Opera House\n- Description: Sydney Opera House as viewed from across the bay\n- Categories: Sydney Opera House from the west, Sydney Opera House remote views - Title: Sydney Opera House - Description: Sydney Opera House as viewed from across the bay - Categories: Sydney Opera House from the west, Sydney Opera House remote views - Contribute your images. Help Wikipedia articles come to life! - Images on Wikipedia come from Wikimedia Commons. - Your images help educate people around the world. - Avoid copyrighted materials you found from the Internet as well as images of posters, book covers, etc. - You think you got it? - Yes! - More Information - Categories - Loading… - None selected - No description - Unknown license - Refresh - Required permission: Read external storage. App cannot access your gallery without this. - Required permission: Write external storage. App cannot access your camera without this. - Optional permission: Get current location for category suggestions - OK - Nearby Places - No nearby places found - Warning - This file already exists on Commons. Are you sure you want to proceed? - Yes - No - Title - Title of the media - Description - Description of the media goes here. This can potentially be fairly long, and will need to wrap across multiple lines. We hope it looks nice though. - Author - Featured image author user name goes here. - Uploaded date - License - Coordinates - None provided - Become a Beta Tester - Opt-in to our beta channel on Google Play and get early access to new features and bug fixes - https://play.google.com/apps/testing/fr.free.nrw.commons - mapbox://styles/mapbox/traffic-day-v2 - mapbox://styles/mapbox/traffic-night-v2 - pk.eyJ1IjoibWFza2FyYXZpdmVrIiwiYSI6ImNqMmxvdzFjMTAwMHYzM283ZWM3eW5tcDAifQ.ib5SZ9EVjwJe6GSKve0bcg - 2FA Code - My Recent Upload Limit - Maximum Limit - Unable to display more than 500 - Set Recent Upload Limit - Two factor authentication is currently not supported. - Do you really want to logout? - Commons Logo - Commons Website - Commons Facebook Page - Commons Github Source Code - Background Image - Media Image Failed - No Image Found - Upload Image - Mount Zao - Llamas - Rainbow Bridge - Tulip - No Selfies - Proprietary Image - Welcome Wikipedia - Welcome Copyright - Sydney Opera House - Cancel - Open - Close - Home - Upload - Nearby - About - Settings - Feedback - Logout - Tutorial - Notifications - Featured - Nearby places cannot be displayed without location permissions - no description found - Commons file page - Wikidata item - Wikipedia article - Error while caching pictures - A unique descriptive title for the file, which will serve as a filename. You may use plain language with spaces. Do not include the file extension - Please describe the media as much as possible: Where was it taken? What does it show? What is the context? Please describe the objects or persons. Reveal information that can not be easily guessed, for instance the time of day if it is a landscape. If the media shows something unusual, please explain what makes it unusual. - This picture is too dark, are you sure you want to upload it? Wikimedia Commons is only for pictures with encyclopedic value. - This picture is blurry, are you sure you want to upload it? Wikimedia Commons is only for pictures with encyclopedic value. - Give permission - Use external storage - Save pictures taken with the in-app camera on your device - Login to your account - Send log file - Send log file to developers via email - No web browser found to open URL - Error! URL not found - Nominate for Deletion - This image has been nominated for deletion. - See webpage for details - View in Browser + Appearance + General + Feedback + Location + Commons + + Settings + Username + Password + Log in to your Commons Beta account + Log in + Forgot Password? + Sign up + Logging in + Please wait… + Login success! + Login failed! + File not found. Please try another file. + Authentication failed, please login again + Upload started! + %1$s uploaded! + Tap to view your upload + Starting %1$s upload + %1$s uploading + Finishing uploading %1$s + Uploading %1$s failed + Tap to view + + %1$d file uploading + %1$d files uploading + + My Recent Uploads + Queued + Failed + %1$d%% complete + Uploading + From Gallery + Take photo + Nearby + My uploads + Share + View in Browser + Title + Please provide a title for this file + Description + Unable to login - network failure + Unable to login - please check your username and password + Too many unsuccessful attempts. Please try again in a few minutes. + Sorry, this user has been blocked on Commons + You must provide your two factor authentication code. + Login failed + Upload + Name this set + Modifications + Upload + Search categories + Save + Refresh + List + GPS is disabled in your device. Would you like to enable it? + Enable GPS + No uploads yet + + + @string/contributions_subtitle_zero + %1$d upload + %1$d uploads + + + Starting %1$d upload + Starting %1$d uploads + + + %1$d upload + %1$d uploads + + No categories matching %1$s found + Add categories to make your images more discoverable on Wikimedia Commons.\nStart typing to add categories. + Categories + Settings + Sign Up + Featured Images + About + The Wikimedia Commons app is an open-source app created and maintained by grantees and volunteers of the Wikimedia community. The Wikimedia Foundation is not involved in the creation, development, or maintenance of the app. + Wikimedia Commons + Create a new <a href=\"https://github.com/commons-app/apps-android-commons/issues\">GitHub issue</a> for bug reports and suggestions. + Privacy policy]]> + Credits]]> + About + Send Feedback (via Email) + No email client installed + Recently used categories + Waiting for first sync… + You have not yet uploaded any photos. + Retry + Cancel + This image will be licensed under %1$s + By submitting this picture, I declare that this is my own work, that it does not contain copyrighted material or selfies, and otherwise adheres to <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">Wikimedia Commons policies</a>. + Download + Default License + Use previous title/description + Automatically get current location + Retrieves current location if image is not geotagged, and geotags image with it. Warning: This will reveal your current location. + Redact from EXIF data + Some cameras add your real name to EXIF data. The following string will be redacted from all fields. + Night mode + Use dark theme + Attribution-ShareAlike 4.0 + Attribution 4.0 + Attribution-ShareAlike 3.0 + Attribution 3.0 + CC0 + CC BY-SA 3.0 + CC BY-SA 3.0 (Austria) + CC BY-SA 3.0 (Germany) + CC BY-SA 3.0 (Estonia) + CC BY-SA 3.0 (Spain) + CC BY-SA 3.0 (Croatia) + CC BY-SA 3.0 (Luxembourg) + CC BY-SA 3.0 (Netherlands) + CC BY-SA 3.0 (Norway) + CC BY-SA 3.0 (Poland) + CC BY-SA 3.0 (Romania) + CC BY 3.0 + CC BY-SA 4.0 + CC BY 4.0 + CC Zero + Wikimedia Commons hosts most of the images that are used in Wikipedia. + Your images help educate people around the world! + Please upload pictures that are taken or created entirely by yourself: + Natural objects (flowers, animals, mountains)\n• Useful objects (bicycles, train stations)\n• Famous people (your mayor, Olympic athletes you met) + Natural objects (flowers, animals, mountains) + Useful objects (bicycles, train stations) + Famous people (your mayor, Olympic athletes you met) + Please do NOT upload: + - Selfies or pictures of your friends\n- Pictures you downloaded from the Internet\n- Screenshots of proprietary apps + Selfies or pictures of your friends + Pictures you downloaded from the Internet + Screenshots of proprietary apps + Example upload: + - Title: Sydney Opera House\n- Description: Sydney Opera House as viewed from across the bay\n- Categories: Sydney Opera House from the west, Sydney Opera House remote views + Title: Sydney Opera House + Description: Sydney Opera House as viewed from across the bay + Categories: Sydney Opera House from the west, Sydney Opera House remote views + Contribute your images. Help Wikipedia articles come to life! + Images on Wikipedia come from Wikimedia Commons. + Your images help educate people around the world. + Avoid copyrighted materials you found from the Internet as well as images of posters, book covers, etc. + You think you got it? + Yes! + More Information + Categories + Loading… + None selected + No description + Unknown license + Refresh + Required permission: Read external storage. App cannot access your gallery without this. + Required permission: Write external storage. App cannot access your camera without this. + Optional permission: Get current location for category suggestions + OK + Nearby Places + No nearby places found + Warning + This file already exists on Commons. Are you sure you want to proceed? + Yes + No + Title + Title of the media + Description + Description of the media goes here. This can potentially be fairly long, and will need to wrap across multiple lines. We hope it looks nice though. + Author + Featured image author user name goes here. + Uploaded date + License + Coordinates + None provided + Become a Beta Tester + Opt-in to our beta channel on Google Play and get early access to new features and bug fixes + https://play.google.com/apps/testing/fr.free.nrw.commons + mapbox://styles/mapbox/traffic-day-v2 + mapbox://styles/mapbox/traffic-night-v2 + pk.eyJ1IjoibWFza2FyYXZpdmVrIiwiYSI6ImNqMmxvdzFjMTAwMHYzM283ZWM3eW5tcDAifQ.ib5SZ9EVjwJe6GSKve0bcg + 2FA Code + My Recent Upload Limit + Maximum Limit + Unable to display more than 500 + Set Recent Upload Limit + Two factor authentication is currently not supported. + Do you really want to logout? + Commons Logo + Commons Website + Commons Facebook Page + Commons Github Source Code + Background Image + Media Image Failed + No Image Found + Upload Image + Mount Zao + Llamas + Rainbow Bridge + Tulip + No Selfies + Proprietary Image + Welcome Wikipedia + Welcome Copyright + Sydney Opera House + Cancel + Open + Close + Home + Upload + Nearby + About + Settings + Feedback + Logout + Tutorial + Notifications + Featured + Nearby places cannot be displayed without location permissions + no description found + Commons file page + Wikidata item + Wikipedia article + Error while caching pictures + A unique descriptive title for the file, which will serve as a filename. You may use plain language with spaces. Do not include the file extension + Please describe the media as much as possible: Where was it taken? What does it show? What is the context? Please describe the objects or persons. Reveal information that can not be easily guessed, for instance the time of day if it is a landscape. If the media shows something unusual, please explain what makes it unusual. + This picture is too dark, are you sure you want to upload it? Wikimedia Commons is only for pictures with encyclopedic value. + This picture is blurry, are you sure you want to upload it? Wikimedia Commons is only for pictures with encyclopedic value. + Give permission + Use external storage + Save pictures taken with the in-app camera on your device + Login to your account + Send log file + Send log file to developers via email + No web browser found to open URL + Error! URL not found + Nominate for Deletion + This image has been nominated for deletion. + See webpage for details + View in Browser Location has not changed. Location not available. @@ -285,8 +287,25 @@ Pic of the Day Pic of the Day - Image successfully added to %1$s on Wikidata! - Failed to update corresponding Wikidata entity! - Set wallpaper - Wallpaper set successfully! + Image successfully added to %1$s on Wikidata! + Failed to update corresponding Wikidata entity! + Set wallpaper + Wallpaper set successfully! + Login session expired, please log in again. + Remove EXIF Tags + Select which EXIF tags to remove from uploads. + Set Location Accuracy + Remove all location data. + Reduce accuracy to 20km + Reduce accuracy to 10km + Reduce accuracy to 5km + Reduce accuracy to 1km + Reduce accuracy to 100m + Do nothing + + Author + Copyright + Camera Model + Software + From fe3b4f64a8d186b5c67c72b4eb9fcdb30d27fac7 Mon Sep 17 00:00:00 2001 From: ilgazer Date: Fri, 13 Jul 2018 21:21:17 +0300 Subject: [PATCH 06/16] ShareActivity: upload anonymized photo and coords *Call FileProcessor.redactEXIFFields() and pass the uri returned by redactEXIFFields() along with anonymized coordinates to the uploadController object instead of the Uri of the unprocessed file. --- .../java/fr/free/nrw/commons/upload/ShareActivity.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 5db101862c..18aaf424fe 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -120,6 +120,7 @@ public class ShareActivity private String mimeType; private CategorizationFragment categorizationFragment; private Uri mediaUri; + private Uri redactedMediaUri; private Contribution contribution; private GPSExtractor gpsObj; private String decimalCoords; @@ -191,7 +192,9 @@ private void uploadBegins() { Timber.d("Cache the categories found"); } - uploadController.startUpload(title, mediaUri, description, mimeType, source, decimalCoords, wikiDataEntityId, c -> { + redactedMediaUri=fileObj.redactEXIFData(); + + uploadController.startUpload(title, redactedMediaUri, description, mimeType, source, fileObj.getAnonymizedDecimalCoords(), wikiDataEntityId, c -> { ShareActivity.this.contribution = c; showPostUpload(); }); @@ -308,7 +311,6 @@ R.drawable.ic_error_outline_black_24dp, getTheme())) fileObj = new FileProcessor(mediaUri, contentResolver, this); checkIfFileExists(); gpsObj = fileObj.processFileCoordinates(locationPermitted); - decimalCoords = fileObj.getDecimalCoords(); } /** @@ -449,7 +451,7 @@ private void checkIfFileExists() { Timber.d("File SHA1 is: %s", fileSHA1); ExistingFileAsync fileAsyncTask = - new ExistingFileAsync(new WeakReference(this), fileSHA1, new WeakReference(this), result -> { + new ExistingFileAsync(new WeakReference<>(this), fileSHA1, new WeakReference<>(this), result -> { Timber.d("%s duplicate check: %s", mediaUri.toString(), result); duplicateCheckPassed = (result == DUPLICATE_PROCEED || result == NO_DUPLICATE); if (duplicateCheckPassed) { From 7c988dbd4c172f3ced1877b02dcd4318370e7ae1 Mon Sep 17 00:00:00 2001 From: ilgazer Date: Sat, 14 Jul 2018 22:45:55 +0300 Subject: [PATCH 07/16] resolved merge conflicts. --- app/build.gradle | 2 +- app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/preferences.xml | 7 ++----- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index dd1ea27752..d04618db78 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -21,7 +21,7 @@ dependencies { implementation 'com.jakewharton.timber:timber:4.5.1' implementation 'info.debatty:java-string-similarity:0.24' implementation 'com.borjabravo:readmoretextview:2.1.0' - implementation 'com.android.support.constraint:constraint-layout:1.0.2' + implementation 'com.android.support.constraint:constraint-layout:1.1.0' implementation "com.android.support:exifinterface:27.1.1" implementation('com.mapbox.mapboxsdk:mapbox-android-sdk:5.5.0@aar') { transitive = true diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 183e7396a4..d7319633c2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -291,6 +291,7 @@ Failed to update corresponding Wikidata entity! Set wallpaper Wallpaper set successfully! + Login session expired, please log in again. Remove EXIF Tags Select which EXIF tags to remove from uploads. diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 226d749216..80cf5a4ead 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -43,11 +43,8 @@ android:key="locationAccuracy" android:title="@string/location_accuracy"/> - + Date: Wed, 25 Jul 2018 09:57:53 +0300 Subject: [PATCH 08/16] Revert "strings.xml:added strings" as it had whitespace issues This reverts commit 35ce75a --- app/src/main/res/values/strings.xml | 508 +++++++++++++--------------- 1 file changed, 244 insertions(+), 264 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d7319633c2..1372d5866d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,247 +1,245 @@ - Appearance - General - Feedback - Location - Commons - - Settings - Username - Password - Log in to your Commons Beta account - Log in - Forgot Password? - Sign up - Logging in - Please wait… - Login success! - Login failed! - File not found. Please try another file. - Authentication failed, please login again - Upload started! - %1$s uploaded! - Tap to view your upload - Starting %1$s upload - %1$s uploading - Finishing uploading %1$s - Uploading %1$s failed - Tap to view - - %1$d file uploading - %1$d files uploading - - My Recent Uploads - Queued - Failed - %1$d%% complete - Uploading - From Gallery - Take photo - Nearby - My uploads - Share - View in Browser - Title - Please provide a title for this file - Description - Unable to login - network failure - Unable to login - please check your username and password - Too many unsuccessful attempts. Please try again in a few minutes. - Sorry, this user has been blocked on Commons - You must provide your two factor authentication code. - Login failed - Upload - Name this set - Modifications - Upload - Search categories - Save - Refresh - List - GPS is disabled in your device. Would you like to enable it? - Enable GPS - No uploads yet - - - @string/contributions_subtitle_zero - %1$d upload - %1$d uploads - - - Starting %1$d upload - Starting %1$d uploads - - - %1$d upload - %1$d uploads - - No categories matching %1$s found - Add categories to make your images more discoverable on Wikimedia Commons.\nStart typing to add categories. - Categories - Settings - Sign Up - Featured Images - About - The Wikimedia Commons app is an open-source app created and maintained by grantees and volunteers of the Wikimedia community. The Wikimedia Foundation is not involved in the creation, development, or maintenance of the app. - Wikimedia Commons - Create a new <a href=\"https://github.com/commons-app/apps-android-commons/issues\">GitHub issue</a> for bug reports and suggestions. - Privacy policy]]> - Credits]]> - About - Send Feedback (via Email) - No email client installed - Recently used categories - Waiting for first sync… - You have not yet uploaded any photos. - Retry - Cancel - This image will be licensed under %1$s - By submitting this picture, I declare that this is my own work, that it does not contain copyrighted material or selfies, and otherwise adheres to <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">Wikimedia Commons policies</a>. - Download - Default License - Use previous title/description - Automatically get current location - Retrieves current location if image is not geotagged, and geotags image with it. Warning: This will reveal your current location. - Redact from EXIF data - Some cameras add your real name to EXIF data. The following string will be redacted from all fields. - Night mode - Use dark theme - Attribution-ShareAlike 4.0 - Attribution 4.0 - Attribution-ShareAlike 3.0 - Attribution 3.0 - CC0 - CC BY-SA 3.0 - CC BY-SA 3.0 (Austria) - CC BY-SA 3.0 (Germany) - CC BY-SA 3.0 (Estonia) - CC BY-SA 3.0 (Spain) - CC BY-SA 3.0 (Croatia) - CC BY-SA 3.0 (Luxembourg) - CC BY-SA 3.0 (Netherlands) - CC BY-SA 3.0 (Norway) - CC BY-SA 3.0 (Poland) - CC BY-SA 3.0 (Romania) - CC BY 3.0 - CC BY-SA 4.0 - CC BY 4.0 - CC Zero - Wikimedia Commons hosts most of the images that are used in Wikipedia. - Your images help educate people around the world! - Please upload pictures that are taken or created entirely by yourself: - Natural objects (flowers, animals, mountains)\n• Useful objects (bicycles, train stations)\n• Famous people (your mayor, Olympic athletes you met) - Natural objects (flowers, animals, mountains) - Useful objects (bicycles, train stations) - Famous people (your mayor, Olympic athletes you met) - Please do NOT upload: - - Selfies or pictures of your friends\n- Pictures you downloaded from the Internet\n- Screenshots of proprietary apps - Selfies or pictures of your friends - Pictures you downloaded from the Internet - Screenshots of proprietary apps - Example upload: - - Title: Sydney Opera House\n- Description: Sydney Opera House as viewed from across the bay\n- Categories: Sydney Opera House from the west, Sydney Opera House remote views - Title: Sydney Opera House - Description: Sydney Opera House as viewed from across the bay - Categories: Sydney Opera House from the west, Sydney Opera House remote views - Contribute your images. Help Wikipedia articles come to life! - Images on Wikipedia come from Wikimedia Commons. - Your images help educate people around the world. - Avoid copyrighted materials you found from the Internet as well as images of posters, book covers, etc. - You think you got it? - Yes! - More Information - Categories - Loading… - None selected - No description - Unknown license - Refresh - Required permission: Read external storage. App cannot access your gallery without this. - Required permission: Write external storage. App cannot access your camera without this. - Optional permission: Get current location for category suggestions - OK - Nearby Places - No nearby places found - Warning - This file already exists on Commons. Are you sure you want to proceed? - Yes - No - Title - Title of the media - Description - Description of the media goes here. This can potentially be fairly long, and will need to wrap across multiple lines. We hope it looks nice though. - Author - Featured image author user name goes here. - Uploaded date - License - Coordinates - None provided - Become a Beta Tester - Opt-in to our beta channel on Google Play and get early access to new features and bug fixes - https://play.google.com/apps/testing/fr.free.nrw.commons - mapbox://styles/mapbox/traffic-day-v2 - mapbox://styles/mapbox/traffic-night-v2 - pk.eyJ1IjoibWFza2FyYXZpdmVrIiwiYSI6ImNqMmxvdzFjMTAwMHYzM283ZWM3eW5tcDAifQ.ib5SZ9EVjwJe6GSKve0bcg - 2FA Code - My Recent Upload Limit - Maximum Limit - Unable to display more than 500 - Set Recent Upload Limit - Two factor authentication is currently not supported. - Do you really want to logout? - Commons Logo - Commons Website - Commons Facebook Page - Commons Github Source Code - Background Image - Media Image Failed - No Image Found - Upload Image - Mount Zao - Llamas - Rainbow Bridge - Tulip - No Selfies - Proprietary Image - Welcome Wikipedia - Welcome Copyright - Sydney Opera House - Cancel - Open - Close - Home - Upload - Nearby - About - Settings - Feedback - Logout - Tutorial - Notifications - Featured - Nearby places cannot be displayed without location permissions - no description found - Commons file page - Wikidata item - Wikipedia article - Error while caching pictures - A unique descriptive title for the file, which will serve as a filename. You may use plain language with spaces. Do not include the file extension - Please describe the media as much as possible: Where was it taken? What does it show? What is the context? Please describe the objects or persons. Reveal information that can not be easily guessed, for instance the time of day if it is a landscape. If the media shows something unusual, please explain what makes it unusual. - This picture is too dark, are you sure you want to upload it? Wikimedia Commons is only for pictures with encyclopedic value. - This picture is blurry, are you sure you want to upload it? Wikimedia Commons is only for pictures with encyclopedic value. - Give permission - Use external storage - Save pictures taken with the in-app camera on your device - Login to your account - Send log file - Send log file to developers via email - No web browser found to open URL - Error! URL not found - Nominate for Deletion - This image has been nominated for deletion. - See webpage for details - View in Browser + Appearance + General + Feedback + Location + Commons + + Settings + Username + Password + Log in to your Commons Beta account + Log in + Forgot Password? + Sign up + Logging in + Please wait… + Login success! + Login failed! + File not found. Please try another file. + Authentication failed, please login again + Upload started! + %1$s uploaded! + Tap to view your upload + Starting %1$s upload + %1$s uploading + Finishing uploading %1$s + Uploading %1$s failed + Tap to view + + %1$d file uploading + %1$d files uploading + + My Recent Uploads + Queued + Failed + %1$d%% complete + Uploading + From Gallery + Take photo + Nearby + My uploads + Share + View in Browser + Title + Please provide a title for this file + Description + Unable to login - network failure + Unable to login - please check your username and password + Too many unsuccessful attempts. Please try again in a few minutes. + Sorry, this user has been blocked on Commons + You must provide your two factor authentication code. + Login failed + Upload + Name this set + Modifications + Upload + Search categories + Save + Refresh + List + GPS is disabled in your device. Would you like to enable it? + Enable GPS + No uploads yet + + + @string/contributions_subtitle_zero + %1$d upload + %1$d uploads + + + Starting %1$d upload + Starting %1$d uploads + + + %1$d upload + %1$d uploads + + No categories matching %1$s found + Add categories to make your images more discoverable on Wikimedia Commons.\nStart typing to add categories. + Categories + Settings + Sign Up + Featured Images + About + The Wikimedia Commons app is an open-source app created and maintained by grantees and volunteers of the Wikimedia community. The Wikimedia Foundation is not involved in the creation, development, or maintenance of the app. + Wikimedia Commons + Create a new <a href=\"https://github.com/commons-app/apps-android-commons/issues\">GitHub issue</a> for bug reports and suggestions. + Privacy policy]]> + Credits]]> + About + Send Feedback (via Email) + No email client installed + Recently used categories + Waiting for first sync… + You have not yet uploaded any photos. + Retry + Cancel + This image will be licensed under %1$s + By submitting this picture, I declare that this is my own work, that it does not contain copyrighted material or selfies, and otherwise adheres to <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">Wikimedia Commons policies</a>. + Download + Default License + Use previous title/description + Automatically get current location + Retrieves current location if image is not geotagged, and geotags image with it. Warning: This will reveal your current location. + Night mode + Use dark theme + Attribution-ShareAlike 4.0 + Attribution 4.0 + Attribution-ShareAlike 3.0 + Attribution 3.0 + CC0 + CC BY-SA 3.0 + CC BY-SA 3.0 (Austria) + CC BY-SA 3.0 (Germany) + CC BY-SA 3.0 (Estonia) + CC BY-SA 3.0 (Spain) + CC BY-SA 3.0 (Croatia) + CC BY-SA 3.0 (Luxembourg) + CC BY-SA 3.0 (Netherlands) + CC BY-SA 3.0 (Norway) + CC BY-SA 3.0 (Poland) + CC BY-SA 3.0 (Romania) + CC BY 3.0 + CC BY-SA 4.0 + CC BY 4.0 + CC Zero + Wikimedia Commons hosts most of the images that are used in Wikipedia. + Your images help educate people around the world! + Please upload pictures that are taken or created entirely by yourself: + Natural objects (flowers, animals, mountains)\n• Useful objects (bicycles, train stations)\n• Famous people (your mayor, Olympic athletes you met) + Natural objects (flowers, animals, mountains) + Useful objects (bicycles, train stations) + Famous people (your mayor, Olympic athletes you met) + Please do NOT upload: + - Selfies or pictures of your friends\n- Pictures you downloaded from the Internet\n- Screenshots of proprietary apps + Selfies or pictures of your friends + Pictures you downloaded from the Internet + Screenshots of proprietary apps + Example upload: + - Title: Sydney Opera House\n- Description: Sydney Opera House as viewed from across the bay\n- Categories: Sydney Opera House from the west, Sydney Opera House remote views + Title: Sydney Opera House + Description: Sydney Opera House as viewed from across the bay + Categories: Sydney Opera House from the west, Sydney Opera House remote views + Contribute your images. Help Wikipedia articles come to life! + Images on Wikipedia come from Wikimedia Commons. + Your images help educate people around the world. + Avoid copyrighted materials you found from the Internet as well as images of posters, book covers, etc. + You think you got it? + Yes! + More Information + Categories + Loading… + None selected + No description + Unknown license + Refresh + Required permission: Read external storage. App cannot access your gallery without this. + Required permission: Write external storage. App cannot access your camera without this. + Optional permission: Get current location for category suggestions + OK + Nearby Places + No nearby places found + Warning + This file already exists on Commons. Are you sure you want to proceed? + Yes + No + Title + Title of the media + Description + Description of the media goes here. This can potentially be fairly long, and will need to wrap across multiple lines. We hope it looks nice though. + Author + Featured image author user name goes here. + Uploaded date + License + Coordinates + None provided + Become a Beta Tester + Opt-in to our beta channel on Google Play and get early access to new features and bug fixes + https://play.google.com/apps/testing/fr.free.nrw.commons + mapbox://styles/mapbox/traffic-day-v2 + mapbox://styles/mapbox/traffic-night-v2 + pk.eyJ1IjoibWFza2FyYXZpdmVrIiwiYSI6ImNqMmxvdzFjMTAwMHYzM283ZWM3eW5tcDAifQ.ib5SZ9EVjwJe6GSKve0bcg + 2FA Code + My Recent Upload Limit + Maximum Limit + Unable to display more than 500 + Set Recent Upload Limit + Two factor authentication is currently not supported. + Do you really want to logout? + Commons Logo + Commons Website + Commons Facebook Page + Commons Github Source Code + Background Image + Media Image Failed + No Image Found + Upload Image + Mount Zao + Llamas + Rainbow Bridge + Tulip + No Selfies + Proprietary Image + Welcome Wikipedia + Welcome Copyright + Sydney Opera House + Cancel + Open + Close + Home + Upload + Nearby + About + Settings + Feedback + Logout + Tutorial + Notifications + Featured + Nearby places cannot be displayed without location permissions + no description found + Commons file page + Wikidata item + Wikipedia article + Error while caching pictures + A unique descriptive title for the file, which will serve as a filename. You may use plain language with spaces. Do not include the file extension + Please describe the media as much as possible: Where was it taken? What does it show? What is the context? Please describe the objects or persons. Reveal information that can not be easily guessed, for instance the time of day if it is a landscape. If the media shows something unusual, please explain what makes it unusual. + This picture is too dark, are you sure you want to upload it? Wikimedia Commons is only for pictures with encyclopedic value. + This picture is blurry, are you sure you want to upload it? Wikimedia Commons is only for pictures with encyclopedic value. + Give permission + Use external storage + Save pictures taken with the in-app camera on your device + Login to your account + Send log file + Send log file to developers via email + No web browser found to open URL + Error! URL not found + Nominate for Deletion + This image has been nominated for deletion. + See webpage for details + View in Browser Location has not changed. Location not available. @@ -287,26 +285,8 @@ Pic of the Day Pic of the Day - Image successfully added to %1$s on Wikidata! - Failed to update corresponding Wikidata entity! - Set wallpaper - Wallpaper set successfully! - - Login session expired, please log in again. - Remove EXIF Tags - Select which EXIF tags to remove from uploads. - Set Location Accuracy - Remove all location data. - Reduce accuracy to 20km - Reduce accuracy to 10km - Reduce accuracy to 5km - Reduce accuracy to 1km - Reduce accuracy to 100m - Do nothing - - Author - Copyright - Camera Model - Software - + Image successfully added to %1$s on Wikidata! + Failed to update corresponding Wikidata entity! + Set wallpaper + Wallpaper set successfully! From 5f8a2f125caf559b53428e64a2ab64bf1acc5e58 Mon Sep 17 00:00:00 2001 From: ilgazer Date: Wed, 25 Jul 2018 09:59:14 +0300 Subject: [PATCH 09/16] strings.xml:added strings for anonymization prefs --- app/src/main/res/values/strings.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1372d5866d..b7cc7671d2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -289,4 +289,22 @@ Failed to update corresponding Wikidata entity! Set wallpaper Wallpaper set successfully! + + Login session expired, please log in again. + Remove EXIF Tags + Select which EXIF tags to remove from uploads. + Set Location Accuracy + Remove all location data. + Reduce accuracy to 20km + Reduce accuracy to 10km + Reduce accuracy to 5km + Reduce accuracy to 1km + Reduce accuracy to 100m + Do nothing + + Author + Copyright + Camera Model + Software + From 8e420387a037a956874ba9a8cb37be5be4dece7e Mon Sep 17 00:00:00 2001 From: ilgazer Date: Wed, 25 Jul 2018 19:24:37 +0300 Subject: [PATCH 10/16] strings.xml:remove duplicate string --- app/src/main/res/values/strings.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 92b45dab38..4a1b90247c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -305,7 +305,6 @@ Set wallpaper Wallpaper set successfully! - Login session expired, please log in again. Remove EXIF Tags Select which EXIF tags to remove from uploads. Set Location Accuracy From 78f5e9fd065219521fdcc82e1b8df73c626e3927 Mon Sep 17 00:00:00 2001 From: ilgazer Date: Wed, 25 Jul 2018 20:22:37 +0300 Subject: [PATCH 11/16] preferences.xml:fixed merge errors --- app/src/main/res/xml/preferences.xml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 9c32d00409..87eb25db5a 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -8,8 +8,7 @@ android:title="@string/preference_theme" android:defaultValue="false" android:key="theme" - android:summary="@string/preference_theme_summary" - android:title="@string/preference_theme" /> + android:summary="@string/preference_theme_summary" /> @@ -21,20 +20,18 @@ android:title="@string/preference_license" android:entries="@array/pref_defaultLicense_entries" android:entryValues="@array/pref_defaultLicense_values" - android:key="defaultLicense" /> + android:defaultValue="@string/license_pref_cc_by_sa_4_0" /> From 40b02a64c682ea2dd5e4aaa1d8408d4bbe603118 Mon Sep 17 00:00:00 2001 From: ilgazer Date: Sat, 28 Jul 2018 13:44:27 +0300 Subject: [PATCH 12/16] strings.xml:fixed merge errors --- app/src/main/res/values/strings.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3c5e1197c8..52581a91bb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -321,11 +321,6 @@ Camera Model Software - - Are you sure you want to clear your search history? - Search history deleted - Login session expired, please log in again. - Quiz Is this picture OK to upload? Question From 86e43d7609753cc89174f7815a6a39f9e90958d6 Mon Sep 17 00:00:00 2001 From: ilgazer Date: Tue, 31 Jul 2018 16:10:00 +0300 Subject: [PATCH 13/16] prefs: Migrate new preferences to LongTitle versions. Create LongTitleMultiSelectListPreference to be used where MultiSelectListPreference was previously used. --- .../LongTitleMultiSelectListPreference.java | 30 +++++++++++++++++++ app/src/main/res/xml/preferences.xml | 8 ++--- 2 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/ui/LongTitlePreferences/LongTitleMultiSelectListPreference.java diff --git a/app/src/main/java/fr/free/nrw/commons/ui/LongTitlePreferences/LongTitleMultiSelectListPreference.java b/app/src/main/java/fr/free/nrw/commons/ui/LongTitlePreferences/LongTitleMultiSelectListPreference.java new file mode 100644 index 0000000000..07bec5223b --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/ui/LongTitlePreferences/LongTitleMultiSelectListPreference.java @@ -0,0 +1,30 @@ +package fr.free.nrw.commons.ui.LongTitlePreferences; + +import android.content.Context; +import android.preference.MultiSelectListPreference; +import android.util.AttributeSet; +import android.view.View; +import android.widget.TextView; + +/** + * Created by Ilgaz Er on 7/31/2018. + */ +public class LongTitleMultiSelectListPreference extends MultiSelectListPreference { + public LongTitleMultiSelectListPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LongTitleMultiSelectListPreference(Context context) { + super(context); + } + + @Override + protected void onBindView(View view) { + super.onBindView(view); + + TextView title = view.findViewById(android.R.id.title); + if (title != null) { + title.setSingleLine(false); + } + } +} diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 87eb25db5a..01c4b8330a 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -37,9 +37,9 @@ - + - - - + From eaed89d18e2799452d274f43d74f14426429a812 Mon Sep 17 00:00:00 2001 From: ilgazer Date: Wed, 5 Sep 2018 00:52:48 +0300 Subject: [PATCH 14/16] Implement wholesale XMP Data removal. Implement better system for matching preference items with EXIF tags. --- .../main/java/fr/free/nrw/commons/Utils.java | 25 +++++ .../nrw/commons/upload/FileMetadataUtils.java | 102 ++++++++++++++++++ .../nrw/commons/upload/FileProcessor.java | 92 +++++++++------- app/src/main/res/values/arrays.xml | 13 +++ app/src/main/res/values/keys.xml | 2 + app/src/main/res/values/strings.xml | 10 +- app/src/main/res/xml/preferences.xml | 14 ++- 7 files changed, 214 insertions(+), 44 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/upload/FileMetadataUtils.java diff --git a/app/src/main/java/fr/free/nrw/commons/Utils.java b/app/src/main/java/fr/free/nrw/commons/Utils.java index 4e4b46b01f..c0ed45d917 100644 --- a/app/src/main/java/fr/free/nrw/commons/Utils.java +++ b/app/src/main/java/fr/free/nrw/commons/Utils.java @@ -19,7 +19,10 @@ import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.util.Arrays; +import java.util.HashSet; import java.util.Locale; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -215,4 +218,26 @@ public static Bitmap getScreenShot(View view) { return bitmap; } + + public static Set toSet(T... params){ + return new HashSet<>(Arrays.asList(params)); + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + * @param reference an object reference + * @param errorMessage the exception message to use if the check fails; will be converted to a + * string using {@link String#valueOf(Object)} + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ + public static T checkNotNull(T reference, Object errorMessage) { + if (reference == null) { + throw new NullPointerException(String.valueOf(errorMessage)); + } + return reference; + } + + } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/FileMetadataUtils.java b/app/src/main/java/fr/free/nrw/commons/upload/FileMetadataUtils.java new file mode 100644 index 0000000000..3fbe2a9309 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/upload/FileMetadataUtils.java @@ -0,0 +1,102 @@ +package fr.free.nrw.commons.upload; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import io.reactivex.Observable; +import timber.log.Timber; + +import static android.support.media.ExifInterface.*; +import static fr.free.nrw.commons.Utils.checkNotNull; + +public class FileMetadataUtils { + + + public static Observable getTagsFromPref(String pref) { + Timber.d("Retuning tags for pref:" + pref); + switch (pref) { + case "Author": + return Observable.fromArray(TAG_ARTIST, TAG_CAMARA_OWNER_NAME); + case "Copyright": + return Observable.fromArray(TAG_COPYRIGHT); + case "Camera Model": + return Observable.fromArray(TAG_MAKE, TAG_MODEL); + case "Lens Model": + return Observable.fromArray(TAG_LENS_MAKE, TAG_LENS_MODEL, TAG_LENS_SPECIFICATION); + case "Serial Numbers": + return Observable.fromArray(TAG_BODY_SERIAL_NUMBER, TAG_LENS_SERIAL_NUMBER); + case "Software": + return Observable.fromArray(TAG_SOFTWARE); + default: + return null; + } + } + + + /** + * Removes all XMP data from the input file and writes the rest of the image to a new file. + * + * This works by black magic. Pleae read the JPEG section of the XMP Spesification Part 3 before making changes. + * https://wwwimages2.adobe.com/content/dam/acom/en/devnet/xmp/pdfs/XMP%20SDK%20Release%20cc-2016-08/XMPSpecificationPart3.pdf + * + * @param inputPath the path of the input file + * @param outputPath the path of the new file + */ + public static void removeXmpAndWriteToFile(String inputPath, String outputPath) { + try (BufferedInputStream is = new BufferedInputStream(new FileInputStream(inputPath)); + BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(outputPath))) { + int next = 0; + while (next != -1) { + next = is.read(); + //Detect first byte of FF E1, the code of APP1 marker + if (next == 0xFF) { + next = is.read(); + if (next == 0xE1) { + Timber.i("Found FF E1"); + //2 bytes that contain the length of the APP1 section. + byte Lp1 = (byte) is.read(); + byte Lp2 = (byte) is.read(); + Timber.i(Integer.toHexString(Lp1)); + Timber.i(Integer.toHexString(Lp2)); + //The identifier of the APP1 section, we find out if this section contains XMP data or not. + byte[] namespace = new byte[28]; + if (is.read(namespace, 0, 28) != 28) + throw new IOException("Wrong amount of bytes read."); + Timber.i(new String(namespace, "UTF-8")); + if (new String(namespace, "UTF-8").equals("http://ns.adobe.com/xap/1.0/")) { + Timber.i("Found XMP marker"); + while (next != 0xFF) { + if (next == -1) + throw new IOException("Unexpected end of file."); + next = is.read(); + } + //FF means the start of the next marker. + // This means the XMP section is finished and that we should resume copying. + outputStream.write(0xFF); + } else { + //Write everything back to the output file as we want to leave non-XMP APP1 sections as-is. + Timber.i("Not XMP marker"); + outputStream.write(0xFF); + outputStream.write(0xE1); + outputStream.write(Lp1); + outputStream.write(Lp2); + outputStream.write(namespace); + } + } else { + outputStream.write(0xFF); + outputStream.write(next); + } + } else { + outputStream.write(next); + } + } + } catch (IOException e) { + Timber.e(e); + } + } + +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java b/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java index 4336f8b4bf..00e920fb71 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java @@ -1,11 +1,11 @@ package fr.free.nrw.commons.upload; + import android.annotation.SuppressLint; import android.app.Activity; import android.content.ContentResolver; import android.content.Context; import android.content.SharedPreferences; -import android.location.Location; import android.support.media.ExifInterface; import android.net.Uri; import android.os.Build; @@ -27,9 +27,12 @@ import javax.inject.Inject; import javax.inject.Named; +import fr.free.nrw.commons.R; +import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.caching.CacheController; import fr.free.nrw.commons.di.ApplicationlessInjection; import fr.free.nrw.commons.mwapi.CategoryApi; +import io.reactivex.Observable; import io.reactivex.schedulers.Schedulers; import timber.log.Timber; @@ -56,24 +59,25 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse { private String decimalCoords; private boolean haveCheckedForOtherImages = false; private String filePath; - private String fileOrCopyPath=null; + private String fileOrCopyPath = null; private boolean prefUseExtStorage; private boolean cacheFound; private GPSExtractor tempImageObj; - private Set prefRedactEXIFTags; + private Set prefManageEXIFTags; private double prefLocationAccuracy; - + private final boolean prefKeepXmp; FileProcessor(Uri mediaUri, ContentResolver contentResolver, Context context) { this.mediaUri = mediaUri; - Timber.d("mediaUri:"+ (this.mediaUri != null ? this.mediaUri.getPath() : "null")); + Timber.d("mediaUri:" + (this.mediaUri != null ? this.mediaUri.getPath() : "null")); this.contentResolver = contentResolver; this.context = context; ApplicationlessInjection.getInstance(context.getApplicationContext()).getCommonsApplicationComponent().inject(this); prefUseExtStorage = prefs.getBoolean("useExternalStorage", true); - prefRedactEXIFTags = prefs.getStringSet("redactExifTags", Collections.emptySet() ); - prefLocationAccuracy = Double.valueOf(prefs.getString("locationAccuracy", "0"))/111300; //about 111300 meters in one degree - Timber.d("prefLocationAccuracy:"+prefLocationAccuracy); + prefManageEXIFTags = prefs.getStringSet("manageExifTags", Collections.emptySet()); + prefLocationAccuracy = Double.valueOf(prefs.getString("locationAccuracy", "0")) / 111300; //about 111300 meters in one degree + prefKeepXmp = prefs.getBoolean("keepXmp", false); + Timber.d("prefLocationAccuracy:" + prefLocationAccuracy); } /** @@ -85,11 +89,11 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse { */ @Nullable private String getPathOfMediaOrCopy() { - if (fileOrCopyPath!=null) + if (fileOrCopyPath != null) return fileOrCopyPath; filePath = FileUtils.getPath(context, mediaUri); Timber.d("Filepath: " + filePath); - if (filePath == null || !prefRedactEXIFTags.isEmpty()) { + if (filePath == null || !prefManageEXIFTags.isEmpty()) { try { ParcelFileDescriptor descriptor = contentResolver.openFileDescriptor(mediaUri, "r"); if (descriptor != null) { @@ -107,7 +111,7 @@ private String getPathOfMediaOrCopy() { return null; } } - fileOrCopyPath=filePath; + fileOrCopyPath = filePath; return filePath; } @@ -159,9 +163,9 @@ String getDecimalCoords() { * @param input * @return The coordinate with reduced accuracy. */ - double anonymizeCoord(double input){ - double intermediate=Math.round(input/prefLocationAccuracy)*prefLocationAccuracy; - return Math.round(intermediate*100000.0)/100000.0; //Round to 5th decimal place. + double anonymizeCoord(double input) { + double intermediate = Math.round(input / prefLocationAccuracy) * prefLocationAccuracy; + return Math.round(intermediate * 100000.0) / 100000.0; //Round to 5th decimal place. } /** @@ -169,21 +173,20 @@ String getDecimalCoords() { * * @return The coordinates with reduced accuracy in "lat|long" format */ - String getAnonymizedDecimalCoords(){ - Timber.d("Anonymizing coords with setting:"+prefLocationAccuracy); - if(prefLocationAccuracy<0) + String getAnonymizedDecimalCoords() { + Timber.d("Anonymizing coords with setting:" + prefLocationAccuracy); + if (prefLocationAccuracy < 0) return null; - else if (prefLocationAccuracy==0) + else if (prefLocationAccuracy == 0) return decimalCoords; - else{ - return String.valueOf(anonymizeCoord(imageObj.getDecLatitude())) + "|" + else { + return String.valueOf(anonymizeCoord(imageObj.getDecLatitude())) + "|" + String.valueOf(anonymizeCoord(imageObj.getDecLongitude())); } } /** * Find other images around the same location that were taken within the last 20 sec - * */ private void findOtherImages() { Timber.d("filePath" + getPathOfMediaOrCopy()); @@ -285,36 +288,49 @@ boolean isCacheFound() { /** - *Redacts EXIF data from the file. - * + * Redacts EXIF data from the file. + * * @return Uri of the new file. - **/ + **/ + @SuppressLint("CheckResult") public Uri redactEXIFData() { + String newFilePath = getPathOfMediaOrCopy(); try { - Timber.d("Tags to be redacted:"+ Arrays.toString(prefRedactEXIFTags.toArray())); - Timber.v("File path:"+getPathOfMediaOrCopy()); - if (!prefRedactEXIFTags.isEmpty() && getPathOfMediaOrCopy() != null) { - ExifInterface exif = new ExifInterface(getPathOfMediaOrCopy());//Temporary EXIF interface to redact data. - for (String tag : prefRedactEXIFTags) { - String oldValue = exif.getAttribute(tag); - if (oldValue != null && !oldValue.isEmpty()) { - Timber.d("Exif tag " + tag + " with value " + oldValue + " redacted."); - exif.setAttribute(tag, null); - } + Timber.d("Tags to be redacted:" + Arrays.toString(prefManageEXIFTags.toArray())); + Timber.v("File path:" + getPathOfMediaOrCopy()); + if (getPathOfMediaOrCopy() != null) { + if (!prefKeepXmp) { + newFilePath = context.getCacheDir().getAbsolutePath() + "/" + new Date().getTime() + ".jpg"; + FileMetadataUtils.removeXmpAndWriteToFile(getPathOfMediaOrCopy(), newFilePath); } - if (prefLocationAccuracy<0) { + ExifInterface exif = new ExifInterface(newFilePath);//Temporary EXIF interface to redact data. + Set redactTags = Utils.toSet(context.getResources().getStringArray(R.array.pref_exifTag_values)); + Timber.d(redactTags.toString()); + redactTags.removeAll(prefManageEXIFTags); + Observable.fromIterable(redactTags) + .flatMap(FileMetadataUtils::getTagsFromPref) + .forEach(tag -> { + Timber.d("Checking for tag:" + tag); + String oldValue = exif.getAttribute(tag); + if (oldValue != null && !oldValue.isEmpty()) { + Timber.d("Exif tag " + tag + " with value " + oldValue + " redacted."); + exif.setAttribute(tag, null); + } + }); + + if (prefLocationAccuracy < 0) { Timber.d("Setting EXIF coordinates to 0"); exif.setLatLong(0d, 0d); - }else if (prefLocationAccuracy!=0){ + } else if (prefLocationAccuracy != 0) { exif.setLatLong(anonymizeCoord(imageObj.getDecLatitude()), anonymizeCoord(imageObj.getDecLongitude())); } exif.saveAttributes(); } } catch (IOException e) { Timber.w(e); - throw new RuntimeException("EXIF redaction failed."); + throw new RuntimeException("EXIF redaction failed."); } - return Uri.parse("file://" + getPathOfMediaOrCopy()); + return Uri.parse("file://" + newFilePath); } /** diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 8ae840e190..2fff08dfd3 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -19,6 +19,8 @@ @string/exif_tag_name_author @string/exif_tag_name_copyright @string/exif_tag_name_model + @string/exif_tag_name_lens_model + @string/exif_tag_name_serial @string/exif_tag_name_software @@ -26,9 +28,20 @@ @string/exif_tag_author @string/exif_tag_copyright @string/exif_tag_model + @string/exif_tag_lens_model + @string/exif_tag_serial @string/exif_tag_software + + @string/exif_tag_author + @string/exif_tag_copyright + @string/exif_tag_model + @string/exif_tag_lens_model + @string/exif_tag_software + + + @string/location_accuracy_pref_remove @string/location_accuracy_pref_20km diff --git a/app/src/main/res/values/keys.xml b/app/src/main/res/values/keys.xml index 3cb9d5bb8d..c47b90d591 100644 --- a/app/src/main/res/values/keys.xml +++ b/app/src/main/res/values/keys.xml @@ -9,5 +9,7 @@ Author Copyright Camera Model + Lens Model + Serial Numbers Software \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8b94767521..be535f5313 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -310,8 +310,8 @@ Set as wallpaper Wallpaper set successfully! - Remove EXIF Tags - Select which EXIF tags to remove from uploads. + Manage EXIF Tags + Select which EXIF tags to include in uploads. Set Location Accuracy Remove all location data. Reduce accuracy to 20km @@ -324,7 +324,9 @@ Author Copyright Camera Model - Software + Lens Model + Serial Numbers + Software Quiz Is this picture OK to upload? @@ -368,5 +370,7 @@ The number of images you have uploaded to Commons, via any upload software The percentage of images you have uploaded to Commons that were not deleted The number of images you have uploaded to Commons that were used in Wikimedia articles + Keep XMP Metadata + XMP is another form of image metadata storage, but it is currently not possible to remove individual XMP tags from the image. You are encouraged to disable this option if anonymity is a concern. Commons Notification diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 01c4b8330a..1428509bc8 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -51,9 +51,17 @@ + android:defaultValue="@array/pref_exifTag_default_values" + android:key="manageExifTags" + android:title="@string/manage_exif_tags" + android:summary="@string/manage_exif_tags_summary"/> + + + From eff6b9524dd8777178c4ebf1083bd96c3040637d Mon Sep 17 00:00:00 2001 From: ilgazer Date: Wed, 5 Sep 2018 19:11:21 +0300 Subject: [PATCH 15/16] SettingsActivityTest: Implemented support for preferences of type Set --- .../java/fr/free/nrw/commons/SettingsActivityTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/androidTest/java/fr/free/nrw/commons/SettingsActivityTest.java b/app/src/androidTest/java/fr/free/nrw/commons/SettingsActivityTest.java index 80caf0010b..ebdc84b152 100644 --- a/app/src/androidTest/java/fr/free/nrw/commons/SettingsActivityTest.java +++ b/app/src/androidTest/java/fr/free/nrw/commons/SettingsActivityTest.java @@ -17,6 +17,7 @@ import org.junit.runner.RunWith; import java.util.Map; +import java.util.Set; import fr.free.nrw.commons.settings.SettingsActivity; @@ -51,6 +52,8 @@ protected void afterActivityFinished() { editor.putBoolean(key, (Boolean)val); } else if (val instanceof Integer) { editor.putInt(key, (Integer)val); + } else if (val instanceof Set){ + editor.putStringSet(key, (Set)val); } else { throw new RuntimeException("type not implemented: " + entry); } From bc0157bef0060c06f0ac85520f4dd227433756b6 Mon Sep 17 00:00:00 2001 From: ilgazer Date: Sun, 16 Sep 2018 09:04:42 +0300 Subject: [PATCH 16/16] Added default value for location preference. --- app/src/main/res/xml/preferences.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 1428509bc8..a72624dea0 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -43,7 +43,8 @@ android:entries="@array/pref_locationAccuracy_entries" android:entryValues="@array/pref_locationAccuracy_values" android:key="locationAccuracy" - android:title="@string/location_accuracy"/> + android:title="@string/location_accuracy" + android:defaultValue="@string/location_accuracy_pref_do_nothing"/>