Can't get last modified date from Uri

Viewed 52

I have some files saved in the private app-specific folder of the app.

If I get a file uri through the FileProvider

val fileUri = getUriForFile(this, "$packageName.fileprovider", file)

and query the content resolver to get the file metadata

contentResolver.query(fileUri, null, null, null, null)

and check all the columnNames inside the cursor I'm getting, I can see that there are only two columns, which are _display_name and _size

What if I wanted to obtain the date of creation/last modification of the file from the uri? Is there something I could do?

(I know I could get it from the File object, but I'd need it from the uri, the real code is more complex than this example.)

Thanks!

2 Answers

What if I wanted to obtain the date of creation/last modification of the file from the uri? Is there something I could do?

That depends a bit on your overall setup.

There's no way to get that information for an arbitrary Uri. There's no requirement for, say, Google Drive to provide that information, which is why it's not part of the OpenableColumns protocol that you're using (or anything else). So, if the reason you're pushing away from using the File is because you have a mixed bag of Uri values from various sources, then you're probably in trouble.

If, OTOH, you know that your Uri values are always from your FileProvider, you can reverse-engineer the filesystem path from the Uri value, use that to construct a File, and get the info that way (or, at least, whatever Android makes available via those File methods). Or, if you can live with the creation/modification time information only being available for a subset of your Uri list, use the aforementioned hack for your own FileProvider Uri values and live without the data for Uri values that you get from elsewhere.

So I also wanted to have the same. I use an Intent to let the user pick an image. When a user selects an image from Camera I can get the last modified via:

fun ContentResolver.lastModified(uri: Uri): Instant? {
  val query = query(uri, null, null, null, null)

  query?.use { cursor ->
    if (cursor.moveToFirst()) {
      val columnNames = listOf(
        "last_modified", // When selecting an image from the Camera via the Intent.ACTION_GET_CONTENT action.
        "datetaken", // When sharing an Image from Google Photos into the app.
      )

      val instant = columnNames.firstNotNullOfOrNull {
        val index = cursor.getColumnIndex(it)
        if (!cursor.isNull(index)) {
          Instant.fromEpochMilliseconds(cursor.getLong(index))
        } else {
          null
        }
      }

      if (instant == null) {
        val string = cursor.columnNames.map { columnName ->
          columnName to cursor.getColumnIndex(columnName).takeIf { it >= 0 }?.let(cursor::getString)
        }
        Timber.tag("Uri").i(UnsupportedOperationException("Can't get last modified from $string"))
      }

      return instant
    }
  }

  return null
}

Note: This might not work for every app, however it's a good first start. Also, when it does not work, it will log an exception via Timber and if you have that wired up to a logging file or Crashlytics, you can progressively add more supported column names.

Related