0

I have a Android app that uses the Android camera app. Because I need the specific file name, I created my own CameraActivity.

In this activity I create my temp file like so:

public File createImageFile() throws IOException {
    File pathOfStorageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
    pathOfStorageDir.mkdir();

    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    String filePrefix = "img_" + timeStamp + "_";
    String suffix = ".jpg";

    File image = File.createTempFile(filePrefix, suffix, pathOfStorageDir);
    currentFileName = image.getName();
    currentFilePath = image.getAbsolutePath();
    return image;
}

The weird thing is that I get sometimes negative values from in the new file name.

As I saw createTempFile() calls generateTempFile() and that method should create a random absolute int. Why is this random int sometimes negative? Or how can I avoid that?

The problem: I need the file later in my app but can't import it with a "-" sign. That throws me this exception: Error:com.android.build.gradle.tasks.ResourceException: <package-name>/app/src/main/res/drawable/img_sfr_20170715_-1‌​13604.jpg: Error: '-' is not a valid file-based resource name character: File-based resource names must contain only lowercase a-z, 0-9, or underscore

Thanks in advance for the help!

  • 1
    Why does it matter? Is there a specific problem it causes, or is it merely aesthetically unpleasing? – Andy Turner Sep 06 '17 at 14:49
  • @Andy Turner: The "-" sign is not accepted as character for a file name in Android. So I always have to rename the names. – Hansi Hansenbaum Sep 06 '17 at 14:51
  • @HansiHansenbaum Sure it is. Android is Linux. The only unacceptable characters are / and null terminators – Gabe Sechan Sep 06 '17 at 14:51
  • 1
    @HansiHansenbaum https://stackoverflow.com/questions/2679699/what-characters-allowed-in-file-names-on-android – Andy Turner Sep 06 '17 at 14:52
  • Thanks for the help, guys. But I don't get it, why do I get this error then? `Error:com.android.build.gradle.tasks.ResourceException: /app/src/main/res/drawable/img_sfr_20170715_-113604.jpg: Error: '-' is not a valid file-based resource name character: File-based resource names must contain only lowercase a-z, 0-9, or underscore` – Hansi Hansenbaum Sep 06 '17 at 14:56

2 Answers2

0

Ok, this is interesting: It seems the File.createTempFile() method in android.jar is implemented differently than the one in rt.jar.

In rt.jar (with Java 8), we have:

public static File createTempFile(String prefix, String suffix,
                                  File directory)
    throws IOException
{
    if (prefix.length() < 3)
        throw new IllegalArgumentException("Prefix string too short");
    if (suffix == null)
        suffix = ".tmp";

    File tmpdir = (directory != null) ? directory
                                      : TempDirectory.location();
    SecurityManager sm = System.getSecurityManager();
    File f;
    do {
        f = TempDirectory.generateFile(prefix, suffix, tmpdir);

        if (sm != null) {
            try {
                sm.checkWrite(f.getPath());
            } catch (SecurityException se) {
                // don't reveal temporary directory location
                if (directory == null)
                    throw new SecurityException("Unable to create temporary file");
                throw se;
            }
        }
    } while ((fs.getBooleanAttributes(f) & FileSystem.BA_EXISTS) != 0);

    if (!fs.createFileExclusively(f.getPath()))
        throw new IOException("Unable to create temporary file");

    return f;
}

which uses TempDirectory.generateFile(String, String, File):

    private static final SecureRandom random = new SecureRandom();
    static File generateFile(String prefix, String suffix, File dir)
        throws IOException
    {
        long n = random.nextLong();
        if (n == Long.MIN_VALUE) {
            n = 0;      // corner case
        } else {
            n = Math.abs(n);
        }

        // Use only the file name from the supplied prefix
        prefix = (new File(prefix)).getName();

        String name = prefix + Long.toString(n) + suffix;
        File f = new File(dir, name);
        if (!name.equals(f.getName()) || f.isInvalid()) {
            if (System.getSecurityManager() != null)
                throw new IOException("Unable to create temporary file");
            else
                throw new IOException("Unable to create temporary file, " + f);
        }
        return f;
    }

This will lead to file names with positive random numbers in them.

In android.jar (Android API 20), we have:

public static File createTempFile(String prefix, String suffix, File directory)
        throws IOException {
    // Force a prefix null check first
    if (prefix.length() < 3) {
        throw new IllegalArgumentException("prefix must be at least 3 characters");
    }
    if (suffix == null) {
        suffix = ".tmp";
    }
    File tmpDirFile = directory;
    if (tmpDirFile == null) {
        String tmpDir = System.getProperty("java.io.tmpdir", ".");
        tmpDirFile = new File(tmpDir);
    }
    File result;
    do {
        result = new File(tmpDirFile, prefix + tempFileRandom.nextInt() + suffix);
    } while (!result.createNewFile());
    return result;
}

Since tempFileRandom is a standard Random instance, its nextInt() method will return positive as well as negative integers.

Thus, using File.createTempFile() can return file names with minus signs / hyphens on Android.

The best option, therefore, would be to implement your own file name generator and createTempFile() method, based on the one provided in the runtime library, but with positive random numbers only.

Markus Fischer
  • 1,326
  • 8
  • 13
  • So it's a bug in Android. Its own code generates filenames which it cannot use. – user207421 Sep 07 '17 at 01:02
  • As stated in other comments, Android in general - being Linux based - can handle file names with minus signs in them. It seems to be a problem with a file name limitation in the build process, judging from Hansenbaum's error message: Error:com.android.build.gradle.tasks.ResourceException. Admittedly, creating a temporary file and then adding it as a resource is a bit unusual. – Markus Fischer Sep 07 '17 at 06:31
  • Thank you for the help! I think I let this one slide. My goal was to implement a temporary solution to save and add image files to the prototype. But on a long term basis, I will have to find a network based solution anyway. – Hansi Hansenbaum Sep 07 '17 at 08:07
0

For an easy workaround, you could just replace "-" with something like "x" in the filename string.

Strictly speaking, this could "void the warranty" about createFileExclusively(), but the possibility of a race/collision is almost non-existent here.

jurez
  • 4,436
  • 2
  • 12
  • 20