BREAKING CHANGE: pictures are now stored in getExternalFilesDir(); no migration takes place!

This commit is contained in:
Dennis Guse
2019-12-29 01:30:03 +01:00
parent a230504925
commit f3a308e669
10 changed files with 26 additions and 104 deletions
@@ -15,8 +15,6 @@
*/ */
package de.dennisguse.opentracks.util; package de.dennisguse.opentracks.util;
import android.os.Environment;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -32,16 +30,6 @@ import java.io.File;
@RunWith(JUnit4.class) @RunWith(JUnit4.class)
public class FileUtilsTest { public class FileUtilsTest {
/**
* Tests {@link FileUtils#getPath(String...)}.
*/
@Test
public void testBuildExternalDirectoryPath() {
String expectedName = Environment.getExternalStorageDirectory() + File.separator + FileUtils.SDCARD_TOP_DIR + File.separator + "a" + File.separator + "b" + File.separator + "c";
String dirName = FileUtils.getPath("a", "b", "c");
Assert.assertEquals(expectedName, dirName);
}
/** /**
* Tests {@link FileUtils#buildUniqueFileName(File, String, String)} when the file is new. * Tests {@link FileUtils#buildUniqueFileName(File, String, String)} when the file is new.
*/ */
@@ -383,10 +383,6 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
startActivity(intent); startActivity(intent);
return true; return true;
case R.id.track_detail_insert_photo: case R.id.track_detail_insert_photo:
if (!FileUtils.isExternalStorageWriteable() || ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE);
return false;
}
createWaypointWithPicture(); createWaypointWithPicture();
return true; return true;
case R.id.track_detail_menu_show_on_map: case R.id.track_detail_menu_show_on_map:
@@ -189,7 +189,7 @@ public class ContentProviderUtils {
// Delete tracks last since it triggers a database vaccum call // Delete tracks last since it triggers a database vaccum call
contentResolver.delete(TracksColumns.CONTENT_URI, null, null); contentResolver.delete(TracksColumns.CONTENT_URI, null, null);
File dir = FileUtils.getPhotoDir(); File dir = FileUtils.getPhotoDir(context);
deleteDirectoryRecurse(context, dir); deleteDirectoryRecurse(context, dir);
} }
@@ -219,7 +219,7 @@ public class ContentProviderUtils {
contentResolver.delete(WaypointsColumns.CONTENT_URI, WaypointsColumns.TRACKID + "=?", contentResolver.delete(WaypointsColumns.CONTENT_URI, WaypointsColumns.TRACKID + "=?",
new String[]{Long.toString(trackId)}); new String[]{Long.toString(trackId)});
deleteDirectoryRecurse(context, FileUtils.getPhotoDir(trackId)); deleteDirectoryRecurse(context, FileUtils.getPhotoDir(context, trackId));
} }
/** /**
@@ -229,12 +229,10 @@ public class ContentProviderUtils {
*/ */
private void deleteDirectoryRecurse(Context context, File dir) { private void deleteDirectoryRecurse(Context context, File dir) {
// TODO Does not work with // TODO Does not work with
if (FileUtils.isDirectory(dir)) { if (dir.exists() && dir.isDirectory()) {
for (File child : dir.listFiles()) { for (File child : dir.listFiles()) {
deleteDirectoryRecurse(context, child); deleteDirectoryRecurse(context, child);
} }
}
if (dir.exists()) {
dir.delete(); dir.delete();
FileUtils.updateMediaScanner(context, Uri.fromFile(dir)); FileUtils.updateMediaScanner(context, Uri.fromFile(dir));
} }
@@ -72,11 +72,6 @@ public class ExportActivity extends FragmentActivity implements FileTypeDialogFr
super.onActivityResult(requestCode, resultCode, resultData); super.onActivityResult(requestCode, resultCode, resultData);
if (requestCode == EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE) { if (requestCode == EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {
// final int takeFlags = resultData.getFlags()
// & (Intent.FLAG_GRANT_READ_URI_PERMISSION
// | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
//// Check for the freshest data.
// getContentResolver().takePersistableUriPermission(uri, takeFlags);
directoryUri = resultData.getData(); directoryUri = resultData.getData();
fileTypeDialogStart(); fileTypeDialogStart();
} else { } else {
@@ -93,9 +88,9 @@ public class ExportActivity extends FragmentActivity implements FileTypeDialogFr
@Override @Override
public void onFileTypeDone(TrackFileFormat trackFileFormat) { public void onFileTypeDone(TrackFileFormat trackFileFormat) {
DocumentFile pickedDirectory = DocumentFile.fromTreeUri(this, directoryUri); DocumentFile pickedDirectory = DocumentFile.fromTreeUri(this, directoryUri);
DocumentFile exportDirectory = pickedDirectory.findFile(FileUtils.SDCARD_TOP_DIR); DocumentFile exportDirectory = pickedDirectory.findFile(FileUtils.EXPORT_DIR);
if (exportDirectory == null) { if (exportDirectory == null) {
exportDirectory = pickedDirectory.createDirectory(FileUtils.SDCARD_TOP_DIR); exportDirectory = pickedDirectory.createDirectory(FileUtils.EXPORT_DIR);
} }
directoryDisplayName = FileUtils.getPathDisplayName(trackFileFormat.getExtension()); directoryDisplayName = FileUtils.getPathDisplayName(trackFileFormat.getExtension());
@@ -377,7 +377,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
if (importTrackId == -1L) { if (importTrackId == -1L) {
return null; return null;
} }
File dir = FileUtils.getPhotoDir(importTrackId); File dir = FileUtils.getPhotoDir(context, importTrackId);
File file = new File(dir, fileName); File file = new File(dir, fileName);
return Uri.fromFile(file).toString(); return Uri.fromFile(file).toString();
} }
@@ -73,11 +73,6 @@ public class ImportActivity extends FragmentActivity implements FileTypeDialogFr
super.onActivityResult(requestCode, resultCode, resultData); super.onActivityResult(requestCode, resultCode, resultData);
if (requestCode == EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE) { if (requestCode == EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {
// final int takeFlags = resultData.getFlags()
// & (Intent.FLAG_GRANT_READ_URI_PERMISSION
// | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
//// Check for the freshest data.
// getContentResolver().takePersistableUriPermission(uri, takeFlags);
directoryUri = resultData.getData(); directoryUri = resultData.getData();
fileTypeDialogStart(); fileTypeDialogStart();
} else { } else {
@@ -68,7 +68,7 @@ public class KmzTrackImporter implements TrackImporter {
while ((zipEntry = zipInputStream.getNextEntry()) != null) { while ((zipEntry = zipInputStream.getNextEntry()) != null) {
if (Thread.interrupted()) { if (Thread.interrupted()) {
Log.d(TAG, "Thread interrupted"); Log.d(TAG, "Thread interrupted");
cleanImport(trackId); cleanImport(context, trackId);
return -1L; return -1L;
} }
String fileName = zipEntry.getName(); String fileName = zipEntry.getName();
@@ -76,7 +76,7 @@ public class KmzTrackImporter implements TrackImporter {
trackId = parseKml(zipInputStream); trackId = parseKml(zipInputStream);
if (trackId == -1L) { if (trackId == -1L) {
Log.d(TAG, "Unable to parse kml in kmz"); Log.d(TAG, "Unable to parse kml in kmz");
cleanImport(trackId); cleanImport(context, trackId);
return -1L; return -1L;
} }
} else { } else {
@@ -90,7 +90,7 @@ public class KmzTrackImporter implements TrackImporter {
return trackId; return trackId;
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "Unable to import file", e); Log.e(TAG, "Unable to import file", e);
cleanImport(trackId); cleanImport(context, trackId);
return -1L; return -1L;
} }
} }
@@ -100,15 +100,15 @@ public class KmzTrackImporter implements TrackImporter {
* *
* @param trackId the trackId * @param trackId the trackId
*/ */
private void cleanImport(long trackId) { private void cleanImport(Context context, long trackId) {
if (PreferencesUtils.isRecording(trackId)) { if (PreferencesUtils.isRecording(trackId)) {
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context); ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
contentProviderUtils.deleteTrack(context, trackId); contentProviderUtils.deleteTrack(context, trackId);
} }
if (importTrackId != -1L) { if (importTrackId != -1L) {
File dir = FileUtils.getPhotoDir(importTrackId); File dir = FileUtils.getPhotoDir(context, importTrackId);
if (FileUtils.isDirectory(dir)) { if (dir.exists() && dir.isDirectory()) {
for (File file : dir.listFiles()) { for (File file : dir.listFiles()) {
file.delete(); file.delete();
} }
@@ -159,8 +159,7 @@ public class KmzTrackImporter implements TrackImporter {
return; return;
} }
File dir = FileUtils.getPhotoDir(importTrackId); File dir = FileUtils.getPhotoDir(context, importTrackId);
FileUtils.ensureDirectoryExists(dir);
File file = new File(dir, fileName); File file = new File(dir, fileName);
try (FileOutputStream fileOutputStream = new FileOutputStream(file)) { try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
@@ -36,64 +36,25 @@ public class FileUtils {
*/ */
static final String FILEPROVIDER = BuildConfig.APPLICATION_ID + ".fileprovider"; static final String FILEPROVIDER = BuildConfig.APPLICATION_ID + ".fileprovider";
/** public static final String EXPORT_DIR = "OpenTracks";
* Our external storage directory.
*/
public static final String SDCARD_TOP_DIR = "OpenTracks"; // TODO RENAME
private static final String TRACK_PREFIX = "OpenTracks";
/** /**
* The maximum FAT32 path length. See the FAT32 spec at * The maximum FAT32 path length. See the FAT32 spec at
* http://msdn.microsoft.com/en-us/windows/hardware/gg463080 * http://msdn.microsoft.com/en-us/windows/hardware/gg463080
*/ */
static final int MAX_FAT32_PATH_LENGTH = 260; static final int MAX_FAT32_PATH_LENGTH = 260;
/**
* The pictures directory under the app's external storage directory.
*/
private static final String PICTURES_DIR = "pictures";
private FileUtils() { private FileUtils() {
} }
/** public static File getPhotoDir(Context context) {
* Returns true if the external storage is writable. return context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
*/
public static boolean isExternalStorageWriteable() {
String state = Environment.getExternalStorageState();
return Environment.MEDIA_MOUNTED.equals(state);
} }
/** public static File getPhotoDir(Context context, long trackId) {
* Returns true if the directory exists. File photoDirectory = new File(getPhotoDir(context), "" + trackId);
* photoDirectory.mkdirs();
* @param dir the directory return photoDirectory;
*/
@Deprecated // TODO REMOVE" rewrite all locations this was used.
public static boolean isDirectory(File dir) {
return dir.exists() && dir.isDirectory();
}
/**
* Ensures the directory exists by creating it and its parents if necessary.
*
* @return whether the directory exists (either already existed or was
* successfully created)
*/
@Deprecated // TODO REMOVE" rewrite all locations this was used.
public static boolean ensureDirectoryExists(File dir) {
if (isDirectory(dir)) {
return true;
}
return dir.mkdirs();
}
@Deprecated // TODO REMOVE" rewrite all locations this was used.
public static File getPhotoDir() {
return new File(getPath(PICTURES_DIR));
}
@Deprecated // TODO REMOVE" rewrite all locations this was used.
public static File getPhotoDir(long trackId) {
return new File(getPath(PICTURES_DIR, TRACK_PREFIX + trackId));
} }
/** /**
@@ -104,7 +65,7 @@ public class FileUtils {
public static String getPathDisplayName(String... components) { public static String getPathDisplayName(String... components) {
StringBuilder dirNameBuilder = new StringBuilder(); StringBuilder dirNameBuilder = new StringBuilder();
dirNameBuilder.append(File.separatorChar); dirNameBuilder.append(File.separatorChar);
dirNameBuilder.append(SDCARD_TOP_DIR); dirNameBuilder.append(EXPORT_DIR);
for (String component : components) { for (String component : components) {
dirNameBuilder.append(File.separatorChar); dirNameBuilder.append(File.separatorChar);
dirNameBuilder.append(component); dirNameBuilder.append(component);
@@ -112,16 +73,6 @@ public class FileUtils {
return dirNameBuilder.toString(); return dirNameBuilder.toString();
} }
/**
* Gets a path on the external storage.
*
* @param components the components
*/
@Deprecated // TODO REMOVE" rewrite all locations this was used.
public static String getPath(String... components) {
return Environment.getExternalStorageDirectory() + getPathDisplayName(components);
}
/** /**
* Builds a filename with the given base name (prefix) and the given extension, possibly adding a suffix to ensure the file doesn't exist. * Builds a filename with the given base name (prefix) and the given extension, possibly adding a suffix to ensure the file doesn't exist.
* *
@@ -161,6 +112,7 @@ public class FileUtils {
return fileName.substring(index + 1); return fileName.substring(index + 1);
} }
// TODO Make user configurable!
public static void updateMediaScanner(Context context, Uri uri) { public static void updateMediaScanner(Context context, Uri uri) {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
mediaScanIntent.setData(uri); mediaScanIntent.setData(uri);
@@ -157,8 +157,7 @@ public class IntentUtils {
* @param trackId the track id * @param trackId the track id
*/ */
public static Pair<Intent, Uri> createTakePictureIntent(Context context, long trackId) { public static Pair<Intent, Uri> createTakePictureIntent(Context context, long trackId) {
File dir = FileUtils.getPhotoDir(trackId); File dir = FileUtils.getPhotoDir(context, trackId);
FileUtils.ensureDirectoryExists(dir);
String fileName = SimpleDateFormat.getDateTimeInstance().format(new Date()); String fileName = SimpleDateFormat.getDateTimeInstance().format(new Date());
File file = new File(dir, FileUtils.buildUniqueFileName(dir, fileName, JPEG_EXTENSION)); File file = new File(dir, FileUtils.buildUniqueFileName(dir, fileName, JPEG_EXTENSION));
+2 -2
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<paths> <paths>
<external-path <files-path
name="mytracks_pictures" name="pictures"
path="/" /> path="/" />
</paths> </paths>