9

Looking at the storage access changes introduced in Android 10 here, location informations are now redacted by default.

Google asks us to call setRequireOriginal() on the "MediaStore" object with the media's uri as a parameter. This works when you fetch medias one by one, but what about when we query the ContentResolver for the whole gallery?

See this sample:

String[] projection = {
        MediaStore.Files.FileColumns._ID,
        MediaStore.Files.FileColumns.DATA,
        MediaStore.Files.FileColumns.MEDIA_TYPE,
        MediaStore.Images.Media.DATE_TAKEN,
        MediaStore.Images.Media.WIDTH,
        MediaStore.Images.Media.HEIGHT,
        MediaStore.Images.Media.LATITUDE, // <----- THIS
        MediaStore.Images.Media.LONGITUDE, // <----- THIS
        MediaStore.Images.Media.MIME_TYPE,
};

String selection = MediaStore.Files.FileColumns.MEDIA_TYPE + "="
        + MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE;

Uri queryUri = MediaStore.Files.getContentUri("external");

Cursor cursor = null;
MediaStore a ;

try {
    cursor = context.getContentResolver().query(queryUri, projection, selection,
            null, MediaStore.Images.Media.DATE_TAKEN + " DESC");


}
catch (NullPointerException ex){
}

Starting Q the latitude and longitude are always set to 0. Is there a way to get the location data for a batch of medias assuming the ACCESS_MEDIA_LOCATION permission is added in the Manifest?

4

1 回答 1

9

不幸的是, Android Q 中已弃用MediaStore.Images.Media.LATITUDE 和 MediaStore.Images.Media.LONGITUDE 。

解决方法是这样使用ExifInterface

Cursor cursor = null;
try {
    String[] projection = {
            MediaStore.Files.FileColumns._ID,
            MediaStore.Images.Media.DATE_TAKEN,
            MediaStore.Images.Media.WIDTH,
            MediaStore.Images.Media.HEIGHT,
            MediaStore.MediaColumns.TITLE,
            MediaStore.Images.Media.MIME_TYPE,
            MediaStore.Images.Media.LATITUDE,
            MediaStore.Images.Media.LONGITUDE
    };
    cursor = getContentResolver().query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            projection,
            null,
            null,
            MediaStore.Images.Media.DATE_TAKEN + " DESC");


    int idColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID);
    int titleColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.TITLE);
    int latColumn = BuildCompat.isAtLeastQ() ? -1
            : cursor.getColumnIndexOrThrow(MediaStore.Images.Media.LATITUDE);
    int longColumn = BuildCompat.isAtLeastQ() ? -1
            : cursor.getColumnIndexOrThrow(MediaStore.Images.Media.LONGITUDE);
    while (cursor.moveToNext()) {

        Uri photoUri = Uri.withAppendedPath(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                cursor.getString(idColumn));
        String title = cursor.getString(titleColumn);

        final double[] latLong;
        if (BuildCompat.isAtLeastQ()) {
            photoUri = MediaStore.setRequireOriginal(photoUri);
            InputStream stream = getContentResolver().openInputStream(photoUri);
            if (stream == null) {
                Log.w(TAG, "Got a null input stream for " + photoUri);
                continue;
            }

            ExifInterface exifInterface = new ExifInterface(stream);
            double[] returnedLatLong = exifInterface.getLatLong();
            // If it returns null, fall back to {0.0, 0.0}.
            latLong = returnedLatLong != null ? returnedLatLong : new double[2];

            // After using ExifInterface, the stream should not be reused.
            stream.close();
        } else {
            latLong = new double[]{
                    cursor.getFloat(latColumn),
                    cursor.getFloat(longColumn)
            };
        }

        Log.i(TAG, title + " | lat: " + latLong[0] + " | lng: " + latLong[1]);
    }
} catch (NullPointerException | IOException ex) {
    Log.e(TAG, "Caught exception", ex);
} finally {
    if (cursor != null) {
        cursor.close();
    }
}

这是目前在 Android Q 上获取照片纬度和经度的唯一方法。

这需要持有ACCESS_MEDIA_LOCATION许可。请注意,此权限不是“用户在设置 UI 中可见”(source),这意味着用户不会看到请求权限的弹出窗口,即使它是运行时权限。这意味着您必须在运行时请求许可(与清单文件相反),但用户不必同意。在此处添加它是因为您可能想知道为什么没有显示额外的 UI 弹出窗口。

于 2019-03-19T18:46:19.453 回答