148 lines
4.9 KiB
Java
148 lines
4.9 KiB
Java
/*
|
||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*/
|
||
package com.arialyy.aria.util;
|
||
|
||
import android.content.Context;
|
||
import android.content.Intent;
|
||
import android.database.Cursor;
|
||
import android.net.Uri;
|
||
import android.os.ParcelFileDescriptor;
|
||
import android.provider.OpenableColumns;
|
||
import android.text.TextUtils;
|
||
|
||
/**
|
||
* 分区存储(Scoped Storage)相关 Uri 工具类,提供文件名解析、可写性探测和权限持久化能力。
|
||
*/
|
||
public class UriFileUtil {
|
||
private static final String TAG = "UriFileUtil";
|
||
|
||
/**
|
||
* 从 Uri 中获取文件名
|
||
*
|
||
* @param context 上下文,可为 null(仅用 lastPathSegment 兜底)
|
||
* @param uri 文档 Uri
|
||
* @return 文件名,无法获取时返回空串
|
||
*/
|
||
public static String getFileName(Context context, Uri uri) {
|
||
if (uri == null) {
|
||
return "";
|
||
}
|
||
String name = queryDisplayName(context, uri);
|
||
if (!TextUtils.isEmpty(name)) {
|
||
return name;
|
||
}
|
||
String last = uri.getLastPathSegment();
|
||
if (!TextUtils.isEmpty(last)) {
|
||
int idx = last.lastIndexOf('/');
|
||
return idx >= 0 ? last.substring(idx + 1) : last;
|
||
}
|
||
return "";
|
||
}
|
||
|
||
private static String queryDisplayName(Context context, Uri uri) {
|
||
if (!"content".equalsIgnoreCase(uri.getScheme()) || context == null) {
|
||
return null;
|
||
}
|
||
Cursor cursor = null;
|
||
try {
|
||
cursor = context.getContentResolver()
|
||
.query(uri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null);
|
||
if (cursor != null && cursor.moveToFirst()) {
|
||
int idx = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
||
if (idx >= 0) {
|
||
return cursor.getString(idx);
|
||
}
|
||
}
|
||
} catch (Exception e) {
|
||
ALog.w(TAG, "query display name error: " + e.getMessage());
|
||
} finally {
|
||
if (cursor != null) {
|
||
cursor.close();
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 测试 Uri 是否可写(以读写方式打开)
|
||
*/
|
||
public static boolean canWrite(Context context, Uri uri) {
|
||
if (context == null || uri == null) {
|
||
return false;
|
||
}
|
||
ParcelFileDescriptor pfd = null;
|
||
try {
|
||
pfd = context.getContentResolver().openFileDescriptor(uri, "rw");
|
||
return pfd != null;
|
||
} catch (Exception e) {
|
||
ALog.w(TAG, "uri canWrite test failed: " + e.getMessage());
|
||
return false;
|
||
} finally {
|
||
if (pfd != null) {
|
||
try {
|
||
pfd.close();
|
||
} catch (Exception ignored) {
|
||
// ignore
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取 Uri 对应文件的长度
|
||
*
|
||
* @return 文件长度;无法获取(Uri 不存在、无权限、流式内容等)时返回 -1
|
||
*/
|
||
public static long getFileLength(Context context, Uri uri) {
|
||
if (context == null || uri == null) {
|
||
return -1;
|
||
}
|
||
ParcelFileDescriptor pfd = null;
|
||
try {
|
||
pfd = context.getContentResolver().openFileDescriptor(uri, "r");
|
||
return pfd == null ? -1 : pfd.getStatSize();
|
||
} catch (Exception e) {
|
||
ALog.w(TAG, "get uri file length failed: " + e.getMessage());
|
||
return -1;
|
||
} finally {
|
||
if (pfd != null) {
|
||
try {
|
||
pfd.close();
|
||
} catch (Exception ignored) {
|
||
// ignore
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 尝试持久化 Uri 权限,以便应用重启后仍能访问(SAF 文档树/文件)。
|
||
* 若 Uri 不支持持久化权限则静默忽略。
|
||
*/
|
||
public static void tryTakePersistablePermission(Context context, Uri uri) {
|
||
if (context == null || uri == null) {
|
||
return;
|
||
}
|
||
try {
|
||
context.getContentResolver()
|
||
.takePersistableUriPermission(uri,
|
||
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||
} catch (SecurityException | IllegalArgumentException ignored) {
|
||
// 部分 Uri(如 MediaStore 插入项)不支持持久化权限,忽略即可
|
||
}
|
||
}
|
||
}
|