Android 10:通过带有位置信息的 MediaStore 获取图库
Android 10: fetch the gallery via MediaStore with location information
查看 Android 10 here 中引入的存储访问更改,现在默认情况下会编辑位置信息。
Google 要求我们使用媒体的 uri 作为参数在“MediaStore”对象上调用 setRequireOriginal()
。这在您一个接一个地获取媒体时有效,但是当我们为整个画廊查询 ContentResolver 时呢?
查看此示例:
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){
}
启动Q,纬度和经度始终设置为0。假设在Manifest中添加了ACCESS_MEDIA_LOCATION
权限,是否有办法获取一批媒体的位置数据?
不幸的是,MediaStore.Images.Media.LATITUDE 和 MediaStore.Images.Media.LONGITUDE 是 deprecated in Android Q。
The work around 就是这样使用 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
permission. Note, this permission is not "user visible in the settings UI" (source),这意味着用户不会看到请求权限的弹出窗口,即使它是运行时权限。这意味着您必须在运行时请求许可(与清单文件不同),但用户不必同意。在这里添加它是因为您可能想知道为什么没有显示额外的 UI 弹出窗口。
查看 Android 10 here 中引入的存储访问更改,现在默认情况下会编辑位置信息。
Google 要求我们使用媒体的 uri 作为参数在“MediaStore”对象上调用 setRequireOriginal()
。这在您一个接一个地获取媒体时有效,但是当我们为整个画廊查询 ContentResolver 时呢?
查看此示例:
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){
}
启动Q,纬度和经度始终设置为0。假设在Manifest中添加了ACCESS_MEDIA_LOCATION
权限,是否有办法获取一批媒体的位置数据?
不幸的是,MediaStore.Images.Media.LATITUDE 和 MediaStore.Images.Media.LONGITUDE 是 deprecated in Android Q。
The work around 就是这样使用 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
permission. Note, this permission is not "user visible in the settings UI" (source),这意味着用户不会看到请求权限的弹出窗口,即使它是运行时权限。这意味着您必须在运行时请求许可(与清单文件不同),但用户不必同意。在这里添加它是因为您可能想知道为什么没有显示额外的 UI 弹出窗口。