diff options
Diffstat (limited to 'core')
| -rw-r--r-- | core/java/android/os/FileUtils.java | 75 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/os/FileUtilsTest.java | 64 |
2 files changed, 139 insertions, 0 deletions
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index b302f95..931cd3e 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -16,11 +16,13 @@ package android.os; +import android.provider.DocumentsContract.Document; import android.system.ErrnoException; import android.system.Os; import android.text.TextUtils; import android.util.Log; import android.util.Slog; +import android.webkit.MimeTypeMap; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; @@ -34,6 +36,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.Comparator; +import java.util.Objects; import java.util.regex.Pattern; import java.util.zip.CRC32; import java.util.zip.CheckedInputStream; @@ -533,4 +536,76 @@ public class FileUtils { } return null; } + + /** + * Generates a unique file name under the given parent directory. If the display name doesn't + * have an extension that matches the requested MIME type, the default extension for that MIME + * type is appended. If a file already exists, the name is appended with a numerical value to + * make it unique. + * + * For example, the display name 'example' with 'text/plain' MIME might produce + * 'example.txt' or 'example (1).txt', etc. + * + * @throws FileNotFoundException + */ + public static File buildUniqueFile(File parent, String mimeType, String displayName) + throws FileNotFoundException { + String name; + String ext; + + if (Document.MIME_TYPE_DIR.equals(mimeType)) { + name = displayName; + ext = null; + } else { + String mimeTypeFromExt; + + // Extract requested extension from display name + final int lastDot = displayName.lastIndexOf('.'); + if (lastDot >= 0) { + name = displayName.substring(0, lastDot); + ext = displayName.substring(lastDot + 1); + mimeTypeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension( + ext.toLowerCase()); + } else { + name = displayName; + ext = null; + mimeTypeFromExt = null; + } + + if (mimeTypeFromExt == null) { + mimeTypeFromExt = "application/octet-stream"; + } + + final String extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType( + mimeType); + if (Objects.equals(mimeType, mimeTypeFromExt) || Objects.equals(ext, extFromMimeType)) { + // Extension maps back to requested MIME type; allow it + } else { + // No match; insist that create file matches requested MIME + name = displayName; + ext = extFromMimeType; + } + } + + File file = buildFile(parent, name, ext); + + // If conflicting file, try adding counter suffix + int n = 0; + while (file.exists()) { + if (n++ >= 32) { + throw new FileNotFoundException("Failed to create unique file"); + } + file = buildFile(parent, name + " (" + n + ")", ext); + } + + return file; + } + + private static File buildFile(File parent, String name, String ext) { + if (TextUtils.isEmpty(ext)) { + return new File(parent, name); + } else { + return new File(parent, name + "." + ext); + } + } } diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java index 5c9e813..ee9e2e4 100644 --- a/core/tests/coretests/src/android/os/FileUtilsTest.java +++ b/core/tests/coretests/src/android/os/FileUtilsTest.java @@ -21,6 +21,7 @@ import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.WEEK_IN_MILLIS; import android.content.Context; +import android.provider.DocumentsContract.Document; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.MediumTest; @@ -43,6 +44,8 @@ public class FileUtilsTest extends AndroidTestCase { private File mDir; private File mTestFile; private File mCopyFile; + private File mTarget; + @Override protected void setUp() throws Exception { @@ -50,11 +53,15 @@ public class FileUtilsTest extends AndroidTestCase { mDir = getContext().getDir("testing", Context.MODE_PRIVATE); mTestFile = new File(mDir, "test.file"); mCopyFile = new File(mDir, "copy.file"); + + mTarget = getContext().getFilesDir(); + FileUtils.deleteContents(mTarget); } @Override protected void tearDown() throws Exception { IoUtils.deleteContents(mDir); + FileUtils.deleteContents(mTarget); } // TODO: test setPermissions(), getPermissions() @@ -225,6 +232,63 @@ public class FileUtilsTest extends AndroidTestCase { assertEquals("foo_bar__baz", FileUtils.buildValidFatFilename("foo?bar**baz")); } + public void testBuildUniqueFile_normal() throws Exception { + assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test")); + assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg")); + assertNameEquals("test.jpeg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpeg")); + assertNameEquals("TEst.JPeg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "TEst.JPeg")); + assertNameEquals("test.png.jpg", + FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.png.jpg")); + assertNameEquals("test.png.jpg", + FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.png")); + + assertNameEquals("test.flac", FileUtils.buildUniqueFile(mTarget, "audio/flac", "test")); + assertNameEquals("test.flac", FileUtils.buildUniqueFile(mTarget, "audio/flac", "test.flac")); + assertNameEquals("test.flac", + FileUtils.buildUniqueFile(mTarget, "application/x-flac", "test")); + assertNameEquals("test.flac", + FileUtils.buildUniqueFile(mTarget, "application/x-flac", "test.flac")); + } + + public void testBuildUniqueFile_unknown() throws Exception { + assertNameEquals("test", + FileUtils.buildUniqueFile(mTarget, "application/octet-stream", "test")); + assertNameEquals("test.jpg", + FileUtils.buildUniqueFile(mTarget, "application/octet-stream", "test.jpg")); + assertNameEquals(".test", + FileUtils.buildUniqueFile(mTarget, "application/octet-stream", ".test")); + + assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, "lolz/lolz", "test")); + assertNameEquals("test.lolz", FileUtils.buildUniqueFile(mTarget, "lolz/lolz", "test.lolz")); + } + + public void testBuildUniqueFile_dir() throws Exception { + assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test")); + new File(mTarget, "test").mkdir(); + assertNameEquals("test (1)", + FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test")); + + assertNameEquals("test.jpg", + FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg")); + new File(mTarget, "test.jpg").mkdir(); + assertNameEquals("test.jpg (1)", + FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg")); + } + + public void testBuildUniqueFile_increment() throws Exception { + assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg")); + new File(mTarget, "test.jpg").createNewFile(); + assertNameEquals("test (1).jpg", + FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg")); + new File(mTarget, "test (1).jpg").createNewFile(); + assertNameEquals("test (2).jpg", + FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg")); + } + + private static void assertNameEquals(String expected, File actual) { + assertEquals(expected, actual.getName()); + } + private void touch(String name, long age) throws Exception { final File file = new File(mDir, name); file.createNewFile(); |
