diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bce7c0642..882e1fb13 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -125,6 +125,16 @@ android:resource="@xml/modifications_sync_adapter" /> + + + + { + interface Callback { + void onResult(Result result); + } + public enum Result { + NO_DUPLICATE, + DUPLICATE_PROCEED, + DUPLICATE_CANCELLED + } + private final String fileSHA1; + private final Context context; + private final Callback callback; - private String fileSHA1; - private Context context; - - public ExistingFileAsync(String fileSHA1, Context context) { + public ExistingFileAsync(String fileSHA1, Context context, Callback callback) { this.fileSHA1 = fileSHA1; this.context = context; + this.callback = callback; } @Override @@ -79,17 +88,20 @@ public class ExistingFileAsync extends AsyncTask { //Go back to ContributionsActivity Intent intent = new Intent(context, ContributionsActivity.class); context.startActivity(intent); + callback.onResult(Result.DUPLICATE_CANCELLED); } }); builder.setNegativeButton(R.string.yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { - //No need to do anything, user remains on upload screen + callback.onResult(Result.DUPLICATE_PROCEED); } }); AlertDialog dialog = builder.create(); dialog.show(); + } else { + callback.onResult(Result.NO_DUPLICATE); } } } \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java b/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java index 206e44f81..c9bed9741 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java @@ -9,6 +9,16 @@ import android.os.Build; import android.os.Environment; import android.provider.DocumentsContract; import android.provider.MediaStore; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; + +import timber.log.Timber; public class FileUtils { @@ -23,6 +33,7 @@ public class FileUtils { */ // Can be safely suppressed, checks for isKitKat before running isDocumentUri @SuppressLint("NewApi") + @Nullable public static String getPath(Context context, Uri uri) { final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; @@ -93,6 +104,7 @@ public class FileUtils { * @param selectionArgs (Optional) Selection arguments used in the query. * @return The value of the _data column, which is typically a file path. */ + @Nullable public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { @@ -108,6 +120,8 @@ public class FileUtils { final int column_index = cursor.getColumnIndexOrThrow(column); return cursor.getString(column_index); } + } catch (IllegalArgumentException e) { + Timber.d(e); } finally { if (cursor != null) cursor.close(); @@ -119,7 +133,7 @@ public class FileUtils { * @param uri The Uri to check. * @return Whether the Uri authority is ExternalStorageProvider. */ - public static boolean isExternalStorageDocument(Uri uri) { + private static boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri.getAuthority()); } @@ -127,7 +141,7 @@ public class FileUtils { * @param uri The Uri to check. * @return Whether the Uri authority is DownloadsProvider. */ - public static boolean isDownloadsDocument(Uri uri) { + private static boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri.getAuthority()); } @@ -135,7 +149,37 @@ public class FileUtils { * @param uri The Uri to check. * @return Whether the Uri authority is MediaProvider. */ - public static boolean isMediaDocument(Uri uri) { + private static boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri.getAuthority()); } + + /** + * Check if the URI is owned by the current app. + */ + public static boolean isSelfOwned(Context context, Uri uri) { + return uri.getAuthority().equals(context.getPackageName() + ".provider"); + } + + /** + * Copy content from source file to destination file. + * @param source stream copied from + * @param destination stream copied to + * @throws IOException thrown when failing to read source or opening destination file + */ + public static void copy(@NonNull FileInputStream source, @NonNull FileOutputStream destination) throws IOException { + FileChannel source_ = source.getChannel(); + FileChannel dest_ = destination.getChannel(); + source_.transferTo(0, source_.size(), dest_); + } + + /** + * Copy content from source file to destination file. + * @param source file descriptor copied from + * @param destination file path copied to + * @throws IOException thrown when failing to read source or opening destination file + */ + public static void copy(@NonNull FileDescriptor source, @NonNull String destination) throws IOException { + copy(new FileInputStream(source), new FileOutputStream(destination)); + } + } \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/upload/GPSExtractor.java b/app/src/main/java/fr/free/nrw/commons/upload/GPSExtractor.java index ab006bd94..bf6675caa 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/GPSExtractor.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/GPSExtractor.java @@ -7,10 +7,14 @@ import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.media.ExifInterface; +import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; +import java.io.FileDescriptor; import java.io.IOException; import fr.free.nrw.commons.CommonsApplication; @@ -23,17 +27,38 @@ import timber.log.Timber; */ public class GPSExtractor { - private String filePath; - private double decLatitude, decLongitude; + private ExifInterface exif; + private double decLatitude; + private double decLongitude; private Double currentLatitude = null; private Double currentLongitude = null; public boolean imageCoordsExists; private MyLocationListener myLocationListener; private LocationManager locationManager; + /** + * Construct from the file descriptor of the image (only for API 24 or newer). + * @param fileDescriptor the file descriptor of the image + */ + @RequiresApi(24) + public GPSExtractor(@NonNull FileDescriptor fileDescriptor) { + try { + exif = new ExifInterface(fileDescriptor); + } catch (IOException | IllegalArgumentException e) { + Timber.w(e); + } + } - public GPSExtractor(String filePath) { - this.filePath = filePath; + /** + * Construct from the file path of the image. + * @param path file path of the image + */ + public GPSExtractor(@NonNull String path) { + try { + exif = new ExifInterface(path); + } catch (IOException | IllegalArgumentException e) { + Timber.w(e); + } } /** @@ -86,45 +111,37 @@ public class GPSExtractor { */ @Nullable public String getCoords(boolean useGPS) { - - ExifInterface exif; String latitude = ""; String longitude = ""; String latitude_ref = ""; String longitude_ref = ""; String decimalCoords = ""; - try { - exif = new ExifInterface(filePath); - } catch (IOException e) { - Timber.w(e); - return null; - } catch (IllegalArgumentException e) { - Timber.w(e); - return null; - } - //If image has no EXIF data and user has enabled GPS setting, get user's location - if (exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE) == null && useGPS) { - registerLocationManager(); + if (exif == null || exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE) == null) { + if (useGPS) { + registerLocationManager(); - imageCoordsExists = false; - Timber.d("EXIF data has no location info"); + imageCoordsExists = false; + Timber.d("EXIF data has no location info"); - //Check what user's preference is for automatic location detection - boolean gpsPrefEnabled = gpsPreferenceEnabled(); + //Check what user's preference is for automatic location detection + boolean gpsPrefEnabled = gpsPreferenceEnabled(); - //Check that currentLatitude and currentLongitude have been explicitly set by MyLocationListener and do not default to (0.0,0.0) - if (gpsPrefEnabled && currentLatitude != null && currentLongitude != null) { - Timber.d("Current location values: Lat = %f Long = %f", - currentLatitude, currentLongitude); - return String.valueOf(currentLatitude) + "|" + String.valueOf(currentLongitude); + //Check that currentLatitude and currentLongitude have been + // explicitly set by MyLocationListener + // and do not default to (0.0,0.0) + if (gpsPrefEnabled && currentLatitude != null && currentLongitude != null) { + Timber.d("Current location values: Lat = %f Long = %f", + currentLatitude, currentLongitude); + return String.valueOf(currentLatitude) + "|" + String.valueOf(currentLongitude); + } else { + // No coords found + return null; + } } else { - // No coords found return null; } - } else if (exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE) == null) { - return null; } else { //If image has EXIF data, extract image coords imageCoordsExists = true; 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 5d9ec5fa1..bc75fe7be 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 @@ -8,6 +8,9 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; +import android.os.ParcelFileDescriptor; +import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; import android.support.design.widget.Snackbar; import android.support.graphics.drawable.VectorDrawableCompat; import android.support.v4.app.ActivityCompat; @@ -20,9 +23,11 @@ import android.widget.Toast; import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder; import com.facebook.drawee.view.SimpleDraweeView; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.Date; import java.util.List; import butterknife.ButterKnife; @@ -48,13 +53,16 @@ public class ShareActivity implements SingleUploadFragment.OnUploadActionInitiated, CategorizationFragment.OnCategoriesSaveHandler { + private static final int REQUEST_PERM_ON_CREATE_STORAGE = 1; + private static final int REQUEST_PERM_ON_CREATE_LOCATION = 2; + private static final int REQUEST_PERM_ON_CREATE_STORAGE_AND_LOCATION = 3; + private static final int REQUEST_PERM_ON_SUBMIT_STORAGE = 4; private CategorizationFragment categorizationFragment; private CommonsApplication app; private String source; private String mimeType; - private String mediaUriString; private Uri mediaUri; private Contribution contribution; @@ -68,15 +76,16 @@ public class ShareActivity private String decimalCoords; private boolean useNewPermissions = false; - private boolean storagePermission = false; - private boolean locationPermission = false; + private boolean storagePermitted = false; + private boolean locationPermitted = false; private String title; private String description; private Snackbar snackbar; + private boolean duplicateCheckPassed = false; /** - * Called when user taps the submit button + * Called when user taps the submit button. */ @Override public void uploadActionInitiated(String title, String description) { @@ -85,10 +94,11 @@ public class ShareActivity this.description = description; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - //Check for Storage permission that is required for upload. Do not allow user to proceed without permission, otherwise will crash - if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - //See http://stackoverflow.com/questions/33169455/onrequestpermissionsresult-not-being-called-in-dialog-fragment - requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 4); + // Check for Storage permission that is required for upload. + // Do not allow user to proceed without permission, otherwise will crash + if (needsToRequestStoragePermission()) { + requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, + REQUEST_PERM_ON_SUBMIT_STORAGE); } else { uploadBegins(); } @@ -97,12 +107,18 @@ public class ShareActivity } } + @RequiresApi(16) + private boolean needsToRequestStoragePermission() { + // We need to ask storage permission when + // the file is not owned by this app, (e.g. shared from the Gallery) + // and permission is not obtained. + return !FileUtils.isSelfOwned(getApplicationContext(), mediaUri) + && (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) + != PackageManager.PERMISSION_GRANTED); + } + private void uploadBegins() { - if (locationPermission) { - getFileMetadata(true); - } else { - getFileMetadata(false); - } + getFileMetadata(locationPermitted); Toast startingToast = Toast.makeText( CommonsApplication.getInstance(), @@ -232,9 +248,9 @@ public class ShareActivity //Receive intent from ContributionController.java when user selects picture to upload Intent intent = getIntent(); - if(intent.getAction().equals(Intent.ACTION_SEND)) { + if (intent.getAction().equals(Intent.ACTION_SEND)) { mediaUri = intent.getParcelableExtra(Intent.EXTRA_STREAM); - if(intent.hasExtra(UploadService.EXTRA_SOURCE)) { + if (intent.hasExtra(UploadService.EXTRA_SOURCE)) { source = intent.getStringExtra(UploadService.EXTRA_SOURCE); } else { source = Contribution.SOURCE_EXTERNAL; @@ -243,129 +259,101 @@ public class ShareActivity } if (mediaUri != null) { - mediaUriString = mediaUri.toString(); - backgroundImageView.setImageURI(mediaUriString); - - //Test SHA1 of image to see if it matches SHA1 of a file on Commons - try { - InputStream inputStream = getContentResolver().openInputStream(mediaUri); - Timber.d("Input stream created from %s", mediaUriString); - String fileSHA1 = Utils.getSHA1(inputStream); - Timber.d("File SHA1 is: %s", fileSHA1); - - ExistingFileAsync fileAsyncTask = new ExistingFileAsync(fileSHA1, this); - fileAsyncTask.execute(); - - } catch (IOException e) { - Timber.d(e, "IO Exception: "); - } + backgroundImageView.setImageURI(mediaUri); } - if(savedInstanceState != null) { + if (savedInstanceState != null) { contribution = savedInstanceState.getParcelable("contribution"); } requestAuthToken(); - Timber.d("Uri: %s", mediaUriString); + Timber.d("Uri: %s", mediaUri.toString()); Timber.d("Ext storage dir: %s", Environment.getExternalStorageDirectory()); + useNewPermissions = false; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { useNewPermissions = true; - if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { - storagePermission = true; + + if (!needsToRequestStoragePermission()) { + storagePermitted = true; } if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { - locationPermission = true; + locationPermitted = true; } } // Check storage permissions if marshmallow or newer - if (useNewPermissions && (!storagePermission || !locationPermission)) { - if (!storagePermission && !locationPermission) { - String permissionRationales = getResources().getString(R.string.storage_permission_rationale) + "\n" + getResources().getString(R.string.location_permission_rationale); - snackbar = Snackbar.make(findViewById(android.R.id.content), permissionRationales, - Snackbar.LENGTH_INDEFINITE) - .setAction(R.string.ok, new View.OnClickListener() { - @Override - public void onClick(View view) { - ActivityCompat.requestPermissions(ShareActivity.this, - new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION}, 3); - } - }); - snackbar.show(); + if (useNewPermissions && (!storagePermitted || !locationPermitted)) { + if (!storagePermitted && !locationPermitted) { + String permissionRationales = + getResources().getString(R.string.storage_permission_rationale) + "\n" + + getResources().getString(R.string.location_permission_rationale); + snackbar = requestPermissionUsingSnackBar( + permissionRationales, + new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION}, + REQUEST_PERM_ON_CREATE_STORAGE_AND_LOCATION); View snackbarView = snackbar.getView(); TextView textView = (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text); textView.setMaxLines(3); - } else if (!storagePermission) { - Snackbar.make(findViewById(android.R.id.content), R.string.storage_permission_rationale, - Snackbar.LENGTH_INDEFINITE) - .setAction(R.string.ok, new View.OnClickListener() { - @Override - public void onClick(View view) { - ActivityCompat.requestPermissions(ShareActivity.this, - new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1); - } - }).show(); - } else if (!locationPermission) { - Snackbar.make(findViewById(android.R.id.content), R.string.location_permission_rationale, - Snackbar.LENGTH_INDEFINITE) - .setAction(R.string.ok, new View.OnClickListener() { - @Override - public void onClick(View view) { - ActivityCompat.requestPermissions(ShareActivity.this, - new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 2); - } - }).show(); + } else if (!storagePermitted) { + requestPermissionUsingSnackBar( + getString(R.string.storage_permission_rationale), + new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, + REQUEST_PERM_ON_CREATE_STORAGE); + } else if (!locationPermitted) { + requestPermissionUsingSnackBar( + getString(R.string.location_permission_rationale), + new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, + REQUEST_PERM_ON_CREATE_LOCATION); } - } else if (useNewPermissions && storagePermission && !locationPermission) { - getFileMetadata(true); - } else if(!useNewPermissions || (storagePermission && locationPermission)) { - getFileMetadata(true); } - + preuploadProcessingOfFile(); } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { switch (requestCode) { - // 1 = Storage (from snackbar) - case 1: { - if (grantResults.length > 0 + case REQUEST_PERM_ON_CREATE_STORAGE: { + if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - getFileMetadata(true); + backgroundImageView.setImageURI(mediaUri); + storagePermitted = true; + preuploadProcessingOfFile(); } return; } - // 2 = Location - case 2: { - if (grantResults.length > 0 + case REQUEST_PERM_ON_CREATE_LOCATION: { + if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - getFileMetadata(false); + locationPermitted = true; + preuploadProcessingOfFile(); } return; } - // 3 = Storage + Location - case 3: { - if (grantResults.length > 1 + case REQUEST_PERM_ON_CREATE_STORAGE_AND_LOCATION: { + if (grantResults.length >= 2 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - getFileMetadata(true); + backgroundImageView.setImageURI(mediaUri); + storagePermitted = true; + preuploadProcessingOfFile(); } - if (grantResults.length > 1 + if (grantResults.length >= 2 && grantResults[1] == PackageManager.PERMISSION_GRANTED) { - getFileMetadata(false); + locationPermitted = true; + preuploadProcessingOfFile(); } return; } - // 4 = Storage (from submit button) - this needs to be separate from (1) because only the + // Storage (from submit button) - this needs to be separate from (1) because only the // submit button should bring user to next screen - case 4: { - if (grantResults.length > 0 + case REQUEST_PERM_ON_SUBMIT_STORAGE: { + if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { //It is OK to call this at both (1) and (4) because if perm had been granted at //snackbar, user should not be prompted at submit button - getFileMetadata(true); + preuploadProcessingOfFile(); //Uploading only begins if storage permission granted from arrow icon uploadBegins(); @@ -376,22 +364,110 @@ public class ShareActivity } } + private void preuploadProcessingOfFile() { + if(!useNewPermissions || storagePermitted) { + + if (!duplicateCheckPassed) { + //Test SHA1 of image to see if it matches SHA1 of a file on Commons + try { + InputStream inputStream = getContentResolver().openInputStream(mediaUri); + Timber.d("Input stream created from %s", mediaUri.toString()); + String fileSHA1 = Utils.getSHA1(inputStream); + Timber.d("File SHA1 is: %s", fileSHA1); + + ExistingFileAsync fileAsyncTask = new ExistingFileAsync(fileSHA1, this, new ExistingFileAsync.Callback() { + @Override + public void onResult(ExistingFileAsync.Result result) { + Timber.d("%s duplicate check: %s", mediaUri.toString(), result); + duplicateCheckPassed = + result == ExistingFileAsync.Result.DUPLICATE_PROCEED + || result == ExistingFileAsync.Result.NO_DUPLICATE; + } + }); + fileAsyncTask.execute(); + } catch (IOException e) { + Timber.d(e, "IO Exception: "); + } + } + + getFileMetadata(locationPermitted); + } else { + Timber.w("not ready for preprocess: useNewPermissions=%s storage=%s location=%s", + useNewPermissions, storagePermitted, locationPermitted); + } + } + + private Snackbar requestPermissionUsingSnackBar(String rationale, final String[] perms, final int code) { + Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), rationale, + Snackbar.LENGTH_INDEFINITE) + .setAction(R.string.ok, new View.OnClickListener() { + @Override + public void onClick(View view) { + ActivityCompat.requestPermissions(ShareActivity.this, + perms, code); + } + }); + snackbar.show(); + return snackbar; + } + + @Nullable + private String getPathOfMediaOrCopy() { + String filePath = FileUtils.getPath(getApplicationContext(), mediaUri); + Timber.d("Filepath: " + filePath); + if (filePath == null) { + // in older devices getPath() may fail depending on the source URI + // creating and using a copy of the file seems to work instead. + // TODO: there might be a more proper solution than this + String copyPath = getApplicationContext().getCacheDir().getAbsolutePath() + + "/" + new Date().getTime() + ".jpg"; + try { + ParcelFileDescriptor descriptor + = getContentResolver().openFileDescriptor(mediaUri, "r"); + if (descriptor != null) { + FileUtils.copy( + descriptor.getFileDescriptor(), + copyPath); + Timber.d("Filepath (copied): %s", copyPath); + return copyPath; + } + } catch (IOException e) { + Timber.w(e, "Error in file " + copyPath); + return null; + } + } + return filePath; + } + /** * Gets coordinates for category suggestions, either from EXIF data or user location - * @param gpsEnabled + * @param gpsEnabled if true use GPS */ - public void getFileMetadata(boolean gpsEnabled) { - String filePath = FileUtils.getPath(getApplicationContext(), mediaUri); - Timber.d("Filepath: %s", filePath); + private void getFileMetadata(boolean gpsEnabled) { Timber.d("Calling GPSExtractor"); - if(imageObj == null) { - imageObj = new GPSExtractor(filePath); - } + try { + if (imageObj == null) { + ParcelFileDescriptor descriptor + = getContentResolver().openFileDescriptor(mediaUri, "r"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if (descriptor != null) { + imageObj = new GPSExtractor(descriptor.getFileDescriptor()); + } + } else { + String filePath = getPathOfMediaOrCopy(); + if (filePath != null) { + imageObj = new GPSExtractor(filePath); + } + } + } - if (filePath != null && !filePath.equals("")) { - // Gets image coords from exif data or user location - decimalCoords = imageObj.getCoords(gpsEnabled); - useImageCoords(); + if (imageObj != null) { + // Gets image coords from exif data or user location + decimalCoords = imageObj.getCoords(gpsEnabled); + useImageCoords(); + } + } catch (FileNotFoundException e) { + Timber.w("File not found: " + mediaUri, e); } } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java index 832062ed5..716a492f9 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java @@ -113,35 +113,36 @@ public class UploadController { } contribution.setDataLength(length); } - } catch(IOException e) { + } catch (IOException e) { Timber.e(e, "IO Exception: "); - } catch(NullPointerException e) { + } catch (NullPointerException e) { Timber.e(e, "Null Pointer Exception: "); - } catch(SecurityException e) { + } catch (SecurityException e) { Timber.e(e, "Security Exception: "); } String mimeType = (String)contribution.getTag("mimeType"); Boolean imagePrefix = false; - if(mimeType == null || TextUtils.isEmpty(mimeType) || mimeType.endsWith("*")) { + if (mimeType == null || TextUtils.isEmpty(mimeType) || mimeType.endsWith("*")) { mimeType = app.getContentResolver().getType(contribution.getLocalUri()); } - if(mimeType != null) { + if (mimeType != null) { contribution.setTag("mimeType", mimeType); imagePrefix = mimeType.startsWith("image/"); Timber.d("MimeType is: %s", mimeType); } - if(imagePrefix && contribution.getDateCreated() == null) { + if (imagePrefix && contribution.getDateCreated() == null) { + Timber.d("local uri " + contribution.getLocalUri()); Cursor cursor = app.getContentResolver().query(contribution.getLocalUri(), new String[]{MediaStore.Images.ImageColumns.DATE_TAKEN}, null, null, null); - if(cursor != null && cursor.getCount() != 0) { + if (cursor != null && cursor.getCount() != 0 && cursor.getColumnCount() != 0) { cursor.moveToFirst(); Date dateCreated = new Date(cursor.getLong(0)); Date epochStart = new Date(0); - if(dateCreated.equals(epochStart) || dateCreated.before(epochStart)) { + if (dateCreated.equals(epochStart) || dateCreated.before(epochStart)) { // If date is incorrect (1st second of unix time) then set it to the current date dateCreated = new Date(); } diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml new file mode 100644 index 000000000..aa6993d43 --- /dev/null +++ b/app/src/main/res/xml/provider_paths.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file