mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 20:33:53 +01:00
Share file with camera using cache and FileProvider
* Use cache instead of external storage to share file with camera * Execute ExistingFileAsync after permission is granted
This commit is contained in:
parent
9e0792f1e2
commit
9c69539276
9 changed files with 333 additions and 177 deletions
|
|
@ -125,6 +125,16 @@
|
||||||
android:resource="@xml/modifications_sync_adapter" />
|
android:resource="@xml/modifications_sync_adapter" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="android.support.v4.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}.provider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/provider_paths"/>
|
||||||
|
</provider>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name=".contributions.ContributionsContentProvider"
|
android:name=".contributions.ContributionsContentProvider"
|
||||||
android:label="@string/provider_contributions"
|
android:label="@string/provider_contributions"
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,12 @@ package fr.free.nrw.commons.contributions;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v4.content.FileProvider;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
@ -29,34 +31,24 @@ public class ContributionController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// See http://stackoverflow.com/a/5054673/17865 for why this is done
|
// See http://stackoverflow.com/a/5054673/17865 for why this is done
|
||||||
private Uri lastGeneratedCaptureURI;
|
private Uri lastGeneratedCaptureUri;
|
||||||
|
|
||||||
private Uri reGenerateImageCaptureURI() {
|
private Uri reGenerateImageCaptureUriInCache() {
|
||||||
String storageState = Environment.getExternalStorageState();
|
File photoFile = new File(fragment.getContext().getCacheDir() + "/images",
|
||||||
if(storageState.equals(Environment.MEDIA_MOUNTED)) {
|
new Date().getTime() + ".jpg");
|
||||||
|
photoFile.getParentFile().mkdirs();
|
||||||
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Commons/images/" + new Date().getTime() + ".jpg";
|
return FileProvider.getUriForFile(
|
||||||
File _photoFile = new File(path);
|
fragment.getContext(),
|
||||||
try {
|
fragment.getActivity().getApplicationContext().getPackageName() + ".provider",
|
||||||
if(!_photoFile.exists()) {
|
photoFile);
|
||||||
_photoFile.getParentFile().mkdirs();
|
|
||||||
_photoFile.createNewFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
Timber.e(e, "Could not create file: %s", path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Uri.fromFile(_photoFile);
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("No external storage found!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startCameraCapture() {
|
public void startCameraCapture() {
|
||||||
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||||
lastGeneratedCaptureURI = reGenerateImageCaptureURI();
|
lastGeneratedCaptureUri = reGenerateImageCaptureUriInCache();
|
||||||
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, lastGeneratedCaptureURI);
|
takePictureIntent.setFlags(
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||||
|
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, lastGeneratedCaptureUri);
|
||||||
fragment.startActivityForResult(takePictureIntent, SELECT_FROM_CAMERA);
|
fragment.startActivityForResult(takePictureIntent, SELECT_FROM_CAMERA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,7 +72,7 @@ public class ContributionController {
|
||||||
break;
|
break;
|
||||||
case SELECT_FROM_CAMERA:
|
case SELECT_FROM_CAMERA:
|
||||||
shareIntent.setType("image/jpeg"); //FIXME: Find out appropriate mime type
|
shareIntent.setType("image/jpeg"); //FIXME: Find out appropriate mime type
|
||||||
shareIntent.putExtra(Intent.EXTRA_STREAM, lastGeneratedCaptureURI);
|
shareIntent.putExtra(Intent.EXTRA_STREAM, lastGeneratedCaptureUri);
|
||||||
shareIntent.putExtra(UploadService.EXTRA_SOURCE, Contribution.SOURCE_CAMERA);
|
shareIntent.putExtra(UploadService.EXTRA_SOURCE, Contribution.SOURCE_CAMERA);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -93,12 +85,12 @@ public class ContributionController {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveState(Bundle outState) {
|
public void saveState(Bundle outState) {
|
||||||
outState.putParcelable("lastGeneratedCaptureURI", lastGeneratedCaptureURI);
|
outState.putParcelable("lastGeneratedCaptureURI", lastGeneratedCaptureUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadState(Bundle savedInstanceState) {
|
public void loadState(Bundle savedInstanceState) {
|
||||||
if(savedInstanceState != null) {
|
if(savedInstanceState != null) {
|
||||||
lastGeneratedCaptureURI = savedInstanceState.getParcelable("lastGeneratedCaptureURI");
|
lastGeneratedCaptureUri = savedInstanceState.getParcelable("lastGeneratedCaptureURI");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -197,7 +197,7 @@ public class MediaDetailFragment extends Fragment {
|
||||||
extractor.fetch();
|
extractor.fetch();
|
||||||
return Boolean.TRUE;
|
return Boolean.TRUE;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
Timber.d(e);
|
||||||
}
|
}
|
||||||
return Boolean.FALSE;
|
return Boolean.FALSE;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,13 @@ import android.content.Intent;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
|
|
||||||
import fr.free.nrw.commons.MWApi;
|
|
||||||
import org.mediawiki.api.ApiResult;
|
import org.mediawiki.api.ApiResult;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
|
import fr.free.nrw.commons.MWApi;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
@ -22,13 +22,22 @@ import timber.log.Timber;
|
||||||
* Displays a warning to the user if the file already exists on Commons
|
* Displays a warning to the user if the file already exists on Commons
|
||||||
*/
|
*/
|
||||||
public class ExistingFileAsync extends AsyncTask<Void, Void, Boolean> {
|
public class ExistingFileAsync extends AsyncTask<Void, Void, Boolean> {
|
||||||
|
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;
|
public ExistingFileAsync(String fileSHA1, Context context, Callback callback) {
|
||||||
private Context context;
|
|
||||||
|
|
||||||
public ExistingFileAsync(String fileSHA1, Context context) {
|
|
||||||
this.fileSHA1 = fileSHA1;
|
this.fileSHA1 = fileSHA1;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
this.callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -79,17 +88,20 @@ public class ExistingFileAsync extends AsyncTask<Void, Void, Boolean> {
|
||||||
//Go back to ContributionsActivity
|
//Go back to ContributionsActivity
|
||||||
Intent intent = new Intent(context, ContributionsActivity.class);
|
Intent intent = new Intent(context, ContributionsActivity.class);
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
|
callback.onResult(Result.DUPLICATE_CANCELLED);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
builder.setNegativeButton(R.string.yes, new DialogInterface.OnClickListener() {
|
builder.setNegativeButton(R.string.yes, new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
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();
|
AlertDialog dialog = builder.create();
|
||||||
dialog.show();
|
dialog.show();
|
||||||
|
} else {
|
||||||
|
callback.onResult(Result.NO_DUPLICATE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -9,6 +9,16 @@ import android.os.Build;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.provider.DocumentsContract;
|
import android.provider.DocumentsContract;
|
||||||
import android.provider.MediaStore;
|
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 {
|
public class FileUtils {
|
||||||
|
|
||||||
|
|
@ -23,6 +33,7 @@ public class FileUtils {
|
||||||
*/
|
*/
|
||||||
// Can be safely suppressed, checks for isKitKat before running isDocumentUri
|
// Can be safely suppressed, checks for isKitKat before running isDocumentUri
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
|
@Nullable
|
||||||
public static String getPath(Context context, Uri uri) {
|
public static String getPath(Context context, Uri uri) {
|
||||||
|
|
||||||
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
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.
|
* @param selectionArgs (Optional) Selection arguments used in the query.
|
||||||
* @return The value of the _data column, which is typically a file path.
|
* @return The value of the _data column, which is typically a file path.
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
public static String getDataColumn(Context context, Uri uri, String selection,
|
public static String getDataColumn(Context context, Uri uri, String selection,
|
||||||
String[] selectionArgs) {
|
String[] selectionArgs) {
|
||||||
|
|
||||||
|
|
@ -108,6 +120,8 @@ public class FileUtils {
|
||||||
final int column_index = cursor.getColumnIndexOrThrow(column);
|
final int column_index = cursor.getColumnIndexOrThrow(column);
|
||||||
return cursor.getString(column_index);
|
return cursor.getString(column_index);
|
||||||
}
|
}
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Timber.d(e);
|
||||||
} finally {
|
} finally {
|
||||||
if (cursor != null)
|
if (cursor != null)
|
||||||
cursor.close();
|
cursor.close();
|
||||||
|
|
@ -119,7 +133,7 @@ public class FileUtils {
|
||||||
* @param uri The Uri to check.
|
* @param uri The Uri to check.
|
||||||
* @return Whether the Uri authority is ExternalStorageProvider.
|
* @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());
|
return "com.android.externalstorage.documents".equals(uri.getAuthority());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,7 +141,7 @@ public class FileUtils {
|
||||||
* @param uri The Uri to check.
|
* @param uri The Uri to check.
|
||||||
* @return Whether the Uri authority is DownloadsProvider.
|
* @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());
|
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,7 +149,37 @@ public class FileUtils {
|
||||||
* @param uri The Uri to check.
|
* @param uri The Uri to check.
|
||||||
* @return Whether the Uri authority is MediaProvider.
|
* @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());
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -7,10 +7,14 @@ import android.location.Location;
|
||||||
import android.location.LocationListener;
|
import android.location.LocationListener;
|
||||||
import android.location.LocationManager;
|
import android.location.LocationManager;
|
||||||
import android.media.ExifInterface;
|
import android.media.ExifInterface;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.annotation.RequiresApi;
|
||||||
|
|
||||||
|
import java.io.FileDescriptor;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
|
|
@ -23,17 +27,38 @@ import timber.log.Timber;
|
||||||
*/
|
*/
|
||||||
public class GPSExtractor {
|
public class GPSExtractor {
|
||||||
|
|
||||||
private String filePath;
|
private ExifInterface exif;
|
||||||
private double decLatitude, decLongitude;
|
private double decLatitude;
|
||||||
|
private double decLongitude;
|
||||||
private Double currentLatitude = null;
|
private Double currentLatitude = null;
|
||||||
private Double currentLongitude = null;
|
private Double currentLongitude = null;
|
||||||
public boolean imageCoordsExists;
|
public boolean imageCoordsExists;
|
||||||
private MyLocationListener myLocationListener;
|
private MyLocationListener myLocationListener;
|
||||||
private LocationManager locationManager;
|
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
|
@Nullable
|
||||||
public String getCoords(boolean useGPS) {
|
public String getCoords(boolean useGPS) {
|
||||||
|
|
||||||
ExifInterface exif;
|
|
||||||
String latitude = "";
|
String latitude = "";
|
||||||
String longitude = "";
|
String longitude = "";
|
||||||
String latitude_ref = "";
|
String latitude_ref = "";
|
||||||
String longitude_ref = "";
|
String longitude_ref = "";
|
||||||
String decimalCoords = "";
|
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 image has no EXIF data and user has enabled GPS setting, get user's location
|
||||||
if (exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE) == null && useGPS) {
|
if (exif == null || exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE) == null) {
|
||||||
registerLocationManager();
|
if (useGPS) {
|
||||||
|
registerLocationManager();
|
||||||
|
|
||||||
imageCoordsExists = false;
|
imageCoordsExists = false;
|
||||||
Timber.d("EXIF data has no location info");
|
Timber.d("EXIF data has no location info");
|
||||||
|
|
||||||
//Check what user's preference is for automatic location detection
|
//Check what user's preference is for automatic location detection
|
||||||
boolean gpsPrefEnabled = gpsPreferenceEnabled();
|
boolean gpsPrefEnabled = gpsPreferenceEnabled();
|
||||||
|
|
||||||
//Check that currentLatitude and currentLongitude have been explicitly set by MyLocationListener and do not default to (0.0,0.0)
|
//Check that currentLatitude and currentLongitude have been
|
||||||
if (gpsPrefEnabled && currentLatitude != null && currentLongitude != null) {
|
// explicitly set by MyLocationListener
|
||||||
Timber.d("Current location values: Lat = %f Long = %f",
|
// and do not default to (0.0,0.0)
|
||||||
currentLatitude, currentLongitude);
|
if (gpsPrefEnabled && currentLatitude != null && currentLongitude != null) {
|
||||||
return String.valueOf(currentLatitude) + "|" + String.valueOf(currentLongitude);
|
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 {
|
} else {
|
||||||
// No coords found
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else if (exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE) == null) {
|
|
||||||
return null;
|
|
||||||
} else {
|
} else {
|
||||||
//If image has EXIF data, extract image coords
|
//If image has EXIF data, extract image coords
|
||||||
imageCoordsExists = true;
|
imageCoordsExists = true;
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@ import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
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.design.widget.Snackbar;
|
||||||
import android.support.graphics.drawable.VectorDrawableCompat;
|
import android.support.graphics.drawable.VectorDrawableCompat;
|
||||||
import android.support.v4.app.ActivityCompat;
|
import android.support.v4.app.ActivityCompat;
|
||||||
|
|
@ -20,9 +23,11 @@ import android.widget.Toast;
|
||||||
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
||||||
import com.facebook.drawee.view.SimpleDraweeView;
|
import com.facebook.drawee.view.SimpleDraweeView;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
|
|
@ -48,13 +53,16 @@ public class ShareActivity
|
||||||
implements SingleUploadFragment.OnUploadActionInitiated,
|
implements SingleUploadFragment.OnUploadActionInitiated,
|
||||||
CategorizationFragment.OnCategoriesSaveHandler {
|
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 CategorizationFragment categorizationFragment;
|
||||||
|
|
||||||
private CommonsApplication app;
|
private CommonsApplication app;
|
||||||
|
|
||||||
private String source;
|
private String source;
|
||||||
private String mimeType;
|
private String mimeType;
|
||||||
private String mediaUriString;
|
|
||||||
|
|
||||||
private Uri mediaUri;
|
private Uri mediaUri;
|
||||||
private Contribution contribution;
|
private Contribution contribution;
|
||||||
|
|
@ -68,15 +76,16 @@ public class ShareActivity
|
||||||
private String decimalCoords;
|
private String decimalCoords;
|
||||||
|
|
||||||
private boolean useNewPermissions = false;
|
private boolean useNewPermissions = false;
|
||||||
private boolean storagePermission = false;
|
private boolean storagePermitted = false;
|
||||||
private boolean locationPermission = false;
|
private boolean locationPermitted = false;
|
||||||
|
|
||||||
private String title;
|
private String title;
|
||||||
private String description;
|
private String description;
|
||||||
private Snackbar snackbar;
|
private Snackbar snackbar;
|
||||||
|
private boolean duplicateCheckPassed = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when user taps the submit button
|
* Called when user taps the submit button.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void uploadActionInitiated(String title, String description) {
|
public void uploadActionInitiated(String title, String description) {
|
||||||
|
|
@ -85,10 +94,11 @@ public class ShareActivity
|
||||||
this.description = description;
|
this.description = description;
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
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
|
// Check for Storage permission that is required for upload.
|
||||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
// Do not allow user to proceed without permission, otherwise will crash
|
||||||
//See http://stackoverflow.com/questions/33169455/onrequestpermissionsresult-not-being-called-in-dialog-fragment
|
if (needsToRequestStoragePermission()) {
|
||||||
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 4);
|
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
|
||||||
|
REQUEST_PERM_ON_SUBMIT_STORAGE);
|
||||||
} else {
|
} else {
|
||||||
uploadBegins();
|
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() {
|
private void uploadBegins() {
|
||||||
if (locationPermission) {
|
getFileMetadata(locationPermitted);
|
||||||
getFileMetadata(true);
|
|
||||||
} else {
|
|
||||||
getFileMetadata(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Toast startingToast = Toast.makeText(
|
Toast startingToast = Toast.makeText(
|
||||||
CommonsApplication.getInstance(),
|
CommonsApplication.getInstance(),
|
||||||
|
|
@ -232,9 +248,9 @@ public class ShareActivity
|
||||||
//Receive intent from ContributionController.java when user selects picture to upload
|
//Receive intent from ContributionController.java when user selects picture to upload
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
|
|
||||||
if(intent.getAction().equals(Intent.ACTION_SEND)) {
|
if (intent.getAction().equals(Intent.ACTION_SEND)) {
|
||||||
mediaUri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
mediaUri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
||||||
if(intent.hasExtra(UploadService.EXTRA_SOURCE)) {
|
if (intent.hasExtra(UploadService.EXTRA_SOURCE)) {
|
||||||
source = intent.getStringExtra(UploadService.EXTRA_SOURCE);
|
source = intent.getStringExtra(UploadService.EXTRA_SOURCE);
|
||||||
} else {
|
} else {
|
||||||
source = Contribution.SOURCE_EXTERNAL;
|
source = Contribution.SOURCE_EXTERNAL;
|
||||||
|
|
@ -243,129 +259,101 @@ public class ShareActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mediaUri != null) {
|
if (mediaUri != null) {
|
||||||
mediaUriString = mediaUri.toString();
|
backgroundImageView.setImageURI(mediaUri);
|
||||||
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: ");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
contribution = savedInstanceState.getParcelable("contribution");
|
contribution = savedInstanceState.getParcelable("contribution");
|
||||||
}
|
}
|
||||||
|
|
||||||
requestAuthToken();
|
requestAuthToken();
|
||||||
|
|
||||||
Timber.d("Uri: %s", mediaUriString);
|
Timber.d("Uri: %s", mediaUri.toString());
|
||||||
Timber.d("Ext storage dir: %s", Environment.getExternalStorageDirectory());
|
Timber.d("Ext storage dir: %s", Environment.getExternalStorageDirectory());
|
||||||
|
|
||||||
|
useNewPermissions = false;
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
useNewPermissions = true;
|
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) {
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
||||||
locationPermission = true;
|
locationPermitted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check storage permissions if marshmallow or newer
|
// Check storage permissions if marshmallow or newer
|
||||||
if (useNewPermissions && (!storagePermission || !locationPermission)) {
|
if (useNewPermissions && (!storagePermitted || !locationPermitted)) {
|
||||||
if (!storagePermission && !locationPermission) {
|
if (!storagePermitted && !locationPermitted) {
|
||||||
String permissionRationales = getResources().getString(R.string.storage_permission_rationale) + "\n" + getResources().getString(R.string.location_permission_rationale);
|
String permissionRationales =
|
||||||
snackbar = Snackbar.make(findViewById(android.R.id.content), permissionRationales,
|
getResources().getString(R.string.storage_permission_rationale) + "\n"
|
||||||
Snackbar.LENGTH_INDEFINITE)
|
+ getResources().getString(R.string.location_permission_rationale);
|
||||||
.setAction(R.string.ok, new View.OnClickListener() {
|
snackbar = requestPermissionUsingSnackBar(
|
||||||
@Override
|
permissionRationales,
|
||||||
public void onClick(View view) {
|
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION},
|
||||||
ActivityCompat.requestPermissions(ShareActivity.this,
|
REQUEST_PERM_ON_CREATE_STORAGE_AND_LOCATION);
|
||||||
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION}, 3);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
snackbar.show();
|
|
||||||
View snackbarView = snackbar.getView();
|
View snackbarView = snackbar.getView();
|
||||||
TextView textView = (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text);
|
TextView textView = (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text);
|
||||||
textView.setMaxLines(3);
|
textView.setMaxLines(3);
|
||||||
} else if (!storagePermission) {
|
} else if (!storagePermitted) {
|
||||||
Snackbar.make(findViewById(android.R.id.content), R.string.storage_permission_rationale,
|
requestPermissionUsingSnackBar(
|
||||||
Snackbar.LENGTH_INDEFINITE)
|
getString(R.string.storage_permission_rationale),
|
||||||
.setAction(R.string.ok, new View.OnClickListener() {
|
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
|
||||||
@Override
|
REQUEST_PERM_ON_CREATE_STORAGE);
|
||||||
public void onClick(View view) {
|
} else if (!locationPermitted) {
|
||||||
ActivityCompat.requestPermissions(ShareActivity.this,
|
requestPermissionUsingSnackBar(
|
||||||
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
|
getString(R.string.location_permission_rationale),
|
||||||
}
|
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
|
||||||
}).show();
|
REQUEST_PERM_ON_CREATE_LOCATION);
|
||||||
} 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 (useNewPermissions && storagePermission && !locationPermission) {
|
|
||||||
getFileMetadata(true);
|
|
||||||
} else if(!useNewPermissions || (storagePermission && locationPermission)) {
|
|
||||||
getFileMetadata(true);
|
|
||||||
}
|
}
|
||||||
|
preuploadProcessingOfFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode,
|
public void onRequestPermissionsResult(int requestCode,
|
||||||
String[] permissions, int[] grantResults) {
|
String[] permissions, int[] grantResults) {
|
||||||
switch (requestCode) {
|
switch (requestCode) {
|
||||||
// 1 = Storage (from snackbar)
|
case REQUEST_PERM_ON_CREATE_STORAGE: {
|
||||||
case 1: {
|
if (grantResults.length >= 1
|
||||||
if (grantResults.length > 0
|
|
||||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
getFileMetadata(true);
|
backgroundImageView.setImageURI(mediaUri);
|
||||||
|
storagePermitted = true;
|
||||||
|
preuploadProcessingOfFile();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 2 = Location
|
case REQUEST_PERM_ON_CREATE_LOCATION: {
|
||||||
case 2: {
|
if (grantResults.length >= 1
|
||||||
if (grantResults.length > 0
|
|
||||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
getFileMetadata(false);
|
locationPermitted = true;
|
||||||
|
preuploadProcessingOfFile();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 3 = Storage + Location
|
case REQUEST_PERM_ON_CREATE_STORAGE_AND_LOCATION: {
|
||||||
case 3: {
|
if (grantResults.length >= 2
|
||||||
if (grantResults.length > 1
|
|
||||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
&& 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) {
|
&& grantResults[1] == PackageManager.PERMISSION_GRANTED) {
|
||||||
getFileMetadata(false);
|
locationPermitted = true;
|
||||||
|
preuploadProcessingOfFile();
|
||||||
}
|
}
|
||||||
return;
|
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
|
// submit button should bring user to next screen
|
||||||
case 4: {
|
case REQUEST_PERM_ON_SUBMIT_STORAGE: {
|
||||||
if (grantResults.length > 0
|
if (grantResults.length >= 1
|
||||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
//It is OK to call this at both (1) and (4) because if perm had been granted at
|
//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
|
//snackbar, user should not be prompted at submit button
|
||||||
getFileMetadata(true);
|
preuploadProcessingOfFile();
|
||||||
|
|
||||||
//Uploading only begins if storage permission granted from arrow icon
|
//Uploading only begins if storage permission granted from arrow icon
|
||||||
uploadBegins();
|
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
|
* 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) {
|
private void getFileMetadata(boolean gpsEnabled) {
|
||||||
String filePath = FileUtils.getPath(getApplicationContext(), mediaUri);
|
|
||||||
Timber.d("Filepath: %s", filePath);
|
|
||||||
Timber.d("Calling GPSExtractor");
|
Timber.d("Calling GPSExtractor");
|
||||||
if(imageObj == null) {
|
try {
|
||||||
imageObj = new GPSExtractor(filePath);
|
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("")) {
|
if (imageObj != null) {
|
||||||
// Gets image coords from exif data or user location
|
// Gets image coords from exif data or user location
|
||||||
decimalCoords = imageObj.getCoords(gpsEnabled);
|
decimalCoords = imageObj.getCoords(gpsEnabled);
|
||||||
useImageCoords();
|
useImageCoords();
|
||||||
|
}
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
Timber.w("File not found: " + mediaUri, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -113,35 +113,36 @@ public class UploadController {
|
||||||
}
|
}
|
||||||
contribution.setDataLength(length);
|
contribution.setDataLength(length);
|
||||||
}
|
}
|
||||||
} catch(IOException e) {
|
} catch (IOException e) {
|
||||||
Timber.e(e, "IO Exception: ");
|
Timber.e(e, "IO Exception: ");
|
||||||
} catch(NullPointerException e) {
|
} catch (NullPointerException e) {
|
||||||
Timber.e(e, "Null Pointer Exception: ");
|
Timber.e(e, "Null Pointer Exception: ");
|
||||||
} catch(SecurityException e) {
|
} catch (SecurityException e) {
|
||||||
Timber.e(e, "Security Exception: ");
|
Timber.e(e, "Security Exception: ");
|
||||||
}
|
}
|
||||||
|
|
||||||
String mimeType = (String)contribution.getTag("mimeType");
|
String mimeType = (String)contribution.getTag("mimeType");
|
||||||
Boolean imagePrefix = false;
|
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());
|
mimeType = app.getContentResolver().getType(contribution.getLocalUri());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mimeType != null) {
|
if (mimeType != null) {
|
||||||
contribution.setTag("mimeType", mimeType);
|
contribution.setTag("mimeType", mimeType);
|
||||||
imagePrefix = mimeType.startsWith("image/");
|
imagePrefix = mimeType.startsWith("image/");
|
||||||
Timber.d("MimeType is: %s", mimeType);
|
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(),
|
Cursor cursor = app.getContentResolver().query(contribution.getLocalUri(),
|
||||||
new String[]{MediaStore.Images.ImageColumns.DATE_TAKEN}, null, null, null);
|
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();
|
cursor.moveToFirst();
|
||||||
Date dateCreated = new Date(cursor.getLong(0));
|
Date dateCreated = new Date(cursor.getLong(0));
|
||||||
Date epochStart = new Date(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
|
// If date is incorrect (1st second of unix time) then set it to the current date
|
||||||
dateCreated = new Date();
|
dateCreated = new Date();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
4
app/src/main/res/xml/provider_paths.xml
Normal file
4
app/src/main/res/xml/provider_paths.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<cache-path name="images" path="images/" />
|
||||||
|
</paths>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue