Commit 9ff223e2 authored by Simon Redman's avatar Simon Redman Committed by Nicolas Fella

Read MediaStore.Images.ImageColumns.DATE_TAKEN for yet another way of...

Read MediaStore.Images.ImageColumns.DATE_TAKEN for yet another way of accessing the last modified information
parent b9a0f310
......@@ -20,11 +20,14 @@
package org.kde.kdeconnect.Helpers;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.util.Log;
import android.webkit.MimeTypeMap;
......@@ -32,9 +35,12 @@ import org.kde.kdeconnect.NetworkPacket;
import java.util.Arrays;
public class FilesHelper {
public static final String LOG_TAG = "SendFileActivity";
private static String getFileExt(String filename) {
//return MimeTypeMap.getFileExtensionFromUrl(filename);
return filename.substring((filename.lastIndexOf(".") + 1));
......@@ -119,24 +125,30 @@ public class FilesHelper {
InputStream inputStream = cr.openInputStream(uri);
NetworkPacket np = new NetworkPacket(type);
String filename = null;
long size = -1;
Long lastModified = null;
if (uri.getScheme().equals("file")) {
// file:// is a non media uri, so we cannot query the ContentProvider
np.set("filename", uri.getLastPathSegment());
try {
size = new File(uri.getPath()).length();
} catch (Exception e) {
Log.e("SendFileActivity", "Could not obtain file size", e);
File mFile = new File(uri.getPath());
filename = mFile.getName();
size = mFile.length();
lastModified = mFile.lastModified();
} catch (NullPointerException e) {
Log.e(LOG_TAG, "Received bad file URI", e);
} else {
// Probably a content:// uri, so we query the Media content provider
// Since we used Intent.CATEGORY_OPENABLE, these two columns are the only ones we are
// guaranteed to have:
String[] proj = {
try (Cursor cursor = cr.query(uri, proj, null, null, null)) {
......@@ -144,7 +156,7 @@ public class FilesHelper {
int sizeColumnIndex = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.SIZE);
String filename = cursor.getString(nameColumnIndex);
filename = cursor.getString(nameColumnIndex);
// It is recommended to check for the value to be null because there are
// situations were we don't know the size (for instance, if the file is
......@@ -153,18 +165,112 @@ public class FilesHelper {
size = cursor.getInt(sizeColumnIndex);
np.set("filename", filename);
lastModified = getLastModifiedTime(context, uri);
} catch (Exception e) {
Log.e("SendFileActivity", "Problem getting file information", e);
Log.e(LOG_TAG, "Problem getting file information", e);
if (filename != null) {
np.set("filename", filename);
} else {
// It would be very surprising if this happens
Log.e(LOG_TAG, "Unable to read filename");
if (lastModified != null) {
np.set("lastModified", lastModified);
} else {
// This would not be too surprising, and probably means we need to improve
// FilesHelper.getLastModifiedTime
Log.w(LOG_TAG, "Unable to read file last modified time");
np.setPayload(new NetworkPacket.Payload(inputStream, size));
return np;
} catch (Exception e) {
Log.e("SendFileActivity", "Exception creating network packet", e);
Log.e(LOG_TAG, "Exception creating network packet", e);
return null;
* By hook or by crook, get the last modified time of the passed content:// URI
* This is a challenge because different content sources have different columns defined, and
* I don't know how to tell what the source of the content is.
* Therefore, my brilliant solution is to just try everything until something works.
* Will return null if nothing worked.
public static Long getLastModifiedTime(final Context context, final Uri uri) {
ContentResolver cr = context.getContentResolver();
Long lastModifiedTime = null;
// Open a cursor without a column because we do not yet know what columns are defined
try (Cursor cursor = cr.query(uri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
String[] allColumns = cursor.getColumnNames();
// MediaStore.MediaColumns.DATE_MODIFIED resolves to "date_modified"
// I see this column defined in case we used the Gallery app to select the file to transfer
// This can occur both for devices running Storage Access Framework (SAF) if we select
// the Gallery to provide the file to transfer, as well as for older devices by doing the same
int mediaDataModifiedColumnIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DATE_MODIFIED);
// DocumentsContract.Document.COLUMN_LAST_MODIFIED resolves to "last_modified"
// I see this column defined when, on a device using SAF we select a file using the
// file browser
// According to
// all "document providers" must provide certain columns. Do we actually have a DocumentProvider here?
// I do not think this code path will ever happen for a non-media file is selected on
// an API < KitKat device, since those will be delivered as a file:// URI and handled
// accordingly. Therefore, it is safe to ignore the warning that this field requires
// API 19
int documentLastModifiedColumnIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_LAST_MODIFIED);
// If we have an image, it may be the case that MediaStore.MediaColumns.DATE_MODIFIED
// catches the modification date, but if not, here is another column we can look for.
// This should be checked *after* DATE_MODIFIED since I think that column might give
// better information
int imageDateTakenColumnIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATE_TAKEN);
// Report whether the captured timestamp is in milliseconds or seconds
// The truthy-ness of this value for each different type of column is known from either
// experimentation or the docs (when docs exist...)
boolean milliseconds;
int properColumnIndex;
if (mediaDataModifiedColumnIndex >= 0) {
properColumnIndex = mediaDataModifiedColumnIndex;
milliseconds = false;
} else if (documentLastModifiedColumnIndex >= 0) {
properColumnIndex = documentLastModifiedColumnIndex;
milliseconds = true;
} else if (imageDateTakenColumnIndex >= 0) {
properColumnIndex = imageDateTakenColumnIndex;
milliseconds = true;
} else {
// Nothing worked :(
String formattedColumns = Arrays.toString(allColumns);
Log.w("SendFileActivity", "Unable to get file modification time. Available columns were: " + formattedColumns);
return null;
if (!cursor.isNull(properColumnIndex)) {
lastModifiedTime = cursor.getLong(properColumnIndex);
if (!milliseconds) {
lastModifiedTime *= 1000;
milliseconds = true;
return lastModifiedTime;
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment